wip: shared library support
This commit is contained in:
@@ -15,3 +15,4 @@ programs/gui/icons/
|
||||
CLAUDE.md
|
||||
programs/lib/bearssl
|
||||
ramdisk.tar
|
||||
PLANS/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x400000;
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
.eh_frame : {
|
||||
*(.eh_frame)
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user