feat: revamp libraries to use ELF shared objects
This commit is contained in:
@@ -23,11 +23,15 @@ namespace Montauk {
|
|||||||
// Library entry tracking
|
// Library entry tracking
|
||||||
struct LibEntry {
|
struct LibEntry {
|
||||||
char path[128];
|
char path[128];
|
||||||
uint64_t baseAddr;
|
uint64_t loadBias;
|
||||||
uint32_t refcount;
|
uint32_t refcount;
|
||||||
bool inUse;
|
bool inUse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint64_t GetLibSlotBase(int libSlot) {
|
||||||
|
return Sched::LIB_BASE + (uint64_t(libSlot) * Sched::LIB_MAX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
// Per-process library table
|
// Per-process library table
|
||||||
static LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess];
|
static LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess];
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ namespace Montauk {
|
|||||||
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
||||||
g_libTable[slot][i].inUse = false;
|
g_libTable[slot][i].inUse = false;
|
||||||
g_libTable[slot][i].refcount = 0;
|
g_libTable[slot][i].refcount = 0;
|
||||||
g_libTable[slot][i].baseAddr = 0;
|
g_libTable[slot][i].loadBias = 0;
|
||||||
g_libTable[slot][i].path[0] = '\0';
|
g_libTable[slot][i].path[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,8 +81,8 @@ namespace Montauk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the library into the process's address space
|
// Load the library into the process's address space
|
||||||
uint64_t libBase = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot);
|
uint64_t loadBias = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot);
|
||||||
if (libBase == 0) {
|
if (loadBias == 0) {
|
||||||
Kt::KernelLogStream(Kt::ERROR, "Lib") << "Failed to load library: " << path;
|
Kt::KernelLogStream(Kt::ERROR, "Lib") << "Failed to load library: " << path;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -86,10 +90,10 @@ namespace Montauk {
|
|||||||
// Store library info
|
// Store library info
|
||||||
g_libTable[slot][libSlot].inUse = true;
|
g_libTable[slot][libSlot].inUse = true;
|
||||||
g_libTable[slot][libSlot].refcount = 1;
|
g_libTable[slot][libSlot].refcount = 1;
|
||||||
g_libTable[slot][libSlot].baseAddr = libBase;
|
g_libTable[slot][libSlot].loadBias = loadBias;
|
||||||
Lib::strncpy(g_libTable[slot][libSlot].path, path, sizeof(LibEntry::path));
|
Lib::strncpy(g_libTable[slot][libSlot].path, path, sizeof(LibEntry::path));
|
||||||
|
|
||||||
Kt::KernelLogStream(Kt::OK, "Lib") << "Loaded library: " << path << " at base " << kcp::hex << libBase << kcp::dec;
|
Kt::KernelLogStream(Kt::OK, "Lib") << "Loaded library: " << path << " with load bias " << kcp::hex << loadBias << kcp::dec;
|
||||||
|
|
||||||
return (uint64_t)(libSlot + 1); // Handle = slot + 1 (0 = invalid)
|
return (uint64_t)(libSlot + 1); // Handle = slot + 1 (0 = invalid)
|
||||||
}
|
}
|
||||||
@@ -108,7 +112,7 @@ namespace Montauk {
|
|||||||
g_libTable[slot][libSlot].refcount--;
|
g_libTable[slot][libSlot].refcount--;
|
||||||
if (g_libTable[slot][libSlot].refcount == 0) {
|
if (g_libTable[slot][libSlot].refcount == 0) {
|
||||||
// Actually unload - free the pages
|
// Actually unload - free the pages
|
||||||
uint64_t libBase = g_libTable[slot][libSlot].baseAddr;
|
uint64_t libBase = GetLibSlotBase(libSlot);
|
||||||
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
||||||
|
|
||||||
auto* proc = Sched::GetCurrentProcessPtr();
|
auto* proc = Sched::GetCurrentProcessPtr();
|
||||||
@@ -124,7 +128,7 @@ namespace Montauk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_libTable[slot][libSlot].inUse = false;
|
g_libTable[slot][libSlot].inUse = false;
|
||||||
g_libTable[slot][libSlot].baseAddr = 0;
|
g_libTable[slot][libSlot].loadBias = 0;
|
||||||
g_libTable[slot][libSlot].path[0] = '\0';
|
g_libTable[slot][libSlot].path[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +137,7 @@ namespace Montauk {
|
|||||||
|
|
||||||
// Resolve a symbol in a loaded library.
|
// Resolve a symbol in a loaded library.
|
||||||
// handle = library handle from LoadLib
|
// handle = library handle from LoadLib
|
||||||
// symbolOffset = offset of the symbol from the library's base address
|
// symbolOffset = ELF symbol value from the library's symbol table
|
||||||
// Returns the virtual address of the symbol, or 0 if not found.
|
// Returns the virtual address of the symbol, or 0 if not found.
|
||||||
static uint64_t Sys_DLSym(uint64_t handle, uint64_t symbolOffset) {
|
static uint64_t Sys_DLSym(uint64_t handle, uint64_t symbolOffset) {
|
||||||
int slot = GetCurrentSlot();
|
int slot = GetCurrentSlot();
|
||||||
@@ -144,11 +148,11 @@ namespace Montauk {
|
|||||||
|
|
||||||
if (!g_libTable[slot][libSlot].inUse) return 0;
|
if (!g_libTable[slot][libSlot].inUse) return 0;
|
||||||
|
|
||||||
uint64_t libBase = g_libTable[slot][libSlot].baseAddr;
|
uint64_t loadBias = g_libTable[slot][libSlot].loadBias;
|
||||||
return libBase + symbolOffset;
|
return loadBias + symbolOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get library base address (for userspace symbol resolution)
|
// Get library relocation base / load bias (for userspace symbol resolution)
|
||||||
static uint64_t Sys_GetLibBase(uint64_t handle) {
|
static uint64_t Sys_GetLibBase(uint64_t handle) {
|
||||||
int slot = GetCurrentSlot();
|
int slot = GetCurrentSlot();
|
||||||
if (slot < 0) return 0;
|
if (slot < 0) return 0;
|
||||||
@@ -158,7 +162,7 @@ namespace Montauk {
|
|||||||
|
|
||||||
if (!g_libTable[slot][libSlot].inUse) return 0;
|
if (!g_libTable[slot][libSlot].inUse) return 0;
|
||||||
|
|
||||||
return g_libTable[slot][libSlot].baseAddr;
|
return g_libTable[slot][libSlot].loadBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup library table for a process slot
|
// Cleanup library table for a process slot
|
||||||
@@ -171,7 +175,7 @@ namespace Montauk {
|
|||||||
// Unmap all libraries for this process
|
// Unmap all libraries for this process
|
||||||
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
||||||
if (g_libTable[slot][i].inUse) {
|
if (g_libTable[slot][i].inUse) {
|
||||||
uint64_t libBase = g_libTable[slot][i].baseAddr;
|
uint64_t libBase = GetLibSlotBase(i);
|
||||||
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
||||||
|
|
||||||
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
|
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
|
||||||
@@ -184,7 +188,7 @@ namespace Montauk {
|
|||||||
|
|
||||||
g_libTable[slot][i].inUse = false;
|
g_libTable[slot][i].inUse = false;
|
||||||
g_libTable[slot][i].refcount = 0;
|
g_libTable[slot][i].refcount = 0;
|
||||||
g_libTable[slot][i].baseAddr = 0;
|
g_libTable[slot][i].loadBias = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+396
-27
@@ -16,6 +16,368 @@
|
|||||||
|
|
||||||
namespace Sched {
|
namespace Sched {
|
||||||
|
|
||||||
|
struct Elf64Dynamic {
|
||||||
|
int64_t d_tag;
|
||||||
|
union {
|
||||||
|
uint64_t d_val;
|
||||||
|
uint64_t d_ptr;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64Rela {
|
||||||
|
uint64_t r_offset;
|
||||||
|
uint64_t r_info;
|
||||||
|
int64_t r_addend;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DynamicInfo {
|
||||||
|
uint64_t hashVaddr = 0;
|
||||||
|
uint64_t symtabVaddr = 0;
|
||||||
|
uint64_t strtabVaddr = 0;
|
||||||
|
uint64_t strtabSize = 0;
|
||||||
|
uint64_t relaVaddr = 0;
|
||||||
|
uint64_t relaSize = 0;
|
||||||
|
uint64_t relaEnt = sizeof(Elf64Rela);
|
||||||
|
uint64_t jmprelVaddr = 0;
|
||||||
|
uint64_t pltrelSize = 0;
|
||||||
|
uint64_t pltRelType = 0;
|
||||||
|
uint64_t symEnt = sizeof(Elf64Sym);
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint64_t DT_NULL = 0;
|
||||||
|
static constexpr uint64_t DT_PLTRELSZ = 2;
|
||||||
|
static constexpr uint64_t DT_HASH = 4;
|
||||||
|
static constexpr uint64_t DT_STRTAB = 5;
|
||||||
|
static constexpr uint64_t DT_SYMTAB = 6;
|
||||||
|
static constexpr uint64_t DT_RELA = 7;
|
||||||
|
static constexpr uint64_t DT_RELASZ = 8;
|
||||||
|
static constexpr uint64_t DT_RELAENT = 9;
|
||||||
|
static constexpr uint64_t DT_STRSZ = 10;
|
||||||
|
static constexpr uint64_t DT_SYMENT = 11;
|
||||||
|
static constexpr uint64_t DT_PLTREL = 20;
|
||||||
|
static constexpr uint64_t DT_JMPREL = 23;
|
||||||
|
|
||||||
|
static constexpr uint32_t R_X86_64_64 = 1;
|
||||||
|
static constexpr uint32_t R_X86_64_GLOB_DAT = 6;
|
||||||
|
static constexpr uint32_t R_X86_64_JUMP_SLOT = 7;
|
||||||
|
static constexpr uint32_t R_X86_64_RELATIVE = 8;
|
||||||
|
|
||||||
|
static constexpr uint16_t SHN_UNDEF = 0;
|
||||||
|
static constexpr uint8_t STB_WEAK = 2;
|
||||||
|
|
||||||
|
static constexpr uint32_t Elf64RelocType(uint64_t info) {
|
||||||
|
return static_cast<uint32_t>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint32_t Elf64RelocSym(uint64_t info) {
|
||||||
|
return static_cast<uint32_t>(info >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t AddSigned(uint64_t base, int64_t addend) {
|
||||||
|
if (addend >= 0) {
|
||||||
|
return base + static_cast<uint64_t>(addend);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base - static_cast<uint64_t>(-addend);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ValidateProgramHeaderTable(const Elf64Header* hdr, uint64_t fileSize) {
|
||||||
|
if (hdr->e_phentsize != sizeof(Elf64ProgramHeader)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected program header size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->e_phoff > fileSize ||
|
||||||
|
hdr->e_phoff + uint64_t(hdr->e_phnum) * hdr->e_phentsize > fileSize) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Program headers outside file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ValidateLoadSegment(const Elf64ProgramHeader* phdr, uint64_t fileSize) {
|
||||||
|
if (phdr->p_filesz > phdr->p_memsz) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment file size exceeds memory size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phdr->p_offset > fileSize || phdr->p_offset + phdr->p_filesz > fileSize) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ValidateLibElfHeader(const Elf64Header* hdr) {
|
||||||
|
if (hdr->e_ident[0] != 0x7f ||
|
||||||
|
hdr->e_ident[1] != 'E' ||
|
||||||
|
hdr->e_ident[2] != 'L' ||
|
||||||
|
hdr->e_ident[3] != 'F') {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Invalid ELF magic";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->e_ident[4] != 2) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->e_ident[5] != 1) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->e_type != ET_DYN) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library is not ET_DYN (type=" << (uint64_t)hdr->e_type << ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->e_machine != EM_X86_64) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not x86_64 (machine=" << (uint64_t)hdr->e_machine << ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool VirtToFileOffset(const Elf64Header* hdr, uint64_t fileSize,
|
||||||
|
uint64_t virtualAddress, uint64_t size, uint64_t* outOffset) {
|
||||||
|
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
||||||
|
auto* phdr = (const Elf64ProgramHeader*)((const uint8_t*)hdr + hdr->e_phoff + i * hdr->e_phentsize);
|
||||||
|
|
||||||
|
if (phdr->p_type != PT_LOAD || phdr->p_filesz == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateLoadSegment(phdr, fileSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virtualAddress < phdr->p_vaddr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virtualAddress + size > phdr->p_vaddr + phdr->p_filesz) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outOffset = phdr->p_offset + (virtualAddress - phdr->p_vaddr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to translate virtual address " << kcp::hex
|
||||||
|
<< virtualAddress << kcp::dec << " to file offset";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool WriteUser64(uint64_t pml4Phys, uint64_t virtualAddress, uint64_t value) {
|
||||||
|
uint64_t physBase = Memory::VMM::Paging::GetPhysAddr(pml4Phys, virtualAddress);
|
||||||
|
if (physBase == 0) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Relocation target not mapped";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t physAddress = physBase + (virtualAddress & 0xFFFULL);
|
||||||
|
*(uint64_t*)Memory::HHDM(physAddress) = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ApplyRelaBlock(uint64_t pml4Phys, uint64_t loadBias,
|
||||||
|
const Elf64Rela* relas, uint64_t relaCount,
|
||||||
|
const Elf64Sym* dynsym, uint64_t dynsymCount,
|
||||||
|
const char* dynstr, uint64_t dynstrSize) {
|
||||||
|
for (uint64_t i = 0; i < relaCount; i++) {
|
||||||
|
const Elf64Rela& rela = relas[i];
|
||||||
|
uint32_t type = Elf64RelocType(rela.r_info);
|
||||||
|
uint32_t symIndex = Elf64RelocSym(rela.r_info);
|
||||||
|
uint64_t targetVA = loadBias + rela.r_offset;
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
|
value = AddSigned(loadBias, rela.r_addend);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R_X86_64_GLOB_DAT:
|
||||||
|
case R_X86_64_JUMP_SLOT:
|
||||||
|
case R_X86_64_64: {
|
||||||
|
if (symIndex >= dynsymCount) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Relocation symbol index out of range";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Elf64Sym& sym = dynsym[symIndex];
|
||||||
|
if (sym.st_shndx == SHN_UNDEF) {
|
||||||
|
uint8_t bind = sym.st_info >> 4;
|
||||||
|
if (bind == STB_WEAK) {
|
||||||
|
value = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = "<unnamed>";
|
||||||
|
if (dynstr != nullptr && sym.st_name < dynstrSize) {
|
||||||
|
name = dynstr + sym.st_name;
|
||||||
|
}
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unresolved dynamic symbol: " << name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = AddSigned(loadBias + sym.st_value, rela.r_addend);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unsupported relocation type=" << (uint64_t)type;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WriteUser64(pml4Phys, targetVA, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ApplyDynamicRelocations(const uint8_t* fileData, uint64_t fileSize,
|
||||||
|
const Elf64Header* hdr, uint64_t pml4Phys,
|
||||||
|
uint64_t loadBias) {
|
||||||
|
const Elf64ProgramHeader* dynamicPhdr = nullptr;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
||||||
|
auto* phdr = (const Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
|
||||||
|
if (phdr->p_type == PT_DYNAMIC) {
|
||||||
|
dynamicPhdr = phdr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicPhdr == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicPhdr->p_offset > fileSize || dynamicPhdr->p_offset + dynamicPhdr->p_filesz > fileSize) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Dynamic section outside file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicInfo info;
|
||||||
|
uint64_t dynCount = dynamicPhdr->p_filesz / sizeof(Elf64Dynamic);
|
||||||
|
auto* dyn = (const Elf64Dynamic*)(fileData + dynamicPhdr->p_offset);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < dynCount; i++) {
|
||||||
|
if (dyn[i].d_tag == DT_NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dyn[i].d_tag) {
|
||||||
|
case DT_HASH: info.hashVaddr = dyn[i].d_ptr; break;
|
||||||
|
case DT_SYMTAB: info.symtabVaddr = dyn[i].d_ptr; break;
|
||||||
|
case DT_STRTAB: info.strtabVaddr = dyn[i].d_ptr; break;
|
||||||
|
case DT_STRSZ: info.strtabSize = dyn[i].d_val; break;
|
||||||
|
case DT_RELA: info.relaVaddr = dyn[i].d_ptr; break;
|
||||||
|
case DT_RELASZ: info.relaSize = dyn[i].d_val; break;
|
||||||
|
case DT_RELAENT: info.relaEnt = dyn[i].d_val; break;
|
||||||
|
case DT_JMPREL: info.jmprelVaddr = dyn[i].d_ptr; break;
|
||||||
|
case DT_PLTRELSZ: info.pltrelSize = dyn[i].d_val; break;
|
||||||
|
case DT_PLTREL: info.pltRelType = dyn[i].d_val; break;
|
||||||
|
case DT_SYMENT: info.symEnt = dyn[i].d_val; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.relaSize == 0 && info.pltrelSize == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.symEnt != sizeof(Elf64Sym)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected dynamic symbol size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.relaEnt != sizeof(Elf64Rela)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected RELA entry size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.symtabVaddr == 0 || info.hashVaddr == 0) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Missing dynamic symbol metadata";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hashOffset = 0;
|
||||||
|
if (!VirtToFileOffset(hdr, fileSize, info.hashVaddr, sizeof(uint32_t) * 2, &hashOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* hashWords = (const uint32_t*)(fileData + hashOffset);
|
||||||
|
uint64_t dynsymCount = hashWords[1];
|
||||||
|
|
||||||
|
uint64_t dynsymOffset = 0;
|
||||||
|
if (!VirtToFileOffset(hdr, fileSize, info.symtabVaddr,
|
||||||
|
dynsymCount * sizeof(Elf64Sym), &dynsymOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* dynsym = (const Elf64Sym*)(fileData + dynsymOffset);
|
||||||
|
const char* dynstr = nullptr;
|
||||||
|
if (info.strtabVaddr != 0 && info.strtabSize != 0) {
|
||||||
|
uint64_t dynstrOffset = 0;
|
||||||
|
if (!VirtToFileOffset(hdr, fileSize, info.strtabVaddr, info.strtabSize, &dynstrOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dynstr = (const char*)(fileData + dynstrOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.relaSize != 0) {
|
||||||
|
uint64_t relaOffset = 0;
|
||||||
|
if (!VirtToFileOffset(hdr, fileSize, info.relaVaddr, info.relaSize, &relaOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* relas = (const Elf64Rela*)(fileData + relaOffset);
|
||||||
|
if (!ApplyRelaBlock(pml4Phys, loadBias, relas, info.relaSize / sizeof(Elf64Rela),
|
||||||
|
dynsym, dynsymCount, dynstr, info.strtabSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.pltrelSize != 0) {
|
||||||
|
if (info.pltRelType != DT_RELA) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unsupported PLT relocation format";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t jmprelOffset = 0;
|
||||||
|
if (!VirtToFileOffset(hdr, fileSize, info.jmprelVaddr, info.pltrelSize, &jmprelOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* pltRelas = (const Elf64Rela*)(fileData + jmprelOffset);
|
||||||
|
if (!ApplyRelaBlock(pml4Phys, loadBias, pltRelas, info.pltrelSize / sizeof(Elf64Rela),
|
||||||
|
dynsym, dynsymCount, dynstr, info.strtabSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidateElfHeader(const Elf64Header* hdr) {
|
static bool ValidateElfHeader(const Elf64Header* hdr) {
|
||||||
// Check ELF magic: 0x7f 'E' 'L' 'F'
|
// Check ELF magic: 0x7f 'E' 'L' 'F'
|
||||||
if (hdr->e_ident[0] != 0x7f ||
|
if (hdr->e_ident[0] != 0x7f ||
|
||||||
@@ -177,43 +539,48 @@ namespace Sched {
|
|||||||
|
|
||||||
Elf64Header* hdr = (Elf64Header*)fileData;
|
Elf64Header* hdr = (Elf64Header*)fileData;
|
||||||
|
|
||||||
// Validate ELF header
|
if (!ValidateLibElfHeader(hdr) || !ValidateProgramHeaderTable(hdr, fileSize)) {
|
||||||
if (hdr->e_ident[0] != 0x7f ||
|
|
||||||
hdr->e_ident[1] != 'E' ||
|
|
||||||
hdr->e_ident[2] != 'L' ||
|
|
||||||
hdr->e_ident[3] != 'F') {
|
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Invalid ELF magic";
|
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->e_ident[4] != 2) {
|
uint64_t slotBase = LIB_BASE + (uint64_t(slot) * LIB_MAX_SIZE);
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF";
|
uint64_t minLoadStart = ~0ULL;
|
||||||
|
uint64_t maxLoadEnd = 0;
|
||||||
|
bool hasLoadSegment = false;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
||||||
|
auto* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
|
||||||
|
|
||||||
|
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateLoadSegment(phdr, fileSize)) {
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->e_ident[5] != 1) {
|
uint64_t segStart = phdr->p_vaddr & ~0xFFFULL;
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian";
|
uint64_t segEnd = (phdr->p_vaddr + phdr->p_memsz + 0xFFFULL) & ~0xFFFULL;
|
||||||
|
if (segStart < minLoadStart) minLoadStart = segStart;
|
||||||
|
if (segEnd > maxLoadEnd) maxLoadEnd = segEnd;
|
||||||
|
hasLoadSegment = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLoadSegment) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library has no loadable segments";
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Libraries are built as ET_EXEC at a fixed base (0x60000000)
|
if (maxLoadEnd - minLoadStart > LIB_MAX_SIZE) {
|
||||||
if (hdr->e_type != ET_EXEC) {
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library image exceeds slot size";
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library not ET_EXEC (type=" << (uint64_t)hdr->e_type << ")";
|
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->e_machine != EM_X86_64) {
|
uint64_t loadBias = slotBase - minLoadStart;
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not x86_64 (machine=" << (uint64_t)hdr->e_machine << ")";
|
|
||||||
Memory::g_heap->Free(fileData);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate library base for this slot (each slot gets LIB_MAX_SIZE region)
|
|
||||||
uint64_t libBase = LIB_BASE + (uint64_t(slot) * LIB_MAX_SIZE);
|
|
||||||
|
|
||||||
// Process program headers
|
// Process program headers
|
||||||
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
||||||
@@ -227,9 +594,7 @@ namespace Sched {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The library is linked at 0x400000. We need to relocate it to libBase.
|
uint64_t targetSegStart = loadBias + phdr->p_vaddr;
|
||||||
// target_vaddr = libBase + (file_vaddr - 0x400000)
|
|
||||||
uint64_t targetSegStart = libBase + (phdr->p_vaddr - 0x400000ULL);
|
|
||||||
uint64_t targetSegFileEnd = targetSegStart + phdr->p_filesz; // only file data
|
uint64_t targetSegFileEnd = targetSegStart + phdr->p_filesz; // only file data
|
||||||
uint64_t targetSegMemEnd = targetSegStart + phdr->p_memsz; // includes bss
|
uint64_t targetSegMemEnd = targetSegStart + phdr->p_memsz; // includes bss
|
||||||
|
|
||||||
@@ -251,7 +616,7 @@ namespace Sched {
|
|||||||
uint64_t pageEnd = pageStart + 0x1000;
|
uint64_t pageEnd = pageStart + 0x1000;
|
||||||
|
|
||||||
// Check that we're within the library region
|
// Check that we're within the library region
|
||||||
if (pageStart < libBase || pageEnd > libBase + LIB_MAX_SIZE) {
|
if (pageStart < slotBase || pageEnd > slotBase + LIB_MAX_SIZE) {
|
||||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside library region";
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside library region";
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -282,10 +647,14 @@ namespace Sched {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ApplyDynamicRelocations(fileData, fileSize, hdr, pml4Phys, loadBias)) {
|
||||||
|
Memory::g_heap->Free(fileData);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Memory::g_heap->Free(fileData);
|
Memory::g_heap->Free(fileData);
|
||||||
|
|
||||||
// Return the library base address for this slot
|
return loadBias;
|
||||||
return libBase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ namespace Sched {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static constexpr uint32_t PT_LOAD = 1;
|
static constexpr uint32_t PT_LOAD = 1;
|
||||||
|
static constexpr uint32_t PT_DYNAMIC = 2;
|
||||||
static constexpr uint16_t ET_EXEC = 2;
|
static constexpr uint16_t ET_EXEC = 2;
|
||||||
|
static constexpr uint16_t ET_DYN = 3;
|
||||||
static constexpr uint16_t EM_X86_64 = 62;
|
static constexpr uint16_t EM_X86_64 = 62;
|
||||||
|
|
||||||
// Fixed base address for shared libraries.
|
// Fixed base address for shared libraries.
|
||||||
@@ -51,8 +53,7 @@ namespace Sched {
|
|||||||
uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys);
|
uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys);
|
||||||
|
|
||||||
// Load a shared library into a fixed address range.
|
// Load a shared library into a fixed address range.
|
||||||
// Loads at LIB_BASE for the given slot.
|
// Returns the relocation base / load bias on success, or 0 on failure.
|
||||||
// Returns the library base virtual address on success, or 0 on failure.
|
|
||||||
uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot);
|
uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,8 @@
|
|||||||
// Library handle - opaque to users
|
// Library handle - opaque to users
|
||||||
struct LibHandle {
|
struct LibHandle {
|
||||||
int handle; // syscall handle (slot + 1)
|
int handle; // syscall handle (slot + 1)
|
||||||
uint64_t base; // base virtual address
|
uint64_t base; // relocation base / load bias for symbol values
|
||||||
char path[256];
|
char path[256];
|
||||||
int symbolCount;
|
|
||||||
struct SymbolEntry {
|
|
||||||
char name[64];
|
|
||||||
uint64_t offset;
|
|
||||||
}* symbols;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace libloader {
|
namespace libloader {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Makefile for libhello shared library
|
# Makefile for libhello shared library
|
||||||
# Builds libhello.lib at fixed address 0x400000 (relocated at load time)
|
# Builds libhello.lib as an ELF shared object (ET_DYN).
|
||||||
|
|
||||||
# Target architecture.
|
# Target architecture.
|
||||||
ARCH := x86_64
|
ARCH := x86_64
|
||||||
@@ -7,12 +7,12 @@ ARCH := x86_64
|
|||||||
# Toolchain
|
# Toolchain
|
||||||
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||||
|
LD := $(TOOLCHAIN_PREFIX)ld
|
||||||
NM := $(TOOLCHAIN_PREFIX)nm
|
NM := $(TOOLCHAIN_PREFIX)nm
|
||||||
STRIP := $(TOOLCHAIN_PREFIX)strip
|
STRIP := $(TOOLCHAIN_PREFIX)strip
|
||||||
|
|
||||||
# Compiler flags: freestanding, no stdlib
|
# Compiler flags: freestanding, no stdlib
|
||||||
# Build as PIE so library-internal data references remain valid after the
|
# Build as PIC so the final ET_DYN image can be relocated by the kernel loader.
|
||||||
# kernel maps the image into a per-process library slot.
|
|
||||||
override CXXFLAGS := \
|
override CXXFLAGS := \
|
||||||
-std=gnu++20 \
|
-std=gnu++20 \
|
||||||
-g -O2 -pipe \
|
-g -O2 -pipe \
|
||||||
@@ -22,7 +22,7 @@ override CXXFLAGS := \
|
|||||||
-ffreestanding \
|
-ffreestanding \
|
||||||
-fno-stack-protector \
|
-fno-stack-protector \
|
||||||
-fno-stack-check \
|
-fno-stack-check \
|
||||||
-fPIE \
|
-fPIC \
|
||||||
-fno-rtti \
|
-fno-rtti \
|
||||||
-fno-exceptions \
|
-fno-exceptions \
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
@@ -40,14 +40,13 @@ override CXXFLAGS := \
|
|||||||
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
||||||
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||||
|
|
||||||
# Linker flags: freestanding static ELF at fixed address
|
# Linker flags for a bare-metal ELF shared object.
|
||||||
# Note: NOT using --gc-sections so all functions are kept
|
|
||||||
override LDFLAGS := \
|
override LDFLAGS := \
|
||||||
-nostdlib \
|
-shared \
|
||||||
-static \
|
--build-id=none \
|
||||||
-Wl,--build-id=none \
|
--hash-style=sysv \
|
||||||
-Wl,-m,elf_x86_64 \
|
-m elf_x86_64 \
|
||||||
-T link.ld
|
-z max-page-size=0x1000
|
||||||
|
|
||||||
# Output
|
# Output
|
||||||
OUTDIR := ../../bin/os
|
OUTDIR := ../../bin/os
|
||||||
@@ -56,7 +55,7 @@ LIBSRC := src/libhello.cpp
|
|||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib.sym
|
all: $(OUTDIR)/$(LIBNAME).lib
|
||||||
|
|
||||||
$(OUTDIR):
|
$(OUTDIR):
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
@@ -68,16 +67,8 @@ src/stb_truetype_impl.o: src/stb_truetype_impl.cpp Makefile | $(OUTDIR)
|
|||||||
$(CXX) $(CXXFLAGS) -c src/stb_truetype_impl.cpp -o $@
|
$(CXX) $(CXXFLAGS) -c src/stb_truetype_impl.cpp -o $@
|
||||||
|
|
||||||
$(OUTDIR)/$(LIBNAME).lib: src/libhello.o src/stb_truetype_impl.o
|
$(OUTDIR)/$(LIBNAME).lib: src/libhello.o src/stb_truetype_impl.o
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) src/libhello.o src/stb_truetype_impl.o -o $@
|
rm -f $(OUTDIR)/$(LIBNAME).lib.sym
|
||||||
|
$(LD) $(LDFLAGS) src/libhello.o src/stb_truetype_impl.o -o $@
|
||||||
# Generate symbol file from nm output
|
|
||||||
# Format: "symbol_name,offset_in_hex"
|
|
||||||
$(OUTDIR)/$(LIBNAME).lib.sym: $(OUTDIR)/$(LIBNAME).lib
|
|
||||||
@mkdir -p $(OUTDIR)
|
|
||||||
@echo "# Symbol table for $(LIBNAME)" > $@
|
|
||||||
@echo "# This file maps symbol names to offsets from library base" >> $@
|
|
||||||
@$(NM) $(OUTDIR)/$(LIBNAME).lib | grep ' T ' | awk '{printf "%s,0x%x\n", $$3, strtonum("0x" $$1) - 0x400000}' >> $@
|
|
||||||
@echo "Generated symbol file: $@"
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libhello.o src/stb_truetype_impl.o
|
rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libhello.o src/stb_truetype_impl.o
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
# Makefile for libmath shared library
|
# Makefile for libmath shared library
|
||||||
# Builds libmath.lib at fixed address 0x400000 (relocated at load time)
|
# Builds libmath.lib as an ELF shared object (ET_DYN).
|
||||||
|
|
||||||
# Target architecture.
|
# Target architecture.
|
||||||
ARCH := x86_64
|
ARCH := x86_64
|
||||||
@@ -7,12 +7,12 @@ ARCH := x86_64
|
|||||||
# Toolchain
|
# Toolchain
|
||||||
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||||
|
LD := $(TOOLCHAIN_PREFIX)ld
|
||||||
NM := $(TOOLCHAIN_PREFIX)nm
|
NM := $(TOOLCHAIN_PREFIX)nm
|
||||||
STRIP := $(TOOLCHAIN_PREFIX)strip
|
STRIP := $(TOOLCHAIN_PREFIX)strip
|
||||||
|
|
||||||
# Compiler flags: freestanding, no stdlib
|
# Compiler flags: freestanding, no stdlib
|
||||||
# Build as PIE so library code remains valid when loaded into a slot-specific
|
# Build as PIC so the final ET_DYN image can be relocated by the kernel loader.
|
||||||
# base address by the kernel loader.
|
|
||||||
override CXXFLAGS := \
|
override CXXFLAGS := \
|
||||||
-std=gnu++20 \
|
-std=gnu++20 \
|
||||||
-g -O2 -pipe \
|
-g -O2 -pipe \
|
||||||
@@ -22,7 +22,7 @@ override CXXFLAGS := \
|
|||||||
-ffreestanding \
|
-ffreestanding \
|
||||||
-fno-stack-protector \
|
-fno-stack-protector \
|
||||||
-fno-stack-check \
|
-fno-stack-check \
|
||||||
-fPIE \
|
-fPIC \
|
||||||
-fno-rtti \
|
-fno-rtti \
|
||||||
-fno-exceptions \
|
-fno-exceptions \
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
@@ -39,14 +39,13 @@ override CXXFLAGS := \
|
|||||||
-isystem ../../include/libc \
|
-isystem ../../include/libc \
|
||||||
-isystem ../../include/montauk
|
-isystem ../../include/montauk
|
||||||
|
|
||||||
# Linker flags: freestanding static ELF at fixed address
|
# Linker flags for a bare-metal ELF shared object.
|
||||||
# Note: NOT using --gc-sections so all functions are kept
|
|
||||||
override LDFLAGS := \
|
override LDFLAGS := \
|
||||||
-nostdlib \
|
-shared \
|
||||||
-static \
|
--build-id=none \
|
||||||
-Wl,--build-id=none \
|
--hash-style=sysv \
|
||||||
-Wl,-m,elf_x86_64 \
|
-m elf_x86_64 \
|
||||||
-T link.ld
|
-z max-page-size=0x1000
|
||||||
|
|
||||||
# Output
|
# Output
|
||||||
OUTDIR := ../../bin/os
|
OUTDIR := ../../bin/os
|
||||||
@@ -55,21 +54,13 @@ LIBSRC := src/libmath.cpp
|
|||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib.sym
|
all: $(OUTDIR)/$(LIBNAME).lib
|
||||||
|
|
||||||
$(OUTDIR)/$(LIBNAME).lib: $(LIBSRC) Makefile
|
$(OUTDIR)/$(LIBNAME).lib: $(LIBSRC) Makefile
|
||||||
@mkdir -p $(OUTDIR)
|
@mkdir -p $(OUTDIR)
|
||||||
|
rm -f $(OUTDIR)/$(LIBNAME).lib.sym
|
||||||
$(CXX) $(CXXFLAGS) -c $(LIBSRC) -o src/libmath.o
|
$(CXX) $(CXXFLAGS) -c $(LIBSRC) -o src/libmath.o
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) src/libmath.o -o $@
|
$(LD) $(LDFLAGS) src/libmath.o -o $@
|
||||||
|
|
||||||
# Generate symbol file from nm output
|
|
||||||
# Format: "symbol_name,offset_in_hex"
|
|
||||||
$(OUTDIR)/$(LIBNAME).lib.sym: $(OUTDIR)/$(LIBNAME).lib
|
|
||||||
@mkdir -p $(OUTDIR)
|
|
||||||
@echo "# Symbol table for $(LIBNAME)" > $@
|
|
||||||
@echo "# This file maps symbol names to offsets from library base" >> $@
|
|
||||||
@$(NM) $(OUTDIR)/$(LIBNAME).lib | grep ' T ' | awk '{printf "%s,0x%x\n", $$3, strtonum("0x" $$1) - 0x400000}' >> $@
|
|
||||||
@echo "Generated symbol file: $@"
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libmath.o
|
rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libmath.o
|
||||||
|
|||||||
Binary file not shown.
@@ -11,15 +11,189 @@
|
|||||||
|
|
||||||
namespace libloader {
|
namespace libloader {
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
static uint64_t hexToUint64(const char* str);
|
|
||||||
|
|
||||||
// Table of currently loaded libraries
|
// Table of currently loaded libraries
|
||||||
static LibHandle g_loadedLibs[MaxLoadedLibs];
|
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) {
|
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 &&
|
if (g_loadedLibs[i].handle > 0 &&
|
||||||
strcmp(g_loadedLibs[i].path, path) == 0) {
|
strcmp(g_loadedLibs[i].path, path) == 0) {
|
||||||
return &g_loadedLibs[i];
|
return &g_loadedLibs[i];
|
||||||
@@ -28,102 +202,6 @@ static LibHandle* findLoadedLib(const char* path) {
|
|||||||
return nullptr;
|
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) {
|
LibHandle* dlopen(const char* path) {
|
||||||
if (!path) return nullptr;
|
if (!path) return nullptr;
|
||||||
|
|
||||||
@@ -134,33 +212,21 @@ LibHandle* dlopen(const char* path) {
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_libCount >= MaxLoadedLibs) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the library via syscall
|
// Load the library via syscall
|
||||||
int handle = montauk::load_lib(path);
|
int handle = montauk::load_lib(path);
|
||||||
if (handle <= 0) {
|
if (handle <= 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create symbol file path: append ".sym"
|
LibHandle* lib = findFreeLib();
|
||||||
char symPath[320];
|
if (!lib) {
|
||||||
size_t pathLen = strlen(path);
|
montauk::unload_lib(handle);
|
||||||
if (pathLen >= sizeof(symPath) - 5) return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(symPath, path);
|
|
||||||
strcpy(symPath + pathLen, ".sym");
|
|
||||||
|
|
||||||
LibHandle* lib = &g_loadedLibs[g_libCount++];
|
|
||||||
lib->handle = handle;
|
lib->handle = handle;
|
||||||
lib->base = 0; // Will be retrieved on first dlsym
|
lib->base = montauk::get_libbase(handle);
|
||||||
strcpy(lib->path, path);
|
strcpy(lib->path, path);
|
||||||
lib->symbolCount = 0;
|
|
||||||
lib->symbols = nullptr;
|
|
||||||
|
|
||||||
// Parse symbol file
|
|
||||||
parseSymbolFile(symPath, lib);
|
|
||||||
|
|
||||||
return lib;
|
return lib;
|
||||||
}
|
}
|
||||||
@@ -173,15 +239,9 @@ void* dlsym(LibHandle* lib, const char* symbolName) {
|
|||||||
lib->base = montauk::get_libbase(lib->handle);
|
lib->base = montauk::get_libbase(lib->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up in symbol table
|
uint64_t value = 0;
|
||||||
for (int i = 0; i < lib->symbolCount; i++) {
|
if (!lookupSymbolValue(lib->path, symbolName, &value)) return nullptr;
|
||||||
if (strcmp(lib->symbols[i].name, symbolName) == 0) {
|
return (void*)(lib->base + value);
|
||||||
uint64_t offset = lib->symbols[i].offset;
|
|
||||||
return (void*)montauk::dlsym(lib->handle, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr; // Symbol not found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dlclose(LibHandle* lib) {
|
int dlclose(LibHandle* lib) {
|
||||||
@@ -189,14 +249,8 @@ int dlclose(LibHandle* lib) {
|
|||||||
|
|
||||||
int result = montauk::unload_lib(lib->handle);
|
int result = montauk::unload_lib(lib->handle);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
// Free symbol table if allocated
|
|
||||||
if (lib->symbols) {
|
|
||||||
montauk::free((void*)lib->symbols);
|
|
||||||
lib->symbols = nullptr;
|
|
||||||
}
|
|
||||||
lib->handle = 0;
|
lib->handle = 0;
|
||||||
lib->base = 0;
|
lib->base = 0;
|
||||||
lib->symbolCount = 0;
|
|
||||||
lib->path[0] = '\0';
|
lib->path[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ LIBDIR := ../../bin
|
|||||||
|
|
||||||
all: $(BINDIR)/test_dl.elf
|
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)
|
@mkdir -p $(BINDIR)
|
||||||
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
|
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) main.o $(LIBDIR)/liblibloader.a ../../lib/libc/liblibc.a -o $@
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) main.o $(LIBDIR)/liblibloader.a ../../lib/libc/liblibc.a -o $@
|
||||||
|
|||||||
Reference in New Issue
Block a user