feat: revamp libraries to use ELF shared objects
This commit is contained in:
@@ -11,15 +11,189 @@
|
||||
|
||||
namespace libloader {
|
||||
|
||||
// Forward declaration
|
||||
static uint64_t hexToUint64(const char* str);
|
||||
|
||||
// Table of currently loaded libraries
|
||||
static LibHandle g_loadedLibs[MaxLoadedLibs];
|
||||
static int g_libCount = 0;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Elf64Header {
|
||||
uint8_t e_ident[16];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint64_t e_entry;
|
||||
uint64_t e_phoff;
|
||||
uint64_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct Elf64SectionHeader {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint64_t sh_flags;
|
||||
uint64_t sh_addr;
|
||||
uint64_t sh_offset;
|
||||
uint64_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint64_t sh_addralign;
|
||||
uint64_t sh_entsize;
|
||||
};
|
||||
|
||||
struct Elf64Sym {
|
||||
uint32_t st_name;
|
||||
uint8_t st_info;
|
||||
uint8_t st_other;
|
||||
uint16_t st_shndx;
|
||||
uint64_t st_value;
|
||||
uint64_t st_size;
|
||||
};
|
||||
|
||||
static constexpr uint16_t ET_EXEC = 2;
|
||||
static constexpr uint16_t ET_DYN = 3;
|
||||
static constexpr uint16_t EM_X86_64 = 62;
|
||||
static constexpr uint32_t SHT_SYMTAB = 2;
|
||||
static constexpr uint32_t SHT_STRTAB = 3;
|
||||
static constexpr uint32_t SHT_DYNSYM = 11;
|
||||
static constexpr uint16_t SHN_UNDEF = 0;
|
||||
|
||||
static bool readFile(const char* path, uint8_t** outBuf, uint64_t* outSize) {
|
||||
*outBuf = nullptr;
|
||||
*outSize = 0;
|
||||
|
||||
int fd = montauk::open(path);
|
||||
if (fd < 0) return false;
|
||||
|
||||
uint64_t size = montauk::getsize(fd);
|
||||
if (size < sizeof(Elf64Header) || size > (1 << 20)) {
|
||||
montauk::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* buf = (uint8_t*)montauk::alloc(size);
|
||||
if (!buf) {
|
||||
montauk::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
int read = montauk::read(fd, buf, 0, size);
|
||||
montauk::close(fd);
|
||||
if (read != (int)size) {
|
||||
montauk::free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
*outBuf = buf;
|
||||
*outSize = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isValidElf(const Elf64Header* hdr, uint64_t size) {
|
||||
if (size < sizeof(Elf64Header)) return false;
|
||||
if (hdr->e_ident[0] != 0x7f || hdr->e_ident[1] != 'E' ||
|
||||
hdr->e_ident[2] != 'L' || hdr->e_ident[3] != 'F') {
|
||||
return false;
|
||||
}
|
||||
if (hdr->e_ident[4] != 2 || hdr->e_ident[5] != 1) return false;
|
||||
if (hdr->e_machine != EM_X86_64) return false;
|
||||
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) return false;
|
||||
if (hdr->e_shentsize != sizeof(Elf64SectionHeader)) return false;
|
||||
if (hdr->e_shoff == 0 || hdr->e_shnum == 0) return false;
|
||||
if (hdr->e_shoff + uint64_t(hdr->e_shnum) * hdr->e_shentsize > size) return false;
|
||||
if (hdr->e_shstrndx >= hdr->e_shnum) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const Elf64SectionHeader* getSectionHeaders(const Elf64Header* hdr) {
|
||||
return (const Elf64SectionHeader*)((const uint8_t*)hdr + hdr->e_shoff);
|
||||
}
|
||||
|
||||
static const char* getSectionNameTable(const Elf64Header* hdr, uint64_t fileSize) {
|
||||
const Elf64SectionHeader* shdrs = getSectionHeaders(hdr);
|
||||
const Elf64SectionHeader& strhdr = shdrs[hdr->e_shstrndx];
|
||||
if (strhdr.sh_type != SHT_STRTAB) return nullptr;
|
||||
if (strhdr.sh_offset + strhdr.sh_size > fileSize) return nullptr;
|
||||
return (const char*)hdr + strhdr.sh_offset;
|
||||
}
|
||||
|
||||
static const Elf64SectionHeader* findSection(const Elf64Header* hdr, uint64_t fileSize,
|
||||
const char* name, uint32_t expectedType) {
|
||||
const Elf64SectionHeader* shdrs = getSectionHeaders(hdr);
|
||||
const char* shstrtab = getSectionNameTable(hdr, fileSize);
|
||||
if (!shstrtab) return nullptr;
|
||||
|
||||
for (uint16_t i = 0; i < hdr->e_shnum; i++) {
|
||||
const Elf64SectionHeader* sh = &shdrs[i];
|
||||
if (sh->sh_name >= shdrs[hdr->e_shstrndx].sh_size) continue;
|
||||
if (sh->sh_type != expectedType) continue;
|
||||
const char* secName = shstrtab + sh->sh_name;
|
||||
if (strcmp(secName, name) == 0) return sh;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool lookupSymbolValue(const char* path, const char* symbolName, uint64_t* outValue) {
|
||||
uint8_t* fileData = nullptr;
|
||||
uint64_t fileSize = 0;
|
||||
if (!readFile(path, &fileData, &fileSize)) return false;
|
||||
|
||||
const Elf64Header* hdr = (const Elf64Header*)fileData;
|
||||
if (!isValidElf(hdr, fileSize)) {
|
||||
montauk::free(fileData);
|
||||
return false;
|
||||
}
|
||||
|
||||
const Elf64SectionHeader* symtab = findSection(hdr, fileSize, ".dynsym", SHT_DYNSYM);
|
||||
const Elf64SectionHeader* strtab = findSection(hdr, fileSize, ".dynstr", SHT_STRTAB);
|
||||
if (!symtab || !strtab) {
|
||||
symtab = findSection(hdr, fileSize, ".symtab", SHT_SYMTAB);
|
||||
strtab = findSection(hdr, fileSize, ".strtab", SHT_STRTAB);
|
||||
}
|
||||
|
||||
if (!symtab || !strtab || symtab->sh_entsize != sizeof(Elf64Sym) ||
|
||||
symtab->sh_offset + symtab->sh_size > fileSize ||
|
||||
strtab->sh_offset + strtab->sh_size > fileSize) {
|
||||
montauk::free(fileData);
|
||||
return false;
|
||||
}
|
||||
|
||||
const Elf64Sym* symbols = (const Elf64Sym*)(fileData + symtab->sh_offset);
|
||||
const char* strings = (const char*)(fileData + strtab->sh_offset);
|
||||
uint64_t count = symtab->sh_size / sizeof(Elf64Sym);
|
||||
|
||||
bool found = false;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
const Elf64Sym& sym = symbols[i];
|
||||
if (sym.st_shndx == SHN_UNDEF || sym.st_name >= strtab->sh_size) continue;
|
||||
const char* name = strings + sym.st_name;
|
||||
if (strcmp(name, symbolName) == 0) {
|
||||
*outValue = sym.st_value;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
montauk::free(fileData);
|
||||
return found;
|
||||
}
|
||||
|
||||
static LibHandle* findFreeLib() {
|
||||
for (int i = 0; i < MaxLoadedLibs; i++) {
|
||||
if (g_loadedLibs[i].handle <= 0) return &g_loadedLibs[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static LibHandle* findLoadedLib(const char* path) {
|
||||
for (int i = 0; i < g_libCount; i++) {
|
||||
for (int i = 0; i < MaxLoadedLibs; i++) {
|
||||
if (g_loadedLibs[i].handle > 0 &&
|
||||
strcmp(g_loadedLibs[i].path, path) == 0) {
|
||||
return &g_loadedLibs[i];
|
||||
@@ -28,102 +202,6 @@ static LibHandle* findLoadedLib(const char* path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool parseSymbolFile(const char* symPath, LibHandle* lib) {
|
||||
int fd = montauk::open(symPath);
|
||||
if (fd < 0) {
|
||||
// No symbol file - that's okay, we'll still load the lib
|
||||
lib->symbolCount = 0;
|
||||
lib->symbols = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t size = montauk::getsize(fd);
|
||||
if (size == 0 || size > 65536) {
|
||||
montauk::close(fd);
|
||||
lib->symbolCount = 0;
|
||||
lib->symbols = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
char* buf = (char*)montauk::alloc(size + 1);
|
||||
if (!buf) {
|
||||
montauk::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
int read = montauk::read(fd, (uint8_t*)buf, 0, size);
|
||||
montauk::close(fd);
|
||||
if (read != (int)size) {
|
||||
montauk::free(buf);
|
||||
return false;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
|
||||
// Count lines first
|
||||
int lineCount = 0;
|
||||
for (uint64_t i = 0; i < size; i++) {
|
||||
if (buf[i] == '\n') lineCount++;
|
||||
}
|
||||
|
||||
if (lineCount == 0) {
|
||||
montauk::free(buf);
|
||||
lib->symbolCount = 0;
|
||||
lib->symbols = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
lib->symbols = (LibHandle::SymbolEntry*)montauk::alloc(lineCount * sizeof(LibHandle::SymbolEntry));
|
||||
if (!lib->symbols) {
|
||||
montauk::free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
lib->symbolCount = lineCount;
|
||||
|
||||
// Parse lines: "symbol_name,offset_hex"
|
||||
int entryIdx = 0;
|
||||
char* lineStart = buf;
|
||||
for (uint64_t i = 0; i <= size; i++) {
|
||||
if (buf[i] == '\n' || buf[i] == '\0') {
|
||||
buf[i] = '\0';
|
||||
char* comma = strchr(lineStart, ',');
|
||||
if (comma && entryIdx < lineCount) {
|
||||
size_t nameLen = comma - lineStart;
|
||||
if (nameLen < sizeof(lib->symbols[0].name) - 1) {
|
||||
memcpy(lib->symbols[entryIdx].name, lineStart, nameLen);
|
||||
lib->symbols[entryIdx].name[nameLen] = '\0';
|
||||
lib->symbols[entryIdx].offset = hexToUint64(comma + 1);
|
||||
entryIdx++;
|
||||
}
|
||||
}
|
||||
lineStart = &buf[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
lib->symbolCount = entryIdx;
|
||||
montauk::free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t hexToUint64(const char* str) {
|
||||
uint64_t result = 0;
|
||||
// Skip optional "0x" prefix
|
||||
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
|
||||
str += 2;
|
||||
}
|
||||
while (*str) {
|
||||
char c = *str;
|
||||
uint64_t val = 0;
|
||||
if (c >= '0' && c <= '9') val = c - '0';
|
||||
else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
|
||||
else break;
|
||||
result = (result << 4) | val;
|
||||
str++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LibHandle* dlopen(const char* path) {
|
||||
if (!path) return nullptr;
|
||||
|
||||
@@ -134,33 +212,21 @@ LibHandle* dlopen(const char* path) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (g_libCount >= MaxLoadedLibs) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load the library via syscall
|
||||
int handle = montauk::load_lib(path);
|
||||
if (handle <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create symbol file path: append ".sym"
|
||||
char symPath[320];
|
||||
size_t pathLen = strlen(path);
|
||||
if (pathLen >= sizeof(symPath) - 5) return nullptr;
|
||||
LibHandle* lib = findFreeLib();
|
||||
if (!lib) {
|
||||
montauk::unload_lib(handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
strcpy(symPath, path);
|
||||
strcpy(symPath + pathLen, ".sym");
|
||||
|
||||
LibHandle* lib = &g_loadedLibs[g_libCount++];
|
||||
lib->handle = handle;
|
||||
lib->base = 0; // Will be retrieved on first dlsym
|
||||
lib->base = montauk::get_libbase(handle);
|
||||
strcpy(lib->path, path);
|
||||
lib->symbolCount = 0;
|
||||
lib->symbols = nullptr;
|
||||
|
||||
// Parse symbol file
|
||||
parseSymbolFile(symPath, lib);
|
||||
|
||||
return lib;
|
||||
}
|
||||
@@ -173,15 +239,9 @@ void* dlsym(LibHandle* lib, const char* symbolName) {
|
||||
lib->base = montauk::get_libbase(lib->handle);
|
||||
}
|
||||
|
||||
// Look up in symbol table
|
||||
for (int i = 0; i < lib->symbolCount; i++) {
|
||||
if (strcmp(lib->symbols[i].name, symbolName) == 0) {
|
||||
uint64_t offset = lib->symbols[i].offset;
|
||||
return (void*)montauk::dlsym(lib->handle, offset);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr; // Symbol not found
|
||||
uint64_t value = 0;
|
||||
if (!lookupSymbolValue(lib->path, symbolName, &value)) return nullptr;
|
||||
return (void*)(lib->base + value);
|
||||
}
|
||||
|
||||
int dlclose(LibHandle* lib) {
|
||||
@@ -189,14 +249,8 @@ int dlclose(LibHandle* lib) {
|
||||
|
||||
int result = montauk::unload_lib(lib->handle);
|
||||
if (result == 0) {
|
||||
// Free symbol table if allocated
|
||||
if (lib->symbols) {
|
||||
montauk::free((void*)lib->symbols);
|
||||
lib->symbols = nullptr;
|
||||
}
|
||||
lib->handle = 0;
|
||||
lib->base = 0;
|
||||
lib->symbolCount = 0;
|
||||
lib->path[0] = '\0';
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ LIBDIR := ../../bin
|
||||
|
||||
all: $(BINDIR)/test_dl.elf
|
||||
|
||||
$(BINDIR)/test_dl.elf: main.cpp
|
||||
$(BINDIR)/test_dl.elf: main.cpp Makefile $(LIBDIR)/liblibloader.a ../../lib/libc/liblibc.a
|
||||
@mkdir -p $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) main.o $(LIBDIR)/liblibloader.a ../../lib/libc/liblibc.a -o $@
|
||||
|
||||
Reference in New Issue
Block a user