diff --git a/kernel/src/Api/LibSyscall.hpp b/kernel/src/Api/LibSyscall.hpp index 6ed4cef..6a380bb 100644 --- a/kernel/src/Api/LibSyscall.hpp +++ b/kernel/src/Api/LibSyscall.hpp @@ -23,11 +23,15 @@ namespace Montauk { // Library entry tracking struct LibEntry { char path[128]; - uint64_t baseAddr; + uint64_t loadBias; uint32_t refcount; bool inUse; }; + static uint64_t GetLibSlotBase(int libSlot) { + return Sched::LIB_BASE + (uint64_t(libSlot) * Sched::LIB_MAX_SIZE); + } + // Per-process library table static LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess]; @@ -37,7 +41,7 @@ namespace Montauk { for (int i = 0; i < MaxLibsPerProcess; i++) { g_libTable[slot][i].inUse = false; 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'; } } @@ -77,8 +81,8 @@ namespace Montauk { } // Load the library into the process's address space - uint64_t libBase = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot); - if (libBase == 0) { + uint64_t loadBias = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot); + if (loadBias == 0) { Kt::KernelLogStream(Kt::ERROR, "Lib") << "Failed to load library: " << path; return 0; } @@ -86,10 +90,10 @@ namespace Montauk { // Store library info g_libTable[slot][libSlot].inUse = true; 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)); - 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) } @@ -108,7 +112,7 @@ namespace Montauk { g_libTable[slot][libSlot].refcount--; if (g_libTable[slot][libSlot].refcount == 0) { // 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; auto* proc = Sched::GetCurrentProcessPtr(); @@ -124,7 +128,7 @@ namespace Montauk { } 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'; } @@ -133,7 +137,7 @@ namespace Montauk { // Resolve a symbol in a loaded library. // 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. static uint64_t Sys_DLSym(uint64_t handle, uint64_t symbolOffset) { int slot = GetCurrentSlot(); @@ -144,11 +148,11 @@ namespace Montauk { if (!g_libTable[slot][libSlot].inUse) return 0; - uint64_t libBase = g_libTable[slot][libSlot].baseAddr; - return libBase + symbolOffset; + uint64_t loadBias = g_libTable[slot][libSlot].loadBias; + 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) { int slot = GetCurrentSlot(); if (slot < 0) return 0; @@ -158,7 +162,7 @@ namespace Montauk { 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 @@ -171,7 +175,7 @@ namespace Montauk { // Unmap all libraries for this process for (int i = 0; i < MaxLibsPerProcess; i++) { 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; 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].refcount = 0; - g_libTable[slot][i].baseAddr = 0; + g_libTable[slot][i].loadBias = 0; } } } diff --git a/kernel/src/Sched/ElfLoader.cpp b/kernel/src/Sched/ElfLoader.cpp index 7f6506d..91185aa 100644 --- a/kernel/src/Sched/ElfLoader.cpp +++ b/kernel/src/Sched/ElfLoader.cpp @@ -16,6 +16,368 @@ 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(info); + } + + static constexpr uint32_t Elf64RelocSym(uint64_t info) { + return static_cast(info >> 32); + } + + static uint64_t AddSigned(uint64_t base, int64_t addend) { + if (addend >= 0) { + return base + static_cast(addend); + } + + return base - static_cast(-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 = ""; + 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) { // Check ELF magic: 0x7f 'E' 'L' 'F' if (hdr->e_ident[0] != 0x7f || @@ -177,43 +539,48 @@ namespace Sched { Elf64Header* hdr = (Elf64Header*)fileData; - // Validate ELF header - 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"; + if (!ValidateLibElfHeader(hdr) || !ValidateProgramHeaderTable(hdr, fileSize)) { Memory::g_heap->Free(fileData); return 0; } - if (hdr->e_ident[4] != 2) { - Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF"; + uint64_t slotBase = LIB_BASE + (uint64_t(slot) * LIB_MAX_SIZE); + 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); + return 0; + } + + uint64_t segStart = phdr->p_vaddr & ~0xFFFULL; + 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); return 0; } - if (hdr->e_ident[5] != 1) { - Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian"; + if (maxLoadEnd - minLoadStart > LIB_MAX_SIZE) { + Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library image exceeds slot size"; Memory::g_heap->Free(fileData); return 0; } - // Libraries are built as ET_EXEC at a fixed base (0x60000000) - if (hdr->e_type != ET_EXEC) { - Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library not ET_EXEC (type=" << (uint64_t)hdr->e_type << ")"; - Memory::g_heap->Free(fileData); - return 0; - } - - if (hdr->e_machine != EM_X86_64) { - 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); + uint64_t loadBias = slotBase - minLoadStart; // Process program headers for (uint16_t i = 0; i < hdr->e_phnum; i++) { @@ -227,9 +594,7 @@ namespace Sched { continue; } - // The library is linked at 0x400000. We need to relocate it to libBase. - // target_vaddr = libBase + (file_vaddr - 0x400000) - uint64_t targetSegStart = libBase + (phdr->p_vaddr - 0x400000ULL); + uint64_t targetSegStart = loadBias + phdr->p_vaddr; uint64_t targetSegFileEnd = targetSegStart + phdr->p_filesz; // only file data uint64_t targetSegMemEnd = targetSegStart + phdr->p_memsz; // includes bss @@ -251,7 +616,7 @@ namespace Sched { uint64_t pageEnd = pageStart + 0x1000; // 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"; Memory::g_heap->Free(fileData); 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); - // Return the library base address for this slot - return libBase; + return loadBias; } } diff --git a/kernel/src/Sched/ElfLoader.hpp b/kernel/src/Sched/ElfLoader.hpp index 5af35dc..3b2dc92 100644 --- a/kernel/src/Sched/ElfLoader.hpp +++ b/kernel/src/Sched/ElfLoader.hpp @@ -38,7 +38,9 @@ namespace Sched { }; 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_DYN = 3; static constexpr uint16_t EM_X86_64 = 62; // Fixed base address for shared libraries. @@ -51,8 +53,7 @@ namespace Sched { uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys); // Load a shared library into a fixed address range. - // Loads at LIB_BASE for the given slot. - // Returns the library base virtual address on success, or 0 on failure. + // Returns the relocation base / load bias on success, or 0 on failure. uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot); } diff --git a/programs/include/libloader/libloader.h b/programs/include/libloader/libloader.h index 9b095e1..ba19d52 100644 --- a/programs/include/libloader/libloader.h +++ b/programs/include/libloader/libloader.h @@ -10,13 +10,8 @@ // Library handle - opaque to users struct LibHandle { 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]; - int symbolCount; - struct SymbolEntry { - char name[64]; - uint64_t offset; - }* symbols; }; namespace libloader { diff --git a/programs/libs/libhello/Makefile b/programs/libs/libhello/Makefile index 5eada70..627ec58 100644 --- a/programs/libs/libhello/Makefile +++ b/programs/libs/libhello/Makefile @@ -1,5 +1,5 @@ # 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. ARCH := x86_64 @@ -7,12 +7,12 @@ ARCH := x86_64 # Toolchain TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf- CXX := $(TOOLCHAIN_PREFIX)g++ +LD := $(TOOLCHAIN_PREFIX)ld NM := $(TOOLCHAIN_PREFIX)nm STRIP := $(TOOLCHAIN_PREFIX)strip # Compiler flags: freestanding, no stdlib -# Build as PIE so library-internal data references remain valid after the -# kernel maps the image into a per-process library slot. +# Build as PIC so the final ET_DYN image can be relocated by the kernel loader. override CXXFLAGS := \ -std=gnu++20 \ -g -O2 -pipe \ @@ -22,7 +22,7 @@ override CXXFLAGS := \ -ffreestanding \ -fno-stack-protector \ -fno-stack-check \ - -fPIE \ + -fPIC \ -fno-rtti \ -fno-exceptions \ -ffunction-sections \ @@ -40,14 +40,13 @@ override CXXFLAGS := \ -isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \ -isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include -# Linker flags: freestanding static ELF at fixed address -# Note: NOT using --gc-sections so all functions are kept +# Linker flags for a bare-metal ELF shared object. override LDFLAGS := \ - -nostdlib \ - -static \ - -Wl,--build-id=none \ - -Wl,-m,elf_x86_64 \ - -T link.ld + -shared \ + --build-id=none \ + --hash-style=sysv \ + -m elf_x86_64 \ + -z max-page-size=0x1000 # Output OUTDIR := ../../bin/os @@ -56,7 +55,7 @@ LIBSRC := src/libhello.cpp .PHONY: all clean -all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib.sym +all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR): 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 $@ $(OUTDIR)/$(LIBNAME).lib: src/libhello.o src/stb_truetype_impl.o - $(CXX) $(CXXFLAGS) $(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: $@" + rm -f $(OUTDIR)/$(LIBNAME).lib.sym + $(LD) $(LDFLAGS) src/libhello.o src/stb_truetype_impl.o -o $@ clean: rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libhello.o src/stb_truetype_impl.o diff --git a/programs/libs/libhello/src/libhello.o b/programs/libs/libhello/src/libhello.o index c5158f9..69459bd 100644 Binary files a/programs/libs/libhello/src/libhello.o and b/programs/libs/libhello/src/libhello.o differ diff --git a/programs/libs/libhello/src/stb_truetype_impl.o b/programs/libs/libhello/src/stb_truetype_impl.o index 4d66d60..8d9ebd4 100644 Binary files a/programs/libs/libhello/src/stb_truetype_impl.o and b/programs/libs/libhello/src/stb_truetype_impl.o differ diff --git a/programs/libs/libmath/Makefile b/programs/libs/libmath/Makefile index 42c5160..c007cf6 100644 --- a/programs/libs/libmath/Makefile +++ b/programs/libs/libmath/Makefile @@ -1,5 +1,5 @@ # 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. ARCH := x86_64 @@ -7,12 +7,12 @@ ARCH := x86_64 # Toolchain TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf- CXX := $(TOOLCHAIN_PREFIX)g++ +LD := $(TOOLCHAIN_PREFIX)ld NM := $(TOOLCHAIN_PREFIX)nm STRIP := $(TOOLCHAIN_PREFIX)strip # Compiler flags: freestanding, no stdlib -# Build as PIE so library code remains valid when loaded into a slot-specific -# base address by the kernel loader. +# Build as PIC so the final ET_DYN image can be relocated by the kernel loader. override CXXFLAGS := \ -std=gnu++20 \ -g -O2 -pipe \ @@ -22,7 +22,7 @@ override CXXFLAGS := \ -ffreestanding \ -fno-stack-protector \ -fno-stack-check \ - -fPIE \ + -fPIC \ -fno-rtti \ -fno-exceptions \ -ffunction-sections \ @@ -39,14 +39,13 @@ override CXXFLAGS := \ -isystem ../../include/libc \ -isystem ../../include/montauk -# Linker flags: freestanding static ELF at fixed address -# Note: NOT using --gc-sections so all functions are kept +# Linker flags for a bare-metal ELF shared object. override LDFLAGS := \ - -nostdlib \ - -static \ - -Wl,--build-id=none \ - -Wl,-m,elf_x86_64 \ - -T link.ld + -shared \ + --build-id=none \ + --hash-style=sysv \ + -m elf_x86_64 \ + -z max-page-size=0x1000 # Output OUTDIR := ../../bin/os @@ -55,21 +54,13 @@ LIBSRC := src/libmath.cpp .PHONY: all clean -all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib.sym +all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib: $(LIBSRC) Makefile @mkdir -p $(OUTDIR) + rm -f $(OUTDIR)/$(LIBNAME).lib.sym $(CXX) $(CXXFLAGS) -c $(LIBSRC) -o src/libmath.o - $(CXX) $(CXXFLAGS) $(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: $@" + $(LD) $(LDFLAGS) src/libmath.o -o $@ clean: rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libmath.o diff --git a/programs/libs/libmath/src/libmath.o b/programs/libs/libmath/src/libmath.o index ab3ae21..14ea1d3 100644 Binary files a/programs/libs/libmath/src/libmath.o and b/programs/libs/libmath/src/libmath.o differ diff --git a/programs/src/libloader/libloader.cpp b/programs/src/libloader/libloader.cpp index 65c1dce..75b71d0 100644 --- a/programs/src/libloader/libloader.cpp +++ b/programs/src/libloader/libloader.cpp @@ -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'; } diff --git a/programs/src/test_dl/Makefile b/programs/src/test_dl/Makefile index 4516c43..d508aa8 100644 --- a/programs/src/test_dl/Makefile +++ b/programs/src/test_dl/Makefile @@ -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 $@