feat(rumpk): Pure Zig libc stubs - Freestanding Doctrine
THE GHOST IN THE MACHINE IS EXORCISED ====================================== Rumpk now builds with ZERO C source files. All libc functions are implemented in pure Zig. WHAT CHANGED ------------ - NEW: hal/stubs.zig - Pure Zig libc implementation - memcpy, memset, memmove, memcmp - strlen, strcmp, strcpy - malloc, free, realloc, calloc (bump allocator) - printf, puts, putchar (route to UART) - exit, abort (halt CPU) - signal, raise (no-op stubs) - REMOVED: core/cstubs.c dependency from build - C code is now only Nim's generated IR - UPDATED: kernel.nim - Removed malloc/free/realloc exports - Now imports from Zig stubs - UPDATED: build.sh - Compiles hal/stubs.zig separately - Links stubs.o with hal.o and nimcache/*.o DOCTRINE DOCUMENT ----------------- - .agents/steering/FREESTANDING-DOCTRINE.md - Codifies the 'Pure Zig ABI' principle - Documents build requirements - Lists all exported symbols VERIFICATION ------------ $ file build/rumpk.elf ELF 64-bit LSB executable, ARM aarch64, statically linked $ qemu-system-aarch64 -M virt -kernel build/rumpk.elf [Rumpk L0] Zig HAL Initialized [Rumpk L1] Nim Kernel Alive! [Rumpk L1] The Rubicon is crossed. [Rumpk L1] Zig + Nim = Sovereign Metal. This proves: - POSIX is optional - GCC is optional - glibc/musl is optional - We are the standard library now
This commit is contained in:
parent
3b755cac06
commit
5c3a8e3713
|
|
@ -1,8 +1,8 @@
|
|||
0
|
||||
12268 9459532 1767076443800031133 c56105a5a3a93a0d4d451c619dc2b349 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c
|
||||
10486 9461201 1767076985098986152 3747702445e0bcda6de1816ec7a47cdd 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c
|
||||
19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h
|
||||
639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h
|
||||
268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h
|
||||
1172 78925833 1761046366000000000 69b529ccb10bbb5d826c563cf9b929c1 1 include/stdbool.h
|
||||
31054 78925836 1761046366000000000 1df950c62cbc96dd5d9790733bbe6016 1 include/stdint.h
|
||||
155 9459777 1767076495338437553 9cc523d7a8a3a0bbc7c7af0fabeafc0b 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stdbool.h
|
||||
924 9459799 1767076530759032485 73bc6834aef9958f6652470b63d7814b 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stdint.h
|
||||
499 9459330 1767076360432003062 357ccd6329b0128cce0610c1443c600d 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/string.h
|
||||
|
|
|
|||
Binary file not shown.
49
build.sh
49
build.sh
|
|
@ -1,6 +1,8 @@
|
|||
#!/bin/bash
|
||||
# Rumpk Build Script
|
||||
# Builds Zig L0 + Nim L1 into a single ELF
|
||||
# Rumpk Build Script - Freestanding Doctrine
|
||||
# Pure Zig L0 + Nim L1, no C stubs, no glibc
|
||||
#
|
||||
# We are the standard library now.
|
||||
|
||||
set -e
|
||||
|
||||
|
|
@ -8,7 +10,8 @@ RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||
BUILD_DIR="$RUMPK_DIR/build"
|
||||
|
||||
echo "╔═══════════════════════════════════════╗"
|
||||
echo "║ RUMPK BUILD SYSTEM v0.1 ║"
|
||||
echo "║ RUMPK FREESTANDING BUILD v0.2 ║"
|
||||
echo "║ No glibc. Pure Sovereignty. ║"
|
||||
echo "╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
|
|
@ -17,25 +20,31 @@ mkdir -p "$BUILD_DIR"
|
|||
mkdir -p "$BUILD_DIR/nimcache"
|
||||
|
||||
# =========================================================
|
||||
# Step 1: Compile Zig L0 (HAL)
|
||||
# Step 1: Compile Zig L0 (HAL + Stubs)
|
||||
# =========================================================
|
||||
echo "[1/3] Compiling Zig L0 (HAL)..."
|
||||
echo "[1/4] Compiling Zig L0 (HAL + libc stubs)..."
|
||||
|
||||
# Compile main.zig
|
||||
zig build-obj \
|
||||
"$RUMPK_DIR/hal/main.zig" \
|
||||
-target aarch64-freestanding-none \
|
||||
-O ReleaseSmall \
|
||||
-femit-bin="$BUILD_DIR/hal.o"
|
||||
|
||||
# Compile stubs.zig (our libc replacement)
|
||||
zig build-obj \
|
||||
"$RUMPK_DIR/hal/stubs.zig" \
|
||||
-target aarch64-freestanding-none \
|
||||
-O ReleaseSmall \
|
||||
-femit-bin="$BUILD_DIR/stubs.o"
|
||||
|
||||
echo " → $BUILD_DIR/hal.o"
|
||||
echo " → $BUILD_DIR/stubs.o"
|
||||
|
||||
# =========================================================
|
||||
# Step 2: Compile Nim L1 (Kernel)
|
||||
# =========================================================
|
||||
echo "[2/3] Compiling Nim L1 (Kernel)..."
|
||||
|
||||
# Note: This requires careful Nim configuration for freestanding
|
||||
# For now, we'll try direct compilation with clang
|
||||
echo "[2/4] Compiling Nim L1 (Kernel)..."
|
||||
|
||||
nim c \
|
||||
--cpu:arm64 \
|
||||
|
|
@ -61,17 +70,6 @@ echo " → $BUILD_DIR/nimcache/*.c"
|
|||
# =========================================================
|
||||
echo "[3/4] Compiling Nim C files..."
|
||||
|
||||
# First compile cstubs.c
|
||||
echo " Compiling cstubs.c..."
|
||||
zig cc \
|
||||
-target aarch64-freestanding-none \
|
||||
-ffreestanding \
|
||||
-fno-builtin \
|
||||
-I"$RUMPK_DIR/core/include" \
|
||||
-c "$RUMPK_DIR/core/cstubs.c" \
|
||||
-o "$BUILD_DIR/cstubs.o"
|
||||
|
||||
# Now compile Nim C files
|
||||
for cfile in "$BUILD_DIR/nimcache"/*.c; do
|
||||
ofile="${cfile%.c}.o"
|
||||
zig cc \
|
||||
|
|
@ -89,9 +87,9 @@ done
|
|||
echo " → $BUILD_DIR/nimcache/*.o"
|
||||
|
||||
# =========================================================
|
||||
# Step 4: Link Everything
|
||||
# Step 4: Link Everything (NO LIBC!)
|
||||
# =========================================================
|
||||
echo "[4/4] Linking..."
|
||||
echo "[4/4] Linking (freestanding, no libc)..."
|
||||
|
||||
# Collect all Nim object files
|
||||
NIM_OBJS=$(find "$BUILD_DIR/nimcache" -name "*.o" 2>/dev/null | tr '\n' ' ')
|
||||
|
|
@ -106,7 +104,7 @@ zig cc \
|
|||
-nostdlib \
|
||||
-T "$RUMPK_DIR/boot/linker.ld" \
|
||||
"$BUILD_DIR/hal.o" \
|
||||
"$BUILD_DIR/cstubs.o" \
|
||||
"$BUILD_DIR/stubs.o" \
|
||||
$NIM_OBJS \
|
||||
-o "$BUILD_DIR/rumpk.elf"
|
||||
|
||||
|
|
@ -116,7 +114,10 @@ echo " → $BUILD_DIR/rumpk.elf"
|
|||
# Done
|
||||
# =========================================================
|
||||
echo ""
|
||||
echo "✅ Build complete!"
|
||||
echo "✅ Freestanding build complete!"
|
||||
echo ""
|
||||
echo "Verification:"
|
||||
echo " file $BUILD_DIR/rumpk.elf"
|
||||
echo ""
|
||||
echo "Run with:"
|
||||
echo " qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -kernel $BUILD_DIR/rumpk.elf"
|
||||
|
|
|
|||
|
|
@ -42,36 +42,14 @@ proc nimPanic(msg: cstring) {.exportc: "panic", cdecl, noreturn.} =
|
|||
rumpk_halt()
|
||||
|
||||
# =========================================================
|
||||
# Memory Allocator Stubs (Required for ARC on freestanding)
|
||||
# Memory Allocator - Provided by Zig L0 (hal/stubs.zig)
|
||||
# =========================================================
|
||||
|
||||
# Static heap for bare metal (64KB)
|
||||
var heapBase {.exportc.}: array[64 * 1024, byte]
|
||||
var heapOffset {.exportc.}: csize_t = 0
|
||||
|
||||
proc allocImpl(size: csize_t): pointer {.exportc: "malloc", cdecl.} =
|
||||
if heapOffset + size > csize_t(heapBase.len):
|
||||
return nil
|
||||
result = addr heapBase[heapOffset]
|
||||
heapOffset += size
|
||||
|
||||
proc deallocImpl(p: pointer) {.exportc: "free", cdecl.} =
|
||||
# Bump allocator - no dealloc
|
||||
discard
|
||||
|
||||
proc reallocImpl(p: pointer, size: csize_t): pointer {.exportc: "realloc", cdecl.} =
|
||||
# Simple realloc - just allocate new (wasteful but works)
|
||||
result = allocImpl(size)
|
||||
|
||||
# Nim's internal allocation hooks
|
||||
proc rawAlloc(size: Natural): pointer {.exportc: "rawAlloc", cdecl.} =
|
||||
result = allocImpl(csize_t(size))
|
||||
|
||||
proc rawDealloc(p: pointer) {.exportc: "rawDealloc", cdecl.} =
|
||||
deallocImpl(p)
|
||||
|
||||
proc rawRealloc(p: pointer, size: Natural): pointer {.exportc: "rawRealloc", cdecl.} =
|
||||
result = reallocImpl(p, csize_t(size))
|
||||
# Zig exports: malloc, free, realloc, calloc
|
||||
# We just import them for any explicit usage
|
||||
proc malloc(size: csize_t): pointer {.importc, cdecl.}
|
||||
proc free(p: pointer) {.importc, cdecl.}
|
||||
proc realloc(p: pointer, size: csize_t): pointer {.importc, cdecl.}
|
||||
|
||||
# =========================================================
|
||||
# Kernel Main Entry
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
// VOXIS FORGE // RUMPK L0
|
||||
// libc_stubs.zig
|
||||
// We are the standard library now.
|
||||
//
|
||||
// These C ABI functions are exported so Nim's generated C code
|
||||
// can link against them. No glibc. No musl. Pure sovereignty.
|
||||
|
||||
// =========================================================
|
||||
// Memory Operations (Nim needs these)
|
||||
// =========================================================
|
||||
|
||||
export fn memcpy(dest: [*]u8, src: [*]const u8, len: usize) [*]u8 {
|
||||
var i: usize = 0;
|
||||
while (i < len) : (i += 1) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
export fn memset(dest: [*]u8, val: c_int, len: usize) [*]u8 {
|
||||
const v: u8 = @intCast(val & 0xFF);
|
||||
var i: usize = 0;
|
||||
while (i < len) : (i += 1) {
|
||||
dest[i] = v;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
export fn memmove(dest: [*]u8, src: [*]const u8, len: usize) [*]u8 {
|
||||
if (@intFromPtr(dest) < @intFromPtr(src)) {
|
||||
return memcpy(dest, src, len);
|
||||
}
|
||||
// Copy backwards for overlapping regions
|
||||
var i: usize = len;
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
dest[i] = src[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
export fn memcmp(s1: [*]const u8, s2: [*]const u8, len: usize) c_int {
|
||||
var i: usize = 0;
|
||||
while (i < len) : (i += 1) {
|
||||
if (s1[i] != s2[i]) {
|
||||
return if (s1[i] < s2[i]) @as(c_int, -1) else @as(c_int, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// String Operations
|
||||
// =========================================================
|
||||
|
||||
export fn strlen(s: [*]const u8) usize {
|
||||
var len: usize = 0;
|
||||
while (s[len] != 0) : (len += 1) {}
|
||||
return len;
|
||||
}
|
||||
|
||||
export fn strcpy(dest: [*]u8, src: [*]const u8) [*]u8 {
|
||||
var i: usize = 0;
|
||||
while (src[i] != 0) : (i += 1) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
dest[i] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
export fn strcmp(s1: [*]const u8, s2: [*]const u8) c_int {
|
||||
var i: usize = 0;
|
||||
while (s1[i] != 0 and s1[i] == s2[i]) : (i += 1) {}
|
||||
if (s1[i] == s2[i]) return 0;
|
||||
return if (s1[i] < s2[i]) @as(c_int, -1) else @as(c_int, 1);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Heap Stubs (Bump Allocator)
|
||||
// =========================================================
|
||||
|
||||
var heap_base: [64 * 1024]u8 = undefined;
|
||||
var heap_offset: usize = 0;
|
||||
|
||||
export fn malloc(size: usize) ?*anyopaque {
|
||||
if (heap_offset + size > heap_base.len) {
|
||||
return null;
|
||||
}
|
||||
const ptr = &heap_base[heap_offset];
|
||||
heap_offset += size;
|
||||
// Align to 8 bytes
|
||||
heap_offset = (heap_offset + 7) & ~@as(usize, 7);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
export fn free(ptr: ?*anyopaque) void {
|
||||
// Bump allocator - no deallocation
|
||||
_ = ptr;
|
||||
}
|
||||
|
||||
export fn realloc(ptr: ?*anyopaque, size: usize) ?*anyopaque {
|
||||
// Simple realloc - just allocate new (wasteful but works for bootstrap)
|
||||
_ = ptr;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
||||
const total = nmemb * size;
|
||||
const ptr = malloc(total);
|
||||
if (ptr) |p| {
|
||||
_ = memset(@ptrCast(p), 0, total);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Stdio Stubs (Route to UART)
|
||||
// =========================================================
|
||||
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
export fn puts(s: [*]const u8) c_int {
|
||||
const len = strlen(s);
|
||||
uart.write_bytes(s[0..len]);
|
||||
uart.putc('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
export fn putchar(c: c_int) c_int {
|
||||
uart.putc(@intCast(c & 0xFF));
|
||||
return c;
|
||||
}
|
||||
|
||||
export fn printf(fmt: [*]const u8, ...) c_int {
|
||||
// Minimal printf - just output format string
|
||||
const len = strlen(fmt);
|
||||
uart.write_bytes(fmt[0..len]);
|
||||
return @intCast(len);
|
||||
}
|
||||
|
||||
export fn fprintf(stream: ?*anyopaque, fmt: [*]const u8, ...) c_int {
|
||||
_ = stream;
|
||||
return printf(fmt);
|
||||
}
|
||||
|
||||
export fn fflush(stream: ?*anyopaque) c_int {
|
||||
_ = stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export fn fwrite(ptr: [*]const u8, size: usize, nmemb: usize, stream: ?*anyopaque) usize {
|
||||
_ = stream;
|
||||
uart.write_bytes(ptr[0 .. size * nmemb]);
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Signal Stubs (No signals in freestanding)
|
||||
// =========================================================
|
||||
|
||||
export fn signal(signum: c_int, handler: ?*anyopaque) ?*anyopaque {
|
||||
_ = signum;
|
||||
_ = handler;
|
||||
return null;
|
||||
}
|
||||
|
||||
export fn raise(sig: c_int) c_int {
|
||||
_ = sig;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Exit Stubs
|
||||
// =========================================================
|
||||
|
||||
fn halt() noreturn {
|
||||
while (true) {
|
||||
asm volatile ("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
export fn exit(status: c_int) noreturn {
|
||||
_ = status;
|
||||
uart.puts("[RUMPK] exit() called - halt\n");
|
||||
halt();
|
||||
}
|
||||
|
||||
export fn abort() noreturn {
|
||||
uart.puts("[RUMPK] abort() called - halt\n");
|
||||
halt();
|
||||
}
|
||||
|
||||
export fn _Exit(status: c_int) noreturn {
|
||||
exit(status);
|
||||
}
|
||||
Loading…
Reference in New Issue