Stability and stuff

Your binary wants to do something requiring a syscall, eh? There are two options: Either you do the syscall directly, or you call a wrapper that does it.

You can write the wrappers yourself (or try to generate them from syscalls.master). I call that doing the syscall directly.

Or you could use the existing wrappers. They are part of libc.

You can link statically or dynamically. Whenever you link dynamically, before executing, loads the shared objects, including the executable, links them all, resolves symbols and stuff, and finally jumps thither.

Something special does is if it finds a shared object called “” prohibiting anything else to make syscalls.

The OpenBSD syscall ABI is not stable. Syscall numbers or parameters may change.

The OpenBSD libc ABI isn’t stable either, though likely more stable than the syscall ABI.

The only good option is to generate as much as possible from the OpenBSD sources.


The syscalls are declared in syscalls.master. There, you can find:

TODO: check if all of the syscalls are mentioned in libc/sys/

Additional metadata is hand-written in libc’s build system. /usr/src/lib/libc/sys/ differentiates a few kinds of syscalls:

ASM (most syscalls)

For dup, the following symbols exist:

global default function symbol
weak default symbol, alias for _thread_sys_dup
global hidden symbol, alias for _thread_sys_dup
ASM_NOERR (a few syscalls)

This is the same as ASM, except that the wrapper doesn’t check the carry bit, with which syscalls indicate errors.

PSEUDO_NOERR (_exit only)

This is the same as ASM_NOERR, except that the symbols are named _thread_sys__exit, _libc__exit, and _exit, even though the syscall is named exit.

HIDDEN (quite a few syscalls)

This is the same as ASM, except that while there are _thread_sys_fork and _libc_fork symbols, there is no fork. Further, for some of these syscalls, the internal names have an extra leading underbar: _thread_sys__ptrace for syscall ptrace.

Many of the HIDDEN syscalls have wrappers in libc/sys/w_*.c. That’s how fork is defined, for example.

For reference, there are the three ELF bindings

not visible outside its own object file
visible in other object files
visible in other object files, but overridden by global symbols

and the four ELF visibilities

symbols visible outside the component (shared object) it’s defined in
symbol visible only inside its component
as hidden “and that some extra, processor specific processing must also be performed upon the symbols as well”
resolving the symbol inside its component does not take definitions of the same symbol outside this component into account

Approaches for foreign declarations

Hare gotta access the syscalls somehow. I’m playing with three options:

Manually doing syscalls

In my first attempt, I write wrappers for a bunch of syscalls by hand.

Reading syscalls.master & libc/sys/

In my second attempt, I generate declarations for those syscalls that actually exist; i.e., I look at internal files to get metadata.

Using the documented surface

The interface to the OpenBSD system is its C API. The libc one. The one that’s documented in man pages. So it makes sense to read the man pages, no?