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?

  1. Linux guarantees syscall ABI stability - Unique among Unix-like OSes
  2. BSD/macOS syscalls can change - Between releases
  3. 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: