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
+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]);
}
}