Last active
August 29, 2015 14:06
-
-
Save markpapadakis/75e4e16b9291211d5acd to your computer and use it in GitHub Desktop.
Scans an ELF DLL/DSO looking for globally exported syms/objects. Useful for App Servers, game engines, etc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <elf.h> | |
| // Scans an ELF DLL/DSO looking for globally epxorted symbols/objects of type object or function | |
| // Very useful for e.g application servers or games where you don't really want to pre-define those functions/objects in e.g a list | |
| // and instead you can define them in any file that makes up the module. That way you will scan them and identfiy what you need and load it later | |
| // | |
| // e.g | |
| // EnumDynSyms("AppServerModules/search.dso", [](const char *const name, const uint8_t type) | |
| // { | |
| // if (type == STT_FUNC && BeginsWith(name, "APIMEthod_")) | |
| // { | |
| // // use dlopen()/dlsym() later to access that function | |
| // } | |
| // | |
| // }); | |
| static int EnumDynSyms(const char *const path, std::function<void(const char *, const uint8_t type)> l) | |
| { | |
| int fd = open(path, O_RDONLY|O_LARGEFILE); | |
| if (unlikely(fd == -1)) | |
| return -1; | |
| const auto fileSize = lseek64(fd, 0, SEEK_END); | |
| if (unlikely(fileSize < 16)) | |
| { | |
| (void)close(fd); | |
| return -1; | |
| } | |
| void *const fileData = mmap(NULL, fileSize, PROT_READ, MAP_SHARED, fd, 0); | |
| (void)close(fd); | |
| if (unlikely(fileData == MAP_FAILED)) | |
| return -1; | |
| (void)madvise(fileData, fileSize, MADV_SEQUENTIAL); | |
| const uint8_t *p = (uint8_t *)fileData; | |
| if (unlikely(*p != 0x7f || memcmp(p + 1, _S("ELF")))) | |
| return -1; | |
| p+=4; | |
| const bool format64 = (*(p++) == 2), format32 = !format64; | |
| const bool littleEndian = (*(p++) == 1), bigEndian = !littleEndian; | |
| const uint8_t ident = *p++; | |
| const uint8_t ABI = *p++; | |
| const uint8_t ABIversion= *p++; | |
| (void)littleEndian; | |
| (void)bigEndian; | |
| (void)ident; | |
| (void)ABI; | |
| (void)ABIversion; | |
| p+=7; // padding | |
| const uint16_t type = *(uint16_t *)p; p+=2; // We care for ET_DYN(3) | |
| const uint16_t targetISA= *(uint16_t *)p; p+=2; // We care for EM_386(3) | |
| const uint32_t version = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| uint64_t entryOffset, programHeaderOffset, sectionHeaderOffset; | |
| (void)type; | |
| (void)targetISA; | |
| (void)version; | |
| if (format32) | |
| { | |
| entryOffset = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| programHeaderOffset = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| sectionHeaderOffset = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| } | |
| else | |
| { | |
| entryOffset = *(uint64_t *)p; p+=sizeof(uint64_t); | |
| programHeaderOffset = *(uint64_t *)p; p+=sizeof(uint64_t); | |
| sectionHeaderOffset = *(uint64_t *)p; p+=sizeof(uint64_t); | |
| } | |
| const uint32_t flags = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| const uint16_t headerSize = *(uint16_t *)p; p+=sizeof(uint16_t); // ELF header size | |
| const uint16_t programHeaderEntrySize = *(uint16_t *)p; p+=sizeof(uint16_t); // size in bytes of one entry in the file's program header(all same size) | |
| const uint16_t programHeaderEntries = *(uint16_t *)p; p+=sizeof(uint16_t); // so program header size = programHeaderEntrySize * programHeaderEntries | |
| const uint16_t sectionHeaderEntrySize = *(uint16_t *)p; p+=sizeof(uint16_t); | |
| const uint16_t sectionHeaderEntries = *(uint16_t *)p; p+=sizeof(uint16_t); | |
| const uint16_t sectionHeaderNameEntriesIndex = *(uint16_t *)p; p+=sizeof(uint16_t); | |
| (void)flags; | |
| (void)headerSize; | |
| (void)programHeaderEntrySize; | |
| (void)programHeaderEntries; | |
| (void)sectionHeaderNameEntriesIndex; | |
| p = (uint8_t *)fileData + sectionHeaderOffset; | |
| uint64_t stringTableOffset = 0, dynamicSectionOffset = 0, dynamicSectionSize, symbolTableSize = 0; | |
| for (uint32_t i = 0; i != sectionHeaderEntries; ++i) | |
| { | |
| const auto *const sectionBase = p; | |
| const uint32_t name = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| const uint32_t type = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| uint64_t flags, addr, offset, size; | |
| (void)name; | |
| if (format32) | |
| { | |
| flags = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| addr = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| offset = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| size = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| } | |
| else | |
| { | |
| flags = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| addr = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| offset = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| size = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| } | |
| const uint32_t link = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| const uint32_t info = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| uint64_t addrAlign, entSize; | |
| if (format32) | |
| { | |
| addrAlign = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| entSize = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| } | |
| else | |
| { | |
| addrAlign = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| entSize = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| } | |
| assert_impl(p - sectionBase == sectionHeaderEntrySize); | |
| if (type == SHT_STRTAB && !stringTableOffset) | |
| { | |
| // Right after .dynsym we are expecting .dynstr | |
| // .dynstr is not the only SHT_STRTAB kind of section, and this heuristic helps us get the right one | |
| stringTableOffset = offset; | |
| } | |
| else if (type == SHT_DYNAMIC) | |
| { | |
| dynamicSectionOffset= offset; | |
| dynamicSectionSize = size; | |
| } | |
| else if (type == SHT_DYNSYM) | |
| symbolTableSize = size; // that is, we have size / entSize entries in that section | |
| (void)link; | |
| (void)info; | |
| (void)addrAlign; | |
| (void)entSize; | |
| } | |
| if (unlikely(symbolTableSize == 0 || dynamicSectionOffset == 0 || stringTableOffset == 0)) | |
| { | |
| (void)munmap(fileData, fileSize); | |
| return 0; | |
| } | |
| const char *const stringTableChunk = (char *)fileData + stringTableOffset; | |
| uint64_t symbolTableOffset = 0; | |
| // First byte in String Table(index 0), defined to hold a null character | |
| assert_impl(*stringTableChunk == 0); | |
| p = (uint8_t *)fileData + dynamicSectionOffset; | |
| for (const uint8_t *const sectionEnd = p + dynamicSectionSize; p != sectionEnd; ) | |
| { | |
| int64_t tag; | |
| uint64_t addrOrVal; | |
| if (format32) | |
| { | |
| tag = *(int32_t *)p; | |
| p+=sizeof(int32_t); | |
| addrOrVal = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| } | |
| else | |
| { | |
| tag = *(int64_t *)p; | |
| p+=sizeof(int64_t); | |
| addrOrVal = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| } | |
| if (tag == DT_SYMTAB) | |
| { | |
| // This will match offset we found when iterating section header array | |
| symbolTableOffset = addrOrVal; | |
| } | |
| } | |
| if (unlikely(symbolTableOffset == 0)) | |
| { | |
| (void)munmap(fileData, fileSize); | |
| return 0; | |
| } | |
| p = (uint8_t *)fileData + symbolTableOffset; | |
| for (const uint8_t *const sectionEnd = p + symbolTableSize; p != sectionEnd; ) | |
| { | |
| uint32_t name = *(uint32_t *)p; p+=sizeof(uint32_t); | |
| uint8_t info, other; | |
| uint16_t section; | |
| uint64_t addr, size; | |
| if (format32) | |
| { | |
| addr = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| size = *(uint32_t *)p; | |
| p+=sizeof(uint32_t); | |
| info = *p++; | |
| other = *p++; | |
| section = *(uint16_t *)p; | |
| p+=sizeof(uint16_t); | |
| } | |
| else | |
| { | |
| info = *p++; | |
| other = *p++; | |
| section = *(uint16_t *)p; | |
| p+=sizeof(uint16_t); | |
| addr = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| size = *(uint64_t *)p; | |
| p+=sizeof(uint64_t); | |
| } | |
| const uint8_t bind = info>>4, type = (info&0xf); | |
| if (bind == STB_GLOBAL) | |
| { | |
| if (type == 1 || type == 2) // STT_OBECT || STT_FUNC | |
| l(stringTableChunk + name, type); | |
| } | |
| } | |
| (void)munmap(fileData, fileSize); | |
| return 0; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment