Skip to content

Instantly share code, notes, and snippets.

@markpapadakis
Last active August 29, 2015 14:06
Show Gist options
  • Select an option

  • Save markpapadakis/75e4e16b9291211d5acd to your computer and use it in GitHub Desktop.

Select an option

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
#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