# Musl libc in the Mittos kernel The Mittos kernel is linked with Musl libc which provides functionality such as fine-grained memory management and string formating. The [musl library](https://musl.libc.org/) is a tiny c standard library impelentation which is easy to use. It is however highly linux-specific and therefore relies of the availability of a large number of linux syscalls. In building musl for the kernel, I patch away this reliance entirely and replace syscalls with calls to internal functions that are prototyped and can then be provided by the kernel or a separate library when needed. By syncing a patch directory into the musl source with rsync before compilation: ``` > rsync -a src/patch-musl/ external/musl/ ``` I can provide ovrrides for specific parts of the musl code such as the following which replaces all `syscall` instructions with calls to external functions: `src/patch-musl/arch/x86_64/syscall_arch.h` ```c #define __SYSCALL_LL_E(x) (x) #define __SYSCALL_LL_O(x) (x) extern long syscall0(long); extern long syscall1(long, long); ... extern long syscall6(long, long, long, long, long, long, long); static __inline long __syscall0(long n) { return syscall0(n); } ... static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) { return syscall6(n, a1, a2, a3, a4, a5, a6); } #define VDSO_USEFUL #define VDSO_CGT_SYM "__vdso_clock_gettime" #define VDSO_CGT_VER "LINUX_2.6" #define VDSO_GETCPU_SYM "__vdso_getcpu" #define VDSO_GETCPU_VER "LINUX_2.6" #define IPC_64 0 ``` The kernel is then linked with `-L/opt/sysroot/usr/lib -lc`, but must provide the functions `syscall0(long)` through `syscall6(long, long, long, long, long, long, long)` itself. At first, those can be very simple: ```c long syscall0(long num) { PANIC("Unknown syscall: %d()\n", num); } ``` This will clearly tell me when I try to use a c function that requires support for a syscall, such as `malloc` which will result in the panic message "Unknown syscall: 12(0)". Looking into the musl source code, the file `arch/x86_64/bits/syscall.h.in` tells me that syscall 12 is `brk`, so that's a good thing to start implementing. Once that's done, I put a reference to my `k_brk` function into an array and update the `syscallN` functions to call my implementations if available: ```c long syscall0(long num) { long retval = 0; if(syscall_handlers[num]) retval = syscall_handlers[num](0, 0, 0, 0, 0, 0); else PANIC("Unknown syscall: %d()\n", num); return retval; } ``` The `k_brk` implementation is very simple too. Just allocate pages until the brk requirement is fulfilled: ```c static long _brk = KERNEL_BRK0; long k_brk(long brk, long, long, long, long, long) { if(brk) { while(_brk < brk) { vmm_set_page(kernel_P4, _brk, pmm_alloc(), PAGE_GLOBAL | PAGE_WRITE | PAGE_PRESENT); _brk += PAGE_SIZE; } } return _brk; } ``` And now I can use standard `malloc` and `calloc` in my kernel. After implementing `mmap` as well, I also have `realloc`. Nice! ## printf In order to enable `printf` for debug output, I only need two "syscalls" mocked - `ioctl` and `writev`. When writing to stdout, musl will call `ioctl` with command `0x5413` to get the terminal size: ```c long k_ioctl(long fd, long cmd, long arg3, long, long, long) { if(fd == 1 && cmd == 0x5413) { struct { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xp; unsigned short ws_yp; } *wsz = (void *)arg3; wsz->ws_row = 24; wsz->ws_col = 80; return 0; } long retval = -1; PANIC("Unknown IOCTL request: fd: %d, command: %x\n", fd, cmd); return retval; } ``` And it will then call `writev` with the vectors of data to write: ```c #include // for struct iovec long k_writev(long fd, long iov, long iovcnt, long, long, long) { if(fd == 1) { size_t len = 0; struct iovec *v = (void *) iov; for(int i = 0; i < iovcnt; i++) { debug_putsn(v[i].iov_base, v[i].iov_len); len += v[i].iov_len; } return len; } long retval = 0; PANIC("Unknown writev request: fd: %d\n", fd); return retval; } ``` I then have a fully featured printf implementation to help me in "printf debugging". ## Relevant files - `toolchain/build-musl.sh` - `src/patch-musl/arch/x86_64` - `src/kernel/lib/musl-glue.c` - `src/kernel/include/musl-glue.c` - `src/kernel/memory/kbrk.c`