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]>
75 lines
2.9 KiB
Markdown
75 lines
2.9 KiB
Markdown
# 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:
|
|
|
|
```c
|
|
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.
|