There is no libc. Every program is compiled with this small built-in prelude of thin syscall wrappers, plus the raw __sys intrinsic underneath. That is the entire standard library - anything else you write yourself.
writeint write(int fd, char* buf, int n)
Write n bytes from buf to file descriptor fd.
| param | meaning |
|---|---|
fd | destination descriptor: 1 = stdout, 2 = stderr, 3+ = an open file |
buf | source buffer |
n | number of bytes to write |
Returns the number of bytes written (always n) syscall 1
readint read(int fd, char* buf, int n)
Read up to n bytes from file descriptor fd into buf. On the terminal (fd 0) a line-mode read stops at the newline; in raw mode (see set_raw) it returns keystrokes as they arrive. Blocks until input is available; returns 0 at end of input (Ctrl-D).
| param | meaning |
|---|---|
fd | source descriptor: 0 = stdin, 3+ = an open file |
buf | destination buffer |
n | maximum bytes to read |
Returns the number of bytes read, or 0 at EOF syscall 0
openint open(char* p, int fl)
Open the file at path p and return a descriptor for it.
| param | meaning |
|---|---|
p | absolute or working-directory-relative path |
fl | flags: 0 = read-only, O_WRONLY 1, O_RDWR 2, O_CREAT 0x40, O_TRUNC 0x200, O_APPEND 0x400 (or them together) |
Returns a descriptor (>= 3) on success, or a negative errno (-2 no such file, -13 permission denied, -21 is a directory) syscall 2
closeint close(int fd)
Close descriptor fd, releasing it for reuse.
| param | meaning |
|---|---|
fd | the descriptor to close |
Returns 0 syscall 3
set_rawint set_raw(int on)
Toggle raw terminal mode. In raw mode read(0, ...) returns individual keystrokes instead of buffering a whole line, which is what full-screen programs like vi need. A child process inherits line mode on exit.
| param | meaning |
|---|---|
on | nonzero to enable raw mode, 0 to return to line mode |
Returns 0 syscall 26
read_nbint read_nb(int fd, char* buf, int len)
The non-blocking counterpart to read: pull up to len bytes that are already waiting into buf, without ever blocking. Reads the console stdin regardless of fd. Pair it with msleep to drive a responsive game or event loop that never stalls waiting for a key.
| param | meaning |
|---|---|
fd | ignored (the console is always the source) |
buf | destination buffer |
len | maximum bytes to read |
Returns the number of bytes read, or 0 if none are ready syscall 78
ioctlint ioctl(int fd, int req, char* arg)
Device control. On the console tty this queries or sets terminal parameters: req selects the operation (e.g. TIOCGWINSZ 0x5413 fills a winsize struct, TCGETS 0x5401 / TCSETS 0x5402 read/write a termios struct) and arg points to the operation's in/out buffer.
| param | meaning |
|---|---|
fd | the descriptor (0/1/2 = the console tty) |
req | the request code |
arg | pointer to the request's argument buffer |
Returns the driver-defined result, or -25 (ENOTTY) on a non-device syscall 72
msleepint msleep(int ms)
Sleep for ms milliseconds of real time, then return. Millisecond pacing for animation and real-time loops; combine with read_nb for a loop that ticks on a fixed clock while still reacting to input.
| param | meaning |
|---|---|
ms | milliseconds to sleep |
Returns 0 once the interval has elapsed syscall 73
getrandomint getrandom(int* p, int n)
Fill p with n random bytes from the userland PRNG. Suitable for games and general randomness; it is not the cryptographic source behind ASLR.
| param | meaning |
|---|---|
p | buffer to fill |
n | number of bytes to write |
Returns 0 syscall 28
statint stat(char* p, char* buf)
Fill buf with metadata about path p: five little-endian int fields laid out as mode@0, uid@4, size@8, type@12 (1 = file, 2 = directory), gid@16.
| param | meaning |
|---|---|
p | path to inspect |
buf | a 20-byte buffer for the five fields |
Returns 0 on success, or -2 if p does not exist syscall 5
listdirint listdir(char* p, char* b, int n)
List directory p, writing its entry names newline-separated into b.
| param | meaning |
|---|---|
p | directory path |
b | destination buffer |
n | capacity of b in bytes |
Returns the number of bytes written, or -2 if p is not a directory syscall 7
getcwdint getcwd(char* b, int n)
Copy the current working directory into b.
| param | meaning |
|---|---|
b | destination buffer |
n | capacity of b in bytes |
Returns the length copied syscall 9
chdirint chdir(char* p)
Change the current working directory to p.
| param | meaning |
|---|---|
p | target directory |
Returns 0 on success, or a negative errno syscall 25
unlinkint unlink(char* p)
Remove the file at path p.
| param | meaning |
|---|---|
p | file to remove |
Returns 0 on success, or a negative errno syscall 15
mkdirint mkdir(char* p, int m)
Create directory p with permission bits m.
| param | meaning |
|---|---|
p | new directory path |
m | octal permission bits, e.g. 0755 |
Returns 0 on success, or a negative errno syscall 16
rmdirint rmdir(char* p)
Remove the empty directory at path p.
| param | meaning |
|---|---|
p | directory to remove |
Returns 0 on success, or a negative errno syscall 17
renameint rename(char* a, char* b)
Rename path a to path b.
| param | meaning |
|---|---|
a | existing path |
b | new path |
Returns 0 on success, or a negative errno syscall 18
chmodint chmod(char* p, int m)
Set the permission bits of path p to m.
| param | meaning |
|---|---|
p | path to modify |
m | octal permission bits, e.g. 0644 |
Returns 0 on success, or a negative errno syscall 19
chownint chown(char* p, int u)
Set the owner user id of path p to u.
| param | meaning |
|---|---|
p | path to modify |
u | new owner uid |
Returns 0 on success, or a negative errno syscall 23
srunint srun(int c, int v)
Run one already-expanded command as a foreground child and wait for it: a fused fork + exec + wait that returns the child's exit code. v is an array of char* argument addresses (v[0] is the path); set-user-ID / set-group-ID bits on the target are honored. It does no I/O plumbing of its own - real redirection uses fork + pipe + dup2 (all in this prelude); cc uses srun to invoke the assembler.
| param | meaning |
|---|---|
c | argument count (argc) |
v | argument vector: the address of an array of char* (argv) |
Returns the child's exit code syscall 24
exitvoid exit(int c)
Terminate the current process with status c. Does not return.
| param | meaning |
|---|---|
c | exit status |
syscall 11
getuidint getuid()
Return the real user id of the current process.
Returns the real uid syscall 13
geteuidint geteuid()
Return the effective user id, which differs from the real uid while a setuid program is running (this is what the guestbook exploit escalates).
Returns the effective uid syscall 14
strlenint strlen(char* s)
Return the length of the NUL-terminated string s. This is the only prelude function that is pure TBC rather than a syscall wrapper.
| param | meaning |
|---|---|
s | a NUL-terminated string |
Returns the number of bytes before the terminating NUL
strcmpint strcmp(char* a, char* b)
Compare two NUL-terminated strings. Returns 0 if equal, otherwise the signed difference of the first differing byte (compared as unsigned char).
| param | meaning |
|---|---|
a | first string |
b | second string |
Returns 0 if equal, else first differing byte difference
strncmpint strncmp(char* a, char* b, int n)
Like strcmp but compares at most n bytes.
| param | meaning |
|---|---|
a | first string |
b | second string |
n | maximum number of bytes to compare |
Returns 0 if equal within n bytes, else first differing byte difference
strcpychar* strcpy(char* d, char* s)
Copy the NUL-terminated string s (including the terminator) into d. Returns d.
| param | meaning |
|---|---|
d | destination buffer (must be large enough) |
s | source string |
Returns d
strcatchar* strcat(char* d, char* s)
Append the NUL-terminated string s to the end of d. Returns d.
| param | meaning |
|---|---|
d | destination string (must have room for the result) |
s | string to append |
Returns d
strchrchar* strchr(char* s, int c)
Find the first occurrence of byte c in the NUL-terminated string s. Returns a pointer to it, or 0 if not found; searching for 0 returns the terminator.
| param | meaning |
|---|---|
s | string to search |
c | byte to find (low 8 bits used) |
Returns pointer to the first match, or 0
memcpychar* memcpy(char* d, char* s, int n)
Copy n bytes from s to d (the regions must not overlap). Returns d.
| param | meaning |
|---|---|
d | destination buffer |
s | source buffer |
n | number of bytes to copy |
Returns d
memsetchar* memset(char* d, int c, int n)
Fill the first n bytes of d with the byte value c. Returns d.
| param | meaning |
|---|---|
d | destination buffer |
c | fill byte (low 8 bits used) |
n | number of bytes to set |
Returns d
memcmpint memcmp(char* a, char* b, int n)
Compare the first n bytes of a and b. Returns 0 if equal, otherwise the signed difference of the first differing byte (compared as unsigned char).
| param | meaning |
|---|---|
a | first buffer |
b | second buffer |
n | number of bytes to compare |
Returns 0 if equal, else first differing byte difference
sbrkchar* sbrk(int d)
Grow (or query) the program break, the top of the heap. sbrk(0) returns the current break without moving it; a positive delta grows the heap upward and returns the *previous* break (the base of the freshly reserved region). The heap grows until it would collide with the stack, then returns (char*)-1.
| param | meaning |
|---|---|
d | bytes to move the break by (0 to query) |
Returns the previous break, or (char*)-1 on failure
mallocchar* malloc(int n)
Allocate n bytes from the heap and return a pointer to them, or 0 if the heap cannot grow far enough. Blocks carry a small header, are reused once freed, and adjacent free blocks are coalesced; the heap is grown on demand via sbrk only when no existing free block fits.
| param | meaning |
|---|---|
n | number of bytes to allocate |
Returns pointer to the allocation, or 0 on failure
freevoid free(char* p)
Release a malloc'd allocation so its space can be reused. The block is marked free and merged with any adjacent free blocks on the next malloc. Passing 0 is a no-op.
| param | meaning |
|---|---|
p | a pointer previously returned by malloc, or 0 |
mmapint mmap(int a, int l, int p, int f, int d, int o)
Map len bytes into the address space and return the mapping's address. prot combines 1 read, 2 write, 4 exec; flags combines 0x10 MAP_FIXED and 0x20 MAP_ANON. An anonymous map (MAP_ANON, fd ignored) is demand-zero; a file-backed map fills pages from fd at off and needs the same read permission as opening the file. Pass addr only with MAP_FIXED.
| param | meaning |
|---|---|
a | requested address (only with MAP_FIXED, else 0) |
l | bytes to map (rounded up to whole pages) |
p | protection bits: 1 read | 2 write | 4 exec |
f | flags: 0x10 MAP_FIXED | 0x20 MAP_ANON |
d | file descriptor for a file-backed map (else -1) |
o | byte offset into the file |
Returns the mapping address, or a negative errno
munmapint munmap(int a, int l)
Release a whole prior mmap mapping, freeing its pages. a and l must match the original mapping.
| param | meaning |
|---|---|
a | the mapping's base address |
l | the mapping's length |
Returns 0, or -EINVAL if it does not match a mapping
mprotectint mprotect(int a, int l, int p)
Change the protection of a whole mmap mapping (see mmap for the protection bits). A write to a page mapped without write permission faults.
| param | meaning |
|---|---|
a | the mapping's base address |
l | the mapping's length |
p | new protection bits |
Returns 0, or a negative errno
forkint fork()
Create a child process: a near-duplicate of the caller with its own copy of memory. Open file descriptors (and pipe ends) are inherited. fork returns twice - the new child's PID in the parent and 0 in the child - so the two branches can follow different paths.
Returns child PID to the parent, 0 to the child, or -EAGAIN if the table is full
getpidint getpid()
The caller's own process id.
Returns the current PID
getppidint getppid()
The parent's process id (the process that fork()ed this one, or 1 once an orphan has been reparented to init).
Returns the parent PID
waitpidint waitpid(int pid, int st, int opt)
Wait for a child to change state and optionally collect its exit status. pid -1 waits for any child; a positive pid waits for that specific child. Pass the address of an int in st to receive the status word (or 0 to ignore it).
| param | meaning |
|---|---|
pid | child to wait for (-1 = any child) |
st | address of an int to receive the status, or 0 |
opt | option bits (1 = WNOHANG: don't block; 4 = WUNTRACED: also report stops) |
Returns the reaped child's PID, 0 (WNOHANG and none ready), or -ECHILD
pipeint pipe(int fds)
Create a unidirectional pipe. Fills the two-int array fds with a read end in fds[0] and a write end in fds[1]; bytes written to the write end are read back, in order, from the read end. With fork() + dup2() this wires one process's output into another's input.
| param | meaning |
|---|---|
fds | a two-int array that receives { read_fd, write_fd } |
Returns 0 on success, or a negative error
dupint dup(int fd)
Duplicate an open file descriptor onto the lowest free descriptor (>= 3). The copy shares the same open-file description - and thus the seek offset - as fd.
| param | meaning |
|---|---|
fd | the descriptor to duplicate |
Returns the new descriptor, or a negative error
dup2int dup2(int of, int nf)
Make nf refer to whatever of refers to, closing nf first if it was open. The classic way to point a child's stdin/stdout/stderr (fd 0/1/2) at a pipe or file just before exec.
| param | meaning |
|---|---|
of | the source descriptor |
nf | the descriptor to overwrite (commonly 0, 1, or 2) |
Returns nf on success, or a negative error
execveint execve(char* p)
Replace the current process image with the program at path p, keeping the caller's credentials and open descriptors. On success it does not return - the new program takes over the process.
| param | meaning |
|---|---|
p | path to the executable |
Returns does not return on success; a negative error otherwise
execvpint execvp(char* p, int v, int n)
Replace the current process image with program p, resolving a bare name against PATH and passing an explicit argument vector. Honors the target's set-user-ID / set-group-ID bits. On success it does not return.
| param | meaning |
|---|---|
p | path or command name to execute |
v | argv: an array of n string pointers (v[0] is the program name) |
n | the number of arguments in v |
Returns does not return on success; a negative error otherwise
killint kill(int pid, int sig)
Send signal sig to a process or process group. A positive pid targets that process, pid 0 the caller's group, and a negative pid the group -pid. Delivery obeys the usual permission rule (same user, or root).
| param | meaning |
|---|---|
pid | target process (>0), the caller's group (0), or group -pid (<0) |
sig | the signal number to deliver |
Returns 0 on success, or a negative error (-EPERM / -ESRCH)
__sysint __sys(int num, int a, int b, int c, int d, int e, int f)
The raw system-call intrinsic that every wrapper above is built on. num selects the call (loaded into r7); up to six arguments go in r1..r6 and the result comes back in r1. Use it to reach syscalls the prelude does not wrap - the syscall reference (linked above) lists every number and its signature.
| param | meaning |
|---|---|
num | the syscall number |
a..f | up to six integer/pointer arguments |
Returns the syscall's result intrinsic