# nulsl-libc A very lightweight, from-scratch C17 libc for Linux, built for [Null Linux](https://github.com/The-Null-Linux-Project) and its 32 MB RAM target. **Status: skeleton. The structure is real, the code is honest stubs.** The string core, the raw syscall layer, the entry point, and process teardown work end to end; everything else declares its intent and returns `ENOSYS` until it is implemented (see the [roadmap](docs/architecture.md#roadmap)). ## Why another libc? Null Linux guideline #6: *"If it's small, if it's lean, you have ZERO reason to link with libC at ALL. Linux specific syscalls can do you well."* - **Static-only, by design.** Everything links with `-nostdlib -static`. No dynamic linker is loaded, relocated, or kept in memory for the lifetime of a process — that memory belongs to the GUI. - **The kernel is the API.** One variadic `syscall()` is the only piece of code that touches the kernel ABI; `read`, `write`, `_exit`, and friends are thin wrappers around it. - **Freestanding C17.** `-ffreestanding -fno-builtin`: the compiler never injects its own `memcpy`, so what you read is what runs. - **No dependencies.** No libtool, no glibc, no kernel UAPI headers. ## Requirements - Linux on x86_64 - A C17 compiler (GCC ≥ 8 or Clang ≥ 6) - autoconf ≥ 2.71, automake ≥ 1.16 - `bear` (optional, for LSP `compile_commands.json`) ## Building ```sh ./autogen.sh # or just run make — it regenerates when needed make release # -> bin/release/, CFLAGS='-O2' make debug # -> bin/debug/, CFLAGS='-O0 -g' make check # smoke test (fully static, no glibc, no ld.so) make bench # benchmark the release build ``` Release is **always** `-O2`, per Null Linux guideline #4. `make bench` and `make check` run against the release build. The repository root is never configured in-tree; `configure.ac` refuses it so the committed driver `Makefile` (which dispatches into `bin/release` and `bin/debug`) cannot be clobbered. `make distclean` removes both build directories. ## What works today | Area | Real | Stub (ENOSYS) | |--------------|-----------------------------------------|-----------------------------| | string | strlen, strcmp, strncmp, strcpy, | — | | | strncpy, memcpy, memmove, memset, | | | | memcmp | | | syscall | syscall() (x86_64) | other architectures | | unistd | read, write, close, getpid, _exit | unlink | | stdio | puts, putchar, fflush (trivially) | printf, fopen, fclose, | | | | fread, fwrite | | stdlib | exit, abort, atoi | malloc, calloc, realloc, | | | | free, strtol | | crt | crt0.o `_start` (x86_64) | other architectures | Stubs follow one convention: standard signature, documented error return, `errno = ENOSYS`, and a `/* TODO */` comment naming what they need. See [docs/architecture.md](docs/architecture.md) for the design and roadmap, [docs/syscalls.md](docs/syscalls.md) for the kernel interface. ## Repository layout ``` Makefile driver: make release / make debug / make bench / ... configure.ac autotools (C17 enforced, static-only) autogen.sh autoreconf -i include/ public headers — declarations only src/ implementations; src/crt/crt0.S is the entry point benchmarks/ make bench; must stay lean tests/ make check; smoke test links -nostdlib -static bin/release/ out-of-tree release build (-O2) bin/debug/ out-of-tree debug build (-O0 -g) docs/ architecture + syscall documentation ``` ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). Short version: C17, clang-format, benchmarks for anything performance-relevant, and per project policy — *AI drafts, humans ship.*