Userland libc
Makar has two C-library layers:
| Layer | Path | Built into | Purpose |
|---|---|---|---|
| kernel freestanding libc | src/libc/ |
libk.a |
tiny support library for the kernel itself |
| userspace libc shim | src/userspace/ |
/usr/lib/libc.a |
hosted-ish static libc for ring-3 apps and TinyCC |
This page describes the userspace libc shim and the ongoing path toward static musl binaries.
Design Goals
The userspace libc is not trying to become glibc. Its goals are narrower:
- Provide enough C/POSIX surface for Makar’s own apps.
- Provide enough hosted behavior for
tcc.elfto compile and link programs inside the OS. - Keep every function small enough to audit.
- Match Linux/POSIX shapes where useful so future musl/uClibc work is not boxed in by Makar-only APIs.
- Remain statically linked and simple.
The long-term compatibility target is static musl i386. The practical in-OS compiler remains TinyCC.
Installed Sysroot
src/userspace/Makefile builds the userspace shim and stages it into the OS
image:
| Installed path | Contents |
|---|---|
/usr/lib/crt0.o |
userspace process entry stub |
/usr/lib/libc.a |
static userspace libc shim |
/usr/include/*.h |
userspace C headers |
/usr/share/examples/hello-tcc.c |
canonical in-OS compile example |
The same makefile also stages apps under /apps.
Generated dependency files (*.d) are enabled with -MMD -MP so changes to
headers such as syscall.h rebuild the apps that include them.
Source Layout
| File | Role |
|---|---|
crt0.S |
initial process entry, argc/argv setup, call main, then exit |
malloc.{h,c} |
malloc, free, calloc, realloc over SYS_BRK |
stdio.{h,c} |
FILE * implementation and printf-family support |
stdlib.h |
declarations/inlines for allocation, conversion, env, system, sort/search |
string.{h,c} |
memory/string functions, tokenizer helpers, strerror |
setjmp.{h,S} |
i386 SysV setjmp/longjmp |
dirent.h |
POSIX-shaped directory iteration wrappers |
time.{h,c} |
UTC broken-down time conversion |
unistd.h |
POSIX-shaped declarations and thin syscall wrappers |
tcc_compat.c |
hosted wrapper functions used heavily by TinyCC |
syscall.h |
inline int 0x80 wrappers and syscall constants |
Current Header Surface
<stdio.h>
Implemented:
FILEstdin,stdout,stderrstyle behavior through fd wrappers where neededfopenfdopenfreadfwritefclosefflushfputsfputcfgetcprintffprintfsprintfsnprintfvsnprintfvfprintf
Not complete:
- no wide-character I/O
- no
popen - no complete POSIX buffering controls
- formatting is intentionally small and integer-focused
<stdlib.h>
Implemented:
mallocfreecallocreallocatoistrtolstrtollstrtod/strtofplaceholders where needed by TCC pathsqsortbsearchgetenvsetenvunsetenvputenvsystemexitabortsscanf
Environment variables are process-local. They do not cross execve, because
the kernel currently ignores the envp pointer passed to SYS_EXECVE.
system(command) forks and runs:
/apps/sh.elf -c "$command"
It returns the child exit status low byte, or -1 if the shell could not be
started.
<string.h>
Implemented:
memcmpmemcpymemmovememsetstrlenstrcpystrncpystrcatstrcmpstrncmpstrchrstrrchrstrstrstrdupstrtokstrtok_rstrerror
The implementation assumes the C locale.
<unistd.h>
Implemented or declared:
readwritecloselseekdupdup2pipechdirgetcwdgetpidgetppidunlinkrmdiraccesssleepusleep_exit
sleep and usleep spin-yield on the 250 Hz uptime tick. There is no
nanosleep syscall and no SIGALRM interruption behavior.
<time.h>
Implemented:
timegmtimegmtime_rlocaltimelocaltime_rmktimestrftimesubset
All local time is UTC. There is no timezone database.
Other Headers
| Header | Status |
|---|---|
<ctype.h> |
ASCII classifiers and case conversion |
<dirent.h> |
DIR * wrappers over Makar indexed readdir |
<errno.h> |
common errno definitions and global errno |
<setjmp.h> |
i386 setjmp/longjmp |
<fcntl.h> |
common open flags |
<sys/stat.h> |
limited struct stat and constants |
<signal.h> |
signal constants and wrappers |
Missing major families:
<pthread.h><locale.h><wchar.h><math.h>
The kernel FPU substrate exists, but libc math functions do not.
Kernel ABI Dependencies
The userspace libc shim depends on these kernel surfaces:
| Kernel feature | Status |
|---|---|
Linux i386 int 0x80 convention |
present |
| fd tables | present |
| file read/write fds | present |
brk |
present |
anonymous mmap2 / munmap |
present |
fork / execve / wait4 |
present |
pipe / dup / dup2 |
present |
stat / fstat / readdir |
present |
| signals and sigreturn | present, partial POSIX |
| FPU save/restore | present |
TLS through set_thread_area |
present |
| auxv | present |
Static musl Bring-Up
The branch adds the main pieces a static i386 musl binary expects during early startup:
- ELF auxv:
AT_PAGESZAT_RANDOMAT_NULL
set_thread_area(243):- one TLS GDT slot at index 6
%gs = 0x33- scheduler restore for TLS-active tasks
exit_group(252)as process exitset_tid_address(258)returning the task pidrt_sigprocmask(175)startup stubioctl(54)returning-ENOTTYfutex(240)startup stub for the single-threaded bring-up case- anonymous
mmap2(192) munmap(91)- FPU state preservation
Remaining work for a comfortable musl environment:
- implement any missing startup syscall discovered by real binaries
- implement
writevif the chosen musl build path requires it - make errno behavior consistent across wrappers
- package musl headers and archives into the Makar sysroot
- add more complete signal-mask semantics
- decide how much thread support is actually desired
- support file-backed mmap and
mprotectif future programs need them
The immediate proof target is a tiny static musl i386 program linked high enough for Makar’s loader, such as:
int main(void) { return 42; }
TinyCC vs musl
TinyCC currently uses the Makar libc shim. This is deliberate:
- the shim is small enough to debug inside the OS
- TCC’s needs are known and covered by
libc-tcc.sh - the generated binaries are static ET_EXEC files that Makar can load directly
musl is the next compatibility target for outside programs. It does not replace the value of the small in-tree shim for OS-native development.
Test Coverage
Use these tests when changing libc or syscall behavior:
./run.sh ktest
./run.sh gui libc
./run.sh gui all-tests
Important in-guest checks:
alloctest.elf: allocation, stdlib, env, tokenizer,bsearch, pipe/dup, time,system, anonymous mmaplibc-tcc.sh: compiles and runs the in-OS TCC/libc matrixshell-smoke.sh: exercisessh -c, quoting, and command behavior
Run kbtest as well if the change touches syscall numbers, shell focus,
keyboard routing, VT behavior, or makmux.