/* * crt1.c * Minimal C runtime startup for MontaukOS userspace programs * Copyright (c) 2026 Daniel Hammer * * This startup shim is intentionally small: it fetches the raw * command-line buffer from the kernel, tokenizes it into argc/argv, * calls main(), then exits with main()'s return code. */ static inline long _sys1(long nr, long a1) { long ret; __asm__ volatile( "mov %[a1], %%rdi\n\t" "syscall" : "=a"(ret) : "a"(nr), [a1] "r"(a1) : "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory"); return ret; } static inline long _sys2(long nr, long a1, long a2) { long ret; __asm__ volatile( "mov %[a1], %%rdi\n\t" "mov %[a2], %%rsi\n\t" "syscall" : "=a"(ret) : "a"(nr), [a1] "r"(a1), [a2] "r"(a2) : "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory"); return ret; } #define SYS_EXIT 0 #define SYS_GETARGS 25 extern int main(int argc, char** argv); /* Global constructor/destructor tables. The linker provides these boundary symbols whenever .preinit_array/.init_array/.fini_array sections exist; the weak references make them all optional so plain C programs keep working unchanged. Frame registration for C++ exceptions (crtbegin's frame_dummy) also arrives via .init_array. */ extern void (*__preinit_array_start[])(void) __attribute__((weak)); extern void (*__preinit_array_end[])(void) __attribute__((weak)); extern void (*__init_array_start[])(void) __attribute__((weak)); extern void (*__init_array_end[])(void) __attribute__((weak)); extern void (*__fini_array_start[])(void) __attribute__((weak)); extern void (*__fini_array_end[])(void) __attribute__((weak)); extern int atexit(void (*fn)(void)); extern void exit(int status); static void _run_init_arrays(void) { for (void (**f)(void) = __preinit_array_start; f < __preinit_array_end; f++) { (*f)(); } for (void (**f)(void) = __init_array_start; f < __init_array_end; f++) { (*f)(); } } /* Destructors run in reverse registration order, hooked through atexit so they also run when the program calls exit() directly. */ static void _run_fini_array(void) { for (void (**f)(void) = __fini_array_end; f > __fini_array_start; f--) { (*(f - 1))(); } } void _start(void) { /* Static: 4 KiB args + 256 argv slots would crowd a 32 KiB stack. */ static char argbuf[4096]; static char* argv[256]; int len = (int)_sys2(SYS_GETARGS, (long)argbuf, (long)sizeof(argbuf)); int argc = 0; argv[argc++] = (char*)"prog"; if (len > 0) { if (len > (int)sizeof(argbuf) - 1) { len = (int)sizeof(argbuf) - 1; } argbuf[len] = '\0'; char* p = argbuf; while (*p != '\0' && argc < 255) { while (*p == ' ') { p++; } if (*p == '\0') { break; } argv[argc++] = p; while (*p != '\0' && *p != ' ') { p++; } if (*p != '\0') { *p++ = '\0'; } } } argv[argc] = 0; if (__fini_array_start != __fini_array_end) { atexit(_run_fini_array); } _run_init_arrays(); /* exit() (not raw SYS_EXIT) so the atexit chain - including the fini-array walker registered above - runs on return from main. */ exit(main(argc, argv)); __builtin_unreachable(); }