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
View File
+1
View File
@@ -15,3 +15,4 @@ programs/gui/icons/
CLAUDE.md
programs/lib/bearssl
ramdisk.tar
PLANS/
+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;
+29
View File
@@ -14,6 +14,35 @@ namespace Lib {
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);
}
+18 -3
View File
@@ -59,7 +59,7 @@ BINDIR := bin
PROGRAMS := $(notdir $(wildcard src/*))
# Programs with custom Makefiles (built separately).
CUSTOM_BUILDS := doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator desktop login shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs
CUSTOM_BUILDS := doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator desktop login shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs test_dl libloader
SYSTEM_PROGRAMS := $(filter-out $(CUSTOM_BUILDS),$(PROGRAMS))
# Build targets: system programs go to bin/os/, apps go to bin/apps/<name>/.
@@ -81,9 +81,9 @@ CA_CERTS := $(BINDIR)/etc/ca-certificates.crt
# Common shared assets (wallpapers, etc.)
COMMONKEEP := $(BINDIR)/common/.keep
.PHONY: all clean doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs icons fonts bearssl libc tls libjpeg libjpegwrite install-apps
.PHONY: all clean doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs icons fonts bearssl libc tls libjpeg libjpegwrite install-apps libloader libs test_dl
all: bearssl libc libjpeg libjpegwrite tls $(TARGETS) fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator doom rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs login desktop shell icons fonts install-apps $(MANDST) $(WWWDST) $(CA_CERTS) $(COMMONKEEP)
all: bearssl libc libjpeg libjpegwrite tls libloader libs $(TARGETS) fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator doom rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs login desktop shell icons fonts install-apps test_dl $(MANDST) $(WWWDST) $(CA_CERTS) $(COMMONKEEP)
# Build BearSSL static library (cross-compiled for freestanding x86_64).
BEARSSL_INCLUDES := -isystem $(shell cd .. && pwd)/kernel/freestnd-c-hdrs/x86_64/include -isystem $(abspath include/libc)
@@ -104,6 +104,14 @@ libc:
tls: bearssl libc
$(MAKE) -C lib/tls
# Build libloader static library (used by test_dl).
libloader: libc
$(MAKE) -C src/libloader
# Build shared libraries (libmath.lib etc).
libs: libloader
$(MAKE) -C libs/libmath
# Build fetch via its own Makefile (depends on bearssl, libc, and tls).
fetch: bearssl libc tls
$(MAKE) -C src/fetch
@@ -265,6 +273,10 @@ printctl: bearssl libc tls
dialogs: bearssl libc tls
$(MAKE) -C src/dialogs
# Build test_dl (dynamic loading test app, depends on libloader).
test_dl: libloader libs
$(MAKE) -C src/test_dl
# Install app bundles (manifests, icons, data files) into bin/apps/<name>/.
install-apps: doom rpgdemo paint spreadsheet wordprocessor weather wikipedia imageviewer fontpreview pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator screenshot texteditor mandelbrot printers dialogs
../scripts/install_apps.sh
@@ -334,3 +346,6 @@ clean:
$(MAKE) -C src/printd clean
$(MAKE) -C src/printctl clean
$(MAKE) -C src/dialogs clean
$(MAKE) -C src/libloader clean
$(MAKE) -C libs/libmath clean
$(MAKE) -C src/test_dl clean
+5
View File
@@ -144,6 +144,11 @@ namespace Montauk {
static constexpr uint64_t SYS_SURFACE_MAP = 112;
static constexpr uint64_t SYS_SURFACE_RESIZE = 113;
// Shared library syscalls
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;
+39
View File
@@ -0,0 +1,39 @@
/*
* libloader.h
* Dynamic library loading interface for MontaukOS
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
// Library handle - opaque to users
struct LibHandle {
int handle; // syscall handle (slot + 1)
uint64_t base; // base virtual address
char path[256];
int symbolCount;
struct SymbolEntry {
char name[64];
uint64_t offset;
}* symbols;
};
namespace libloader {
// Maximum libraries that can be loaded per process
static constexpr int MaxLoadedLibs = 8;
// Load a shared library.
// Returns a LibHandle* on success, or nullptr on failure.
LibHandle* dlopen(const char* path);
// Look up a symbol in a loaded library.
// Returns the function/data pointer, or nullptr if not found.
void* dlsym(LibHandle* lib, const char* symbolName);
// Unload a library.
// Returns 0 on success, -1 on failure.
int dlclose(LibHandle* lib);
} // namespace libloader
+11
View File
@@ -282,6 +282,17 @@ namespace montauk {
return (int)syscall2(Montauk::SYS_SURFACE_RESIZE, (uint64_t)handle, newSize);
}
// Shared library support
inline int load_lib(const char* path) {
return (int)syscall1(Montauk::SYS_LOAD_LIB, (uint64_t)path);
}
inline int unload_lib(int handle) {
return (int)syscall1(Montauk::SYS_UNLOAD_LIB, (uint64_t)handle);
}
inline void* dlsym(int handle, uint64_t symbolOffset) {
return (void*)syscall2(Montauk::SYS_DLSYM, (uint64_t)handle, symbolOffset);
}
// Process management
inline void waitpid(int pid) { syscall1(Montauk::SYS_WAITPID, (uint64_t)pid); }
+73
View File
@@ -0,0 +1,73 @@
# Makefile for libmath shared library
# Builds libmath.lib at fixed address 0x400000 (relocated at load time)
# Target architecture.
ARCH := x86_64
# Toolchain
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
CXX := $(TOOLCHAIN_PREFIX)g++
NM := $(TOOLCHAIN_PREFIX)nm
STRIP := $(TOOLCHAIN_PREFIX)strip
# Compiler flags: freestanding, no stdlib
override CXXFLAGS := \
-std=gnu++20 \
-g -O2 -pipe \
-Wall \
-Wextra \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-fno-rtti \
-fno-exceptions \
-ffunction-sections \
-fdata-sections \
-m64 \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=small \
-I ../../include \
-isystem ../../include/libc \
-isystem ../../include/montauk
# Linker flags: freestanding static ELF at fixed address
# Note: NOT using --gc-sections so all functions are kept
override LDFLAGS := \
-nostdlib \
-static \
-Wl,--build-id=none \
-Wl,-m,elf_x86_64 \
-T link.ld
# Output
OUTDIR := ../../bin/os
LIBNAME := libmath
LIBSRC := src/libmath.cpp
.PHONY: all clean
all: $(OUTDIR)/$(LIBNAME).lib $(OUTDIR)/$(LIBNAME).lib.sym
$(OUTDIR)/$(LIBNAME).lib: $(LIBSRC)
@mkdir -p $(OUTDIR)
$(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: $@"
clean:
rm -rf ../../bin/os/$(LIBNAME).lib ../../bin/os/$(LIBNAME).lib.sym src/libmath.o
+10
View File
@@ -0,0 +1,10 @@
SECTIONS
{
. = 0x400000;
.text : {
*(.text)
}
.eh_frame : {
*(.eh_frame)
}
}
+55
View File
@@ -0,0 +1,55 @@
/*
* libmath.cpp
* Sample shared math library for MontaukOS
* Copyright (c) 2026 Daniel Hammer
*/
extern "C" {
// Basic math functions
int math_add(int a, int b) {
return a + b;
}
int math_sub(int a, int b) {
return a - b;
}
int math_mul(int a, int b) {
return a * b;
}
int math_div(int a, int b) {
if (b == 0) return 0;
return a / b;
}
int math_mod(int a, int b) {
if (b == 0) return 0;
return a % b;
}
// Comparison
int math_max(int a, int b) {
return (a > b) ? a : b;
}
int math_min(int a, int b) {
return (a < b) ? a : b;
}
int math_abs(int a) {
return (a < 0) ? -a : a;
}
// Utility
int math_pow(int base, int exp) {
int result = 1;
for (int i = 0; i < exp; i++) {
result *= base;
}
return result;
}
} // extern "C"
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
# Makefile for libloader
ARCH := x86_64
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
CXX := $(TOOLCHAIN_PREFIX)g++
AR := $(TOOLCHAIN_PREFIX)ar
override CXXFLAGS := \
-std=gnu++20 \
-g -O2 -pipe \
-Wall \
-Wextra \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-fno-rtti \
-fno-exceptions \
-ffunction-sections \
-fdata-sections \
-m64 \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=small \
-I ../../include \
-isystem ../../include/libc \
-isystem ../../include/montauk \
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
BINDIR := ../../bin
.PHONY: all clean
all: $(BINDIR)/liblibloader.a
$(BINDIR)/liblibloader.a: libloader.cpp
@mkdir -p $(BINDIR)
$(CXX) $(CXXFLAGS) -c libloader.cpp -o libloader.o
$(AR) rcs $(BINDIR)/liblibloader.a libloader.o
rm -f libloader.o
clean:
rm -f $(BINDIR)/liblibloader.a
+207
View File
@@ -0,0 +1,207 @@
/*
* libloader.cpp
* Dynamic library loading implementation for MontaukOS
* Copyright (c) 2026 Daniel Hammer
*/
#include <libloader/libloader.h>
#include <montauk/syscall.h>
#include <libc/string.h>
#include <stdlib.h>
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;
static LibHandle* findLoadedLib(const char* path) {
for (int i = 0; i < g_libCount; i++) {
if (g_loadedLibs[i].handle > 0 &&
strcmp(g_loadedLibs[i].path, path) == 0) {
return &g_loadedLibs[i];
}
}
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;
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;
// Check if already loaded
LibHandle* existing = findLoadedLib(path);
if (existing) {
// Just return existing handle
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;
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
strcpy(lib->path, path);
lib->symbolCount = 0;
lib->symbols = nullptr;
// Parse symbol file
parseSymbolFile(symPath, lib);
return lib;
}
void* dlsym(LibHandle* lib, const char* symbolName) {
if (!lib || !symbolName) return nullptr;
// Get base address if we don't have it
if (lib->base == 0) {
// The base is stored in the handle - we need a syscall to get it
// For now, we'll use a workaround: calculate base from slot
// handle = slot + 1, so slot = handle - 1
// base = 0x60000000 + slot * 0x200000
int slot = lib->handle - 1;
lib->base = 0x60000000ULL + (uint64_t)slot * 0x200000ULL;
}
// 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
}
int dlclose(LibHandle* lib) {
if (!lib) return -1;
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';
}
return result;
}
} // namespace libloader
+60
View File
@@ -0,0 +1,60 @@
# Makefile for test_dl
ARCH := x86_64
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
CXX := $(TOOLCHAIN_PREFIX)g++
override CXXFLAGS := \
-std=gnu++20 \
-g -O2 -pipe \
-Wall \
-Wextra \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-fno-rtti \
-fno-exceptions \
-ffunction-sections \
-fdata-sections \
-m64 \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=small \
-I ../../include \
-isystem ../../include/libc \
-isystem ../../include/montauk \
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
override LDFLAGS := \
-nostdlib \
-static \
-Wl,--build-id=none \
-Wl,--gc-sections \
-Wl,-m,elf_x86_64 \
-z max-page-size=0x1000 \
-T ../../link.ld
BINDIR := ../../bin/os
LIBDIR := ../../bin
.PHONY: all clean
all: $(BINDIR)/test_dl.elf
$(BINDIR)/test_dl.elf: main.cpp
@mkdir -p $(BINDIR)
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) main.o $(LIBDIR)/liblibloader.a ../../lib/libc/liblibc.a -o $@
rm -f main.o
rm -f $(LIBDIR)/liblibloader.a
clean:
rm -f $(BINDIR)/test_dl.elf
+112
View File
@@ -0,0 +1,112 @@
/*
* test_dl.cpp
* Test application for dynamic library loading
* Copyright (c) 2026 Daniel Hammer
*/
#include <montauk/syscall.h>
#include <libloader/libloader.h>
// Forward declaration
static void print_int(int val);
extern "C" void _start() {
montauk::print("=== Dynamic Library Test ===\n\n");
// Test loading libmath
montauk::print("Loading libmath...\n");
LibHandle* lib = libloader::dlopen("0:/os/libmath.lib");
if (!lib) {
montauk::print("ERROR: Failed to load libmath.lib\n");
montauk::exit(1);
}
montauk::print("Library loaded successfully!\n\n");
// Test dlsym for various functions
montauk::print("Resolving symbols...\n");
// math_add
typedef int (*math_fn_t)(int, int);
math_fn_t math_add = (math_fn_t)libloader::dlsym(lib, "math_add");
if (!math_add) {
montauk::print("ERROR: Failed to resolve math_add\n");
montauk::exit(1);
}
montauk::print(" math_add: OK\n");
// math_mul
math_fn_t math_mul = (math_fn_t)libloader::dlsym(lib, "math_mul");
if (!math_mul) {
montauk::print("ERROR: Failed to resolve math_mul\n");
montauk::exit(1);
}
montauk::print(" math_mul: OK\n");
// math_max
typedef int (*math_max_t)(int, int);
math_max_t math_max = (math_max_t)libloader::dlsym(lib, "math_max");
if (!math_max) {
montauk::print("ERROR: Failed to resolve math_max\n");
montauk::exit(1);
}
montauk::print(" math_max: OK\n\n");
// Test the functions
montauk::print("Testing library functions:\n");
// Test add: 10 + 5 = 15
int a = 10, b = 5;
int result = math_add(a, b);
montauk::print(" math_add(10, 5) = ");
print_int(result);
montauk::print("\n");
// Test mul: 7 * 6 = 42
result = math_mul(7, 6);
montauk::print(" math_mul(7, 6) = ");
print_int(result);
montauk::print("\n");
// Test max: max(100, 200) = 200
result = math_max(100, 200);
montauk::print(" math_max(100, 200) = ");
print_int(result);
montauk::print("\n\n");
// Unload the library
montauk::print("Unloading library...\n");
libloader::dlclose(lib);
montauk::print("Library unloaded.\n");
montauk::print("\n=== All tests passed! ===\n");
montauk::exit(0);
}
// Simple integer to string conversion for printing
static void print_int(int val) {
char buf[16];
int idx = 0;
bool neg = false;
if (val < 0) {
neg = true;
val = -val;
}
if (val == 0) {
buf[idx++] = '0';
} else {
while (val > 0) {
buf[idx++] = '0' + (val % 10);
val /= 10;
}
}
if (neg) {
montauk::putchar('-');
}
for (int i = idx - 1; i >= 0; i--) {
montauk::putchar(buf[i]);
}
}