wip: shared library support

This commit is contained in:
2026-04-05 15:14:07 +02:00
parent d6c0470c62
commit 01c242d4de
20 changed files with 1026 additions and 4 deletions
+192
View File
@@ -0,0 +1,192 @@
/*
* LibSyscall.hpp
* SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Sched/ElfLoader.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Terminal/Terminal.hpp>
#include <Libraries/Memory.hpp>
#include <Libraries/String.hpp>
namespace Montauk {
// Maximum libraries per process
static constexpr int MaxLibsPerProcess = 8;
static constexpr int MaxProcesses = 64;
// Library entry tracking
struct LibEntry {
char path[128];
uint64_t baseAddr;
uint32_t refcount;
bool inUse;
};
// Per-process library table
static LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess];
// Initialize library table for a process slot
static void InitLibTable(int slot) {
if (slot < 0 || slot >= MaxProcesses) return;
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].path[0] = '\0';
}
}
// Load a shared library into the current process's address space.
// Returns a library handle (slot index + 1) on success, or 0 on failure.
// The library is loaded at a fixed address based on the slot.
static uint64_t Sys_LoadLib(const char* path) {
int slot = GetCurrentSlot();
if (slot < 0) return 0;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
// Validate path
if (path == nullptr) return 0;
size_t pathLen = Lib::strlen(path);
if (pathLen == 0 || pathLen >= sizeof(LibEntry::path)) return 0;
// Find a free slot or reuse an existing slot with the same path
int libSlot = -1;
for (int i = 0; i < MaxLibsPerProcess; i++) {
if (g_libTable[slot][i].inUse) {
if (Lib::strncmp(g_libTable[slot][i].path, path, sizeof(LibEntry::path)) == 0) {
// Same library already loaded - just increment refcount
g_libTable[slot][i].refcount++;
return (uint64_t)(i + 1); // Handle = slot + 1 (0 = invalid)
}
} else if (libSlot < 0) {
libSlot = i;
}
}
if (libSlot < 0) {
Kt::KernelLogStream(Kt::ERROR, "Lib") << "No free library slots";
return 0;
}
// Load the library into the process's address space
uint64_t libBase = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot);
if (libBase == 0) {
Kt::KernelLogStream(Kt::ERROR, "Lib") << "Failed to load library: " << path;
return 0;
}
// Store library info
g_libTable[slot][libSlot].inUse = true;
g_libTable[slot][libSlot].refcount = 1;
g_libTable[slot][libSlot].baseAddr = libBase;
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;
return (uint64_t)(libSlot + 1); // Handle = slot + 1 (0 = invalid)
}
// Unload a shared library.
// Decrements refcount; actually unmaps when refcount reaches 0.
static int Sys_UnloadLib(uint64_t handle) {
int slot = GetCurrentSlot();
if (slot < 0) return -1;
int libSlot = (int)handle - 1;
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return -1;
if (!g_libTable[slot][libSlot].inUse) return -1;
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 libEnd = libBase + Sched::LIB_MAX_SIZE;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc != nullptr) {
// Unmap all pages in the library region
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, va);
if (physAddr != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, va);
}
}
}
g_libTable[slot][libSlot].inUse = false;
g_libTable[slot][libSlot].baseAddr = 0;
g_libTable[slot][libSlot].path[0] = '\0';
}
return 0;
}
// Resolve a symbol in a loaded library.
// handle = library handle from LoadLib
// symbolOffset = offset of the symbol from the library's base address
// 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();
if (slot < 0) return 0;
int libSlot = (int)handle - 1;
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return 0;
if (!g_libTable[slot][libSlot].inUse) return 0;
uint64_t libBase = g_libTable[slot][libSlot].baseAddr;
return libBase + symbolOffset;
}
// Get library base address (for userspace symbol resolution)
static uint64_t Sys_GetLibBase(uint64_t handle) {
int slot = GetCurrentSlot();
if (slot < 0) return 0;
int libSlot = (int)handle - 1;
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return 0;
if (!g_libTable[slot][libSlot].inUse) return 0;
return g_libTable[slot][libSlot].baseAddr;
}
// Cleanup library table for a process slot
static void CleanupLibTable(int slot) {
if (slot < 0 || slot >= MaxProcesses) return;
auto* proc = Sched::GetProcessSlot(slot);
if (proc == nullptr) return;
// 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 libEnd = libBase + Sched::LIB_MAX_SIZE;
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, va);
if (physAddr != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, va);
}
}
g_libTable[slot][i].inUse = false;
g_libTable[slot][i].refcount = 0;
g_libTable[slot][i].baseAddr = 0;
}
}
}
}
+11
View File
@@ -33,6 +33,7 @@
#include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL
#include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO
#include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE
#include "LibSyscall.hpp" // SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM
// Assembly entry point
extern "C" void SyscallEntry();
@@ -65,6 +66,8 @@ namespace Montauk {
auto* proc = Sched::GetCurrentProcessPtr();
if (slot >= 0 && proc)
CleanupHeapForSlot(slot, proc->pml4Phys);
if (slot >= 0)
CleanupLibTable(slot);
Sys_Exit((int)frame->arg1);
return 0;
}
@@ -274,6 +277,7 @@ namespace Montauk {
auto* slot0 = Sched::GetProcessSlot(0);
int targetSlot = (int)(target - slot0);
CleanupHeapForSlot(targetSlot, target->pml4Phys);
CleanupLibTable(targetSlot);
}
return (int64_t)Sys_Kill((int)frame->arg1);
}
@@ -406,6 +410,13 @@ namespace Montauk {
return (int64_t)Sys_SurfaceMap((int)frame->arg1);
case SYS_SURFACE_RESIZE:
return Sys_SurfaceResize((int)frame->arg1, frame->arg2);
case SYS_LOAD_LIB:
if (!ValidUserPtr(frame->arg1)) return 0;
return (int64_t)Sys_LoadLib((const char*)frame->arg1);
case SYS_UNLOAD_LIB:
return Sys_UnloadLib(frame->arg1);
case SYS_DLSYM:
return (int64_t)Sys_DLSym(frame->arg1, frame->arg2);
default:
return -1;
}
+5
View File
@@ -206,6 +206,11 @@ namespace Montauk {
static constexpr uint64_t SYS_SURFACE_MAP = 112;
static constexpr uint64_t SYS_SURFACE_RESIZE = 113;
/* LibSyscall.hpp */
static constexpr uint64_t SYS_LOAD_LIB = 114;
static constexpr uint64_t SYS_UNLOAD_LIB = 115;
static constexpr uint64_t SYS_DLSYM = 116;
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1;
static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2;
+30 -1
View File
@@ -8,12 +8,41 @@ namespace Lib {
while (*string != '\0') {
string++;
c++;
c++;
}
return c;
}
inline int strcmp(const char *a, const char *b) {
while (*a && *b) {
if (*a != *b) {
return (unsigned char)*a - (unsigned char)*b;
}
a++;
b++;
}
return (unsigned char)*a - (unsigned char)*b;
}
inline int strncmp(const char *a, const char *b, size_t max_len) {
for (size_t i = 0; i < max_len; i++) {
if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i];
if (a[i] == '\0') return 0;
}
return 0;
}
inline char *strncpy(char *dest, const char *src, size_t max_len) {
size_t i = 0;
while (i < max_len - 1 && src[i]) {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
return dest;
}
char *int2basestr(int num, size_t radix);
char *u64_2_basestr(uint64_t num, size_t radix);
char *uint2basestr(uint32_t num, size_t radix);
+138
View File
@@ -149,4 +149,142 @@ namespace Sched {
return entryPoint;
}
uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot) {
Fs::Vfs::BackendFile file = {-1, -1};
if (Fs::Vfs::OpenBackendFile(vfsPath, file) < 0) {
return 0;
}
uint64_t fileSize = Fs::Vfs::GetBackendFileSize(file);
if (fileSize < sizeof(Elf64Header)) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "File too small (" << fileSize << " bytes)";
Fs::Vfs::CloseBackendFile(file);
return 0;
}
// Read entire file into a heap buffer
uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize);
if (fileData == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file";
Fs::Vfs::CloseBackendFile(file);
return 0;
}
Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize);
Fs::Vfs::CloseBackendFile(file);
asm volatile("" ::: "memory");
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";
Memory::g_heap->Free(fileData);
return 0;
}
if (hdr->e_ident[4] != 2) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF";
Memory::g_heap->Free(fileData);
return 0;
}
if (hdr->e_ident[5] != 1) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian";
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);
// Process program headers
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
if (phdr->p_type != PT_LOAD) {
continue;
}
if (phdr->p_memsz == 0) {
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 targetSegFileEnd = targetSegStart + phdr->p_filesz; // only file data
uint64_t targetSegMemEnd = targetSegStart + phdr->p_memsz; // includes bss
// Align segment start down, segment end up to page boundaries
uint64_t pageSegStart = targetSegStart & ~0xFFFULL;
uint64_t pageSegEnd = (targetSegMemEnd + 0xFFFULL) & ~0xFFFULL;
uint64_t numPages = (pageSegEnd - pageSegStart) / 0x1000;
for (uint64_t p = 0; p < numPages; p++) {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages";
Memory::g_heap->Free(fileData);
return 0;
}
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
uint64_t pageStart = pageSegStart + p * 0x1000;
uint64_t pageEnd = pageStart + 0x1000;
// Check that we're within the library region
if (pageStart < libBase || pageEnd > libBase + LIB_MAX_SIZE) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside library region";
Memory::g_heap->Free(fileData);
return 0;
}
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, pageStart)) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
Memory::g_heap->Free(fileData);
return 0;
}
// Calculate overlap between this page and the file-data portion of the segment
uint64_t copyStart = (pageStart > targetSegStart) ? pageStart : targetSegStart;
uint64_t copyEnd = (pageEnd < targetSegFileEnd) ? pageEnd : targetSegFileEnd;
if (copyStart < copyEnd) {
// Source: file data at (copyStart - targetSegStart) offset from p_offset
uint64_t srcOffset = phdr->p_offset + (copyStart - targetSegStart);
// Dest: page at (copyStart - pageStart) offset
uint64_t dstOffset = copyStart - pageStart;
uint64_t copySize = copyEnd - copyStart;
uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + dstOffset;
uint8_t* src = fileData + srcOffset;
memcpy(dst, src, copySize);
}
}
}
Memory::g_heap->Free(fileData);
// Return the library base address for this slot
return libBase;
}
}
+9
View File
@@ -41,9 +41,18 @@ namespace Sched {
static constexpr uint16_t ET_EXEC = 2;
static constexpr uint16_t EM_X86_64 = 62;
// Fixed base address for shared libraries.
static constexpr uint64_t LIB_BASE = 0x60000000ULL;
static constexpr uint64_t LIB_MAX_SIZE = 0x200000ULL; // 2MB per library
// Load an ELF64 binary into a per-process address space.
// pml4Phys = physical address of the process's PML4.
// Returns the entry point address, or 0 on failure.
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.
uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot);
}