README introduces the project and build workflow; CONTRIBUTING covers the stub convention and syscall wrapper process; docs/ explains the architecture and the kernel ABI surface. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
4.2 KiB
nulsl-libc
A very lightweight, from-scratch C17 libc for Linux, built for Null Linux 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).
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 ownmemcpy, 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 LSPcompile_commands.json)
Building
./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 for the design and roadmap,
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. Short version: C17, clang-format, benchmarks for anything performance-relevant, and per project policy — AI drafts, humans ship.