Skip to content
CryoCryo home
StdlibSystem

sys

sys is the one layer of the standard library that talks to the operating system directly. Everything above it — String, the collections, the I/O buffering, the upper net layers — is pure Cryo. Below it sits each platform's native system interface, selected per target.

public module sys::syscall;
FileWhat it holdsImport
syscallSyscall numbers, sys_callN, the typed sys_* wrappers, and the Win32 / NT surfacesimport std::sys::syscall;

The seam functions below live at the root of the module: import std::sys;.

This is a low-level seam, not the everyday I/O surface. Almost everything you want is one layer up in io, fs, or ffi::libc.

Backends

Each seam function is declared twice, gated with ![target(...)]:

  • Linux — the raw kernel syscall, issued through inline assembly with no libc involvement. On Linux the kernel syscall interface is the stable ABI; glibc is a wrapper over it.
  • Everything else — a thin pass-through to ffi::libc. On Windows the eventual native surface is kernel32 and ntdll, since there the DLLs are the stable ABI.

The error model

Every fallible sys function returns a signed integer.

  • A negative result is -errno — the negated POSIX error number, so -EINTR is -4 and -EBADF is -9.
  • A non-negative result is the success value: a byte count, a file descriptor, or 0.

This is normalized at the seam, independent of the backend. A raw Linux syscall already returns -errno in the result register and is passed through as-is; the libc arms read errno after a -1 return and negate it.

Nothing here retries or builds a rich error. A caller that wants Result and IoError and EINTR retry builds that on top — that policy lives above sys, in io::fd.

The seam surface

FunctionReturns
read(fd, buf, count)Bytes read (0 is EOF), or -errno.
write(fd, buf, count)Bytes written — short writes are normal.
open(path, flags, mode)The new fd, or -errno.
close(fd)0, or -errno.
mmap(addr, length, prot, flags, fd, offset)The mapped address as a non-negative i64.
munmap(addr, length)0, or -errno.
exit(code)Does not return.

A few details that bite if you don't know them:

read and write are single raw calls with no EINTR retry — loop on -EINTR yourself if you need it.

open forces O_BINARY on the libc arms. The seam presents POSIX byte semantics, and MSVCRT would otherwise default the descriptor to text mode, where CRLF translation makes byte counts and contents stop matching what was passed. On POSIX targets O_BINARY is 0, so it costs nothing.

mmap is a Unix-only seam surface; Windows callers use VirtualAlloc through sys::syscall directly, which is per-DLL rather than per-libc so there is nothing to wean off. x86-64 user addresses fit comfortably in the positive i64 range, so the sign carries the success/failure distinction losslessly.

exit is exit_group(2) natively, ending every thread in the process — Syscall::exit alone would end only the calling thread. It runs no libc atexit handlers, and unflushed buffered writers lose their contents, so flush first.

function read(fd: i32, buf: u8*, count: u64) -> i64;

read(2): read up to count bytes from fd into buf. Returns the byte count read (0 == EOF), or -errno on failure. A single raw call — no EINTR retry; a caller that wants retry loops on -EINTR itself.

function write(fd: i32, buf: u8*, count: u64) -> i64;

write(2): write up to count bytes from buf to fd. Returns the byte count written (short writes are normal), or -errno on failure.

function open(path: u8*, flags: i32, mode: u32) -> i64;

open(2): open path (a NUL-terminated C string) with flags and, when creating, mode. Returns the new file descriptor (>= 0), or -errno. The O_* flag values pass through unchanged: on Linux, glibc's O_* constants are the kernel's own bit values.

function close(fd: i32) -> i64;

close(2): release fd. Returns 0 on success, or -errno.

function mmap(addr: u8*, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> i64;

mmap(2): map length bytes of memory. Returns the mapped address as a non-negative i64, or -errno on failure. Unlike the fd calls, mapping is a unix-only seam surface (Windows callers use VirtualAlloc through sys::syscall directly — per-DLL, not per-libc, so there is nothing to wean). x86-64 user addresses fit comfortably in the positive i64 range, so the sign carries the success/failure distinction losslessly.

function munmap(addr: u8*, length: u64) -> i64;

munmap(2): unmap [addr, addr+length). Returns 0 on success, or -errno.

function exit(code: i32) -> void;

_exit(2)-style process termination with status code. Does not return. Unflushed buffered writers lose their contents; flush before calling.