FlashOS tour · 8 / 15
8. Userland: ELF, execve & flibc
FlashOS runs ordinary ELF64 programs at EL0. They are Rust no_std static
libraries linked with explicit user linker scripts and packaged into a
deterministic initramfs.
Building the filesystem image
xtask/src/build.rs builds every program under userland/, inspects the ELF,
strips it, and stages it under its runtime path. xtask/src/initramfs.rs
encodes the sorted staging tree as a deterministic newc CPIO archive.
Checked-in seed files come from rootfs/:
rootfs/etc/passwd → /etc/passwd
rootfs/etc/fshrc → /etc/fshrc
generated shadow → /etc/shadow
userland/init/pid1 → /sbin/init
userland/shells/fsh → /bin/fsh
userland/* tools → /bin/* and /test/*
rootfs/etc/perms.tab is the third checked-in seed. It is deployed to the
FAT32 volume as PERMS.TAB, not embedded in the read-only initramfs.
Programs are staged as mode 0755, public configuration as 0644, and shadow
as 0600, owned by root. The archive is embedded by crates/kernel/link/initramfs.S and
served read-only by crates/kernel/src/fs/initramfs_backend.rs.
The runtime boundary
The FlashSDK flashsdk-rt crate supplies the _start-side EL0 runtime, panic
path, memory intrinsics, and raw SVC transport. The loader enters a program with
a stack containing argc, the argv pointer array, and NUL-terminated argument
strings.
A teaching-sized entry shape looks like this:
#[no_mangle]
pub extern "C" fn main(argc: usize, argv: *const *const u8) -> i32 {
// inspect arguments through bounded userland helpers
0
}
The exact exported symbol and panic glue are supplied by each user crate and
the FlashSDK flashsdk-rt crate; inspect the real crate before treating a
simplified snippet as linkable code.
flibc
crates/flibc/ is the current Rust mini-libc. It provides:
- formatted and raw I/O;
- process and file syscall wrappers;
- a bump heap over
brk/sbrk; - readline, history, and completion;
- key decoding and TUI rendering;
- pager, gap-buffer, and grep-match cores.
It is part of the current in-repository implementation, not yet a stable external SDK. The post-Rust-port FlashSDK work will define the narrow public boundary and make the kernel consume that canonical ABI.
Loading PID 1
The kernel finds /sbin/init through the VFS and maps its ELF segments with
permissions derived from their program headers. userland/init/pid1/src/lib.rs runs
the optional boot-selftest harness, then replaces itself with /bin/login.
Four dedicated fixtures under /test exercise argument transfer, runtime I/O,
fork pressure, and stack failure paths without becoming user-facing commands.
[!NOTE] Kernel filesystem code, including the initramfs parser, is under
crates/kernel/; EL0 programs are underuserland/; static seeds are underrootfs/.
Next, we follow PID 1 through authentication and privilege dropping.