Files
nulsl-libc/docs/syscalls.md
T
huntedbytheirsandSisyphus 5487bbc808 Add documentation
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]>
2026-08-30 04:09:49 -04:00

2.9 KiB

Syscalls

This project's first principle (project guideline #6): the kernel is the API. This document is the map of every syscall nulsl-libc touches, how errors travel, and how to add the next one.

The one and only ABI surface

src/syscall.c — the only file that executes the syscall instruction.

long syscall(long number, ...);
  • Arguments 1-6 map to the platform's syscall argument registers.
  • On success the kernel return value is returned as-is.
  • On error the Linux kernel returns -errno (range -1..-4095). syscall() translates it: errno = -ret; return -1; — the standard libc convention.
  • Numbers live in include/sys/syscall.h as SYS_* macros, #ifndef-guarded so kernel UAPI headers can coexist.

The syscalls we use

x86_64 Linux ABI:

Number Name Used by Status
0 read read() wrapped
1 write write(), puts(), putchar() wrapped
2 open — constant
3 close close() wrapped
39 getpid getpid(), bench_syscall wrapped
60 exit _exit(), exit(), abort() wrapped
87 unlink — (stub uses errno = ENOSYS) constant
228 clock_gettime benchmarks only constant

"Wrapped" means there is a public function in src/ that calls it. "Constant" means the number is defined in the header but nothing wraps it yet (benchmarks call it directly through syscall()).

Adding a new syscall wrapper

  1. Add the number to include/sys/syscall.h (guarded, with the ABI it belongs to in a comment).

  2. Add the public declaration to the right header (include/unistd.h for POSIX stuff, include/stdio.h for stdio, ...).

  3. Implement it in the matching src/ file as a thin wrapper:

    ssize_t write(int fd, const void *buf, size_t count)
    {
        return (ssize_t)syscall(SYS_write, fd, buf, count);
    }
    

    If the syscall is not implemented yet, follow the stub convention instead (return the documented error value, errno = ENOSYS, /* TODO */ comment naming what it needs).

  4. Update this table.

  5. Add or extend a smoke test in tests/ — if nothing exercises it, it does not exist.

Porting to a new architecture

  • src/syscall.c — add the register mapping for the new ABI (#error otherwise).
  • src/crt/crt0.S — add _start for the new ABI.
  • include/sys/syscall.h — the numbers are per-architecture; split the table or move it to src/arch/ once a second target exists.
  • configure.ac — extend the host_cpu case.

The project targets Linux on x86_64 today; everything above is written so a second port is a contained, reviewable change.