Raw Syscalls
Part of: Reef Language Reference Last reviewed on version: 0.8.0 Status: Implemented
Overview
Reef provides raw syscall wrappers for Linux that bypass libc entirely. This enables:
- True libc-free binaries
- Embedded and baremetal development
- Maximum control over system interaction
Key Features:
- Direct Linux syscalls via inline assembly
- AMD64 and ARM64 architectures
- No libc dependency
- Negative return values indicate
-errno
Platform Support:
| Platform | Module | Status |
|---|---|---|
| Linux x86-64 | sys.platform.raw.linux_amd64 |
Implemented |
| Linux ARM64 | sys.platform.raw.linux_arm64 |
Implemented |
| Linux RISC-V | Future | Planned |
Note: BSD and macOS always use libc wrappers. OpenBSD forbids raw syscalls in dynamic binaries.
Module: sys.platform.raw.linux_amd64
Process Control
fn sys_exit(status: int): int // Terminate process (never returns)
fn sys_getpid(): int // Get process ID
fn sys_getppid(): int // Get parent process ID
fn sys_getuid(): int // Get user ID
fn sys_getgid(): int // Get group ID
File I/O
fn sys_read(fd: int, buf: pointer, count: int): int
fn sys_write(fd: int, buf: pointer, count: int): int
fn sys_open(path: string, flags: int, mode: int): int
fn sys_close(fd: int): int
fn sys_lseek(fd: int, offset: int64, whence: int): int64
Memory
fn sys_brk(addr: pointer): pointer
fn sys_mmap(addr: pointer, length: int, prot: int, flags: int, fd: int, offset: int64): pointer
fn sys_munmap(addr: pointer, length: int): int
Directory
fn sys_getcwd(buf: pointer, size: int): int
fn sys_chdir(path: string): int
fn sys_mkdir(path: string, mode: int): int
fn sys_rmdir(path: string): int
fn sys_unlink(path: string): int
Time
fn sys_time(tloc: pointer): int64
fn sys_nanosleep(req: pointer, rem: pointer): int
Module: sys.platform.raw.linux_arm64
ARM64 uses *at variants for some syscalls (no standalone open, mkdir, etc.):
Process Control
fn sys_exit(status: int): int
fn sys_getpid(): int
fn sys_getppid(): int
fn sys_getuid(): int
fn sys_getgid(): int
File I/O
fn sys_read(fd: int, buf: pointer, count: int): int
fn sys_write(fd: int, buf: pointer, count: int): int
fn sys_openat(dirfd: int, path: string, flags: int, mode: int): int
fn sys_close(fd: int): int
fn sys_lseek(fd: int, offset: int64, whence: int): int64
Memory
fn sys_brk(addr: pointer): pointer
fn sys_mmap(addr: pointer, length: int, prot: int, flags: int, fd: int, offset: int64): pointer
fn sys_munmap(addr: pointer, length: int): int
Directory
fn sys_getcwd(buf: pointer, size: int): int
fn sys_chdir(path: string): int
fn sys_mkdirat(dirfd: int, path: string, mode: int): int
fn sys_unlinkat(dirfd: int, path: string, flags: int): int
Usage
Import
import sys.platform.raw.linux_amd64 as raw
Example: Hello World via Raw Syscall
import sys.platform.raw.linux_amd64 as raw
proc main()
let msg = "Hello from raw syscall!\n"
let len = 24
// sys_write's buf parameter is `pointer` — a `string` argument needs an
// explicit cast (strings are heap-allocated, so this cast doesn't need
// `unsafe`; see 100_UNSAFE.md's raw-address-cast section for the
// int/array <-> pointer casts that do).
// STDOUT_FILENO = 1
raw.sys_write(1, msg as pointer, len)
raw.sys_exit(0)
end main
Return Value Convention
All syscalls return:
- Success: Non-negative value (often 0, or bytes read/written, or fd)
- Error: Negative errno value (e.g., -2 for ENOENT)
let fd = raw.sys_open("/etc/hosts", 0, 0)
if fd < 0
// Error: fd contains -errno
println("Open failed")
end if
Syscall ABI Reference
Linux x86-64
| Register | Purpose |
|---|---|
| RAX | Syscall number |
| RDI | Argument 1 |
| RSI | Argument 2 |
| RDX | Argument 3 |
| R10 | Argument 4 |
| R8 | Argument 5 |
| R9 | Argument 6 |
| RAX | Return value |
Clobbered: RCX, R11
Linux ARM64
| Register | Purpose |
|---|---|
| X8 | Syscall number |
| X0 | Argument 1 |
| X1 | Argument 2 |
| X2 | Argument 3 |
| X3 | Argument 4 |
| X4 | Argument 5 |
| X5 | Argument 6 |
| X0 | Return value |
Common Syscall Numbers
Linux x86-64
| Syscall | Number |
|---|---|
| read | 0 |
| write | 1 |
| open | 2 |
| close | 3 |
| lseek | 8 |
| mmap | 9 |
| munmap | 11 |
| brk | 12 |
| getpid | 39 |
| exit | 60 |
Linux ARM64
| Syscall | Number |
|---|---|
| read | 63 |
| write | 64 |
| openat | 56 |
| close | 57 |
| lseek | 62 |
| mmap | 222 |
| munmap | 215 |
| brk | 214 |
| getpid | 172 |
| exit | 93 |
Design Rationale
Why Raw Syscalls for Linux Only?
- Linux guarantees syscall ABI stability - Unique among Unix-like OSes
- BSD/macOS syscalls can change - Between releases
- OpenBSD forbids raw syscalls - In dynamic binaries
When to Use Raw Syscalls
- Baremetal/embedded development
- Minimal binary size requirements
- Maximum control over system interaction
- Educational purposes
When to Use libc Wrappers
- Cross-platform portability
- Standard behavior guarantees
- Integration with C libraries
See Also
For higher-level systems programming (process management, file descriptors, signals, Unix sockets, event loops), see:
- 115_SYSTEMS_PROGRAMMING.md —
sys.process,sys.fd,sys.signal,sys.poll,net.unixmodules (v0.4.0)