foundry
Foundry OS: the operating system built on the Rustbelt kernel
3925 bytes ยท raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | # Porting a C program to Foundry OS
*Status: milestone M2. `hello-musl` and `fprobe` (in `c/`) are built this way, live in `/bin`, and `make test` checks their output.*
## The toolchain
Rustbelt (since 0.19.0) loads **static-PIE** programs (`ET_DYN` with no
`PT_INTERP`), so C programs need no special link tricks any more. Every C
program and port is built as a static-PIE:
```sh
make musl # once: musl into out/sysroot (+ musl-pie.specs)
toolchain/foundry-cc -O2 c/hello-musl.c -o out/hello-musl
readelf -l out/hello-musl | grep INTERP # must print nothing
```
`toolchain/foundry-cc` is three lines: `gcc -specs out/sysroot/lib/musl-pie.specs
-static-pie "$@"`. The specs file (`toolchain/musl/musl-pie.specs.in`, from
Clarke's 0.19.0 re-test) points gcc at our musl headers and libraries and,
for `-static-pie`, uses musl's self-relocating start file **`rcrt1.o`** and
links with **`--no-dynamic-linker`**.
**The gotcha:** the `musl-gcc` wrapper that musl itself installs (into
`out/sysroot/bin`) uses upstream `musl-gcc.specs`, and with `-static-pie`
that makes a *dynamic* PIE: it still has a `PT_INTERP`
(`/lib/ld-musl-x86_64.so.1`). Rustbelt refuses that with `ENOEXEC`. So use
`foundry-cc`, not that `musl-gcc`. Debian's `musl-gcc -static-pie`
(package `musl-tools`) also works; the kernel builds its own musl fixtures
with it.
The binaries also run on the Linux host, which is handy because configure
scripts run their test programs.
The old `rb-cc` wrapper from the proposal (crt1.o, `-Ttext-segment`,
`--defsym=_DYNAMIC`, `--no-relax`, for `ET_EXEC` at Rustbelt's old high
user base) is retired.
### Plain `-static` (ET_EXEC) works too, since 0.20.0, but we don't use it
Rustbelt 0.20.0 moved user space down to start at `0x40_0000`, so an
ordinary `gcc -static` / `musl-gcc -static` program (an `ET_EXEC` fixed at
`0x400000`) runs as it is. Checked on 2026-10-31 with a one-off image
(not part of `make test`):
```
hello-exec (Debian musl-gcc -O2 -static) ET_EXEC, entry 0x4010c8, no INTERP
hello-exec2 (gcc -specs out/sysroot/lib/musl-pie.specs -static -no-pie) same
A:/ $ hello-exec
hello from musl on Rustbelt 0.20.0 (Foundry OS, ET_EXEC)
A:/ $ hello-exec2
hello from musl on Rustbelt 0.20.0 (Foundry OS, ET_EXEC)
```
Foundry OS still builds everything as **static-PIE**: it works on 0.19.0
and later, gets a random load address, and doesn't depend on where the
kernel puts user space. Treat ET_EXEC as "good to know" (e.g. a
third-party binary you can't rebuild), not something to rely on. The
kernel tests it with its own fixtures (`exechello`, `execthreads`,
`timerfdtest`).
## Our own C programs (c/)
```
c/hello-musl.c one line: "hello from musl on Rustbelt 0.21.0 (Foundry OS, static-PIE)"
c/fprobe.c the libc self-check: 40 checks, "fprobe: all 40 checks passed"
```
To add one: write `c/NAME.c`, add `NAME` to `FOUNDRY_C` in `config.mk`,
run it from the "Foundry OS" block at the end of
`rootfs/scripts/selftest.sh`, and add the lines it must print to
`scripts/expect-foundry.txt`. `make test` then builds it (`make cprogs`,
with `toolchain/foundry-cc`), puts it in `/bin`, boots, and checks.
If you add or remove a check in `fprobe`, update the count in
`expect-foundry.txt` too.
## Adding a port
```
ports/<name>/<name>.sha256 "SHA256 <tarball>"
ports/<name>/<name>.url where to download it
ports/<name>/patches/ 0001-*.patch ...
ports/<name>/build.sh unpack from cache/, patch, CC=toolchain/foundry-cc ./configure --host=x86_64-linux-musl,
make, install into out/staging-v2/root (busybox/sbase: under /usr/lib/busybox)
```
Then boot it (`make test` / `make run`), try it, and note any missing
syscall in [syscall-gaps.md](syscall-gaps.md). `run --trace prog` in rbsh
shows every syscall, and `syscall_N(...) = -38 ENOSYS` marks the ones
Rustbelt doesn't have.
|