libk  porting

porting libk

libk is a highly modular library that relies on several layers of abstractions to make porting as easy as possible. porting it to new architectures should be relatively simple, potentially even doable by a single person. porting it to new non-POSIX operating systems is a much more complex task.

new architectures

the process of porting libk to a new architecture is straightforward. libk relies on a small amount of architecture-specific assembly code to provide the syscall apparatus and basic C runtime. this sounds complicated, but on linux, the files are only a handful of lines long.

the architecture-specific code is in /mod/kcore, with various support macros and headers in the /arch directory. the file names are structured on the pattern name.role.arch.os[.bits].s; this pattern is used by the build script to select the appropriate assembly file for the build target. role must be either "fn" or "rt"; "fn" files are assembled into the shared library while ones marked "rt" are placed into kboot.o, the libk version of crt0.o which must be statically linked into the program. the fn/rt distinction is not relevant for the libk.a static target, and is only used when generating the shared library.

please note that the use of inline assembly is not permitted in libk code as it is an extension, rather than part of the standardized language — not to mention that it's near-unreadable and has poor support for smart macros or intel syntax, which are both used extensively by libk.

new operating systems

porting to a new operating system is relatively easy if it's a UNIX system.

firstly, you will need to implement a syscall function for that system's syscall ABI. you will also need to provide a header with a listing of syscall numbers; if you have a working install running on the target you wish to port to, you may be able to extract the appropriate syscall numbers from that system's <sys/syscall.h> header, but the canonical and likely best source is your operating system's kernel source tree (if available), as this will also contain listings of syscall codes for all architectures that operating system supports. failing that, the musl libc source tree may have the necessary listings.

by far, one of the simplest and most helpful things you can do as a contributor is extract syscall code listings for as-yet unsupported targets and add new syscall mappings to the libk source tree; even if you cannot or do not intend to port libk to that target yourself, this busy work can save a lot of effort down the road if someone else decides they want to.

some architectures may also use their own errno codes and signal numbers. these are currently defined using preprocessor conditionals in the header <arch/posix.h>, as POSIX magic numbers are generally consistent with only a small number of exceptions. however, if this mechanism becomes unwieldy in the longterm, the global POSIX header may need to be pared down or even removed in favor of fully target-specific definitions.

non-UNIX systems are a different story.

the first step in porting libk to a new non-UNIX operating system is porting its build script. this may be a non-issue on certain partially-POSIX systems like Haiku, which support the bash shell and gcc toolchain in spite of not being a UNIX. systems like Windows (assuming you're not building for a POSIX compatibility layer) will require a full port, however.

once the new build system is in place, you will have to add OS-specific code to all low-level functions that invoke the OS directly, such as kmheapa() or kiosend(). the header <k/def.h> (generated from /mod/kcore/def.h.m) provides defines that may be used to detect the current system. to locate system-specific code, simply run the build script — if written correctly, this will generate compile errors from every file that contains system-specific code (via the Knoimpl() macro defined in <k/def.h>).