RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

authorGrok Bot <grokbot@localhost>2026-10-31 19:04:02 -0700
commit7cf3556ce83e3d8d018ab0da3d9961df733566b2
parent909242193c97a959e835a276764fa1067519c3fc
treebrowse files at this commit
toolchain: foundry-cc + musl-pie.specs, every C program and port is static-PIE

Based on Clarke's 0.19.0 re-test (clarke-scratch/tc/musl-pie.specs and
rb-cc-pie, read only): rcrt1.o + --no-dynamic-linker. Upstream
musl-gcc.specs makes a dynamic PIE with PT_INTERP, which Rustbelt refuses
(ENOEXEC). make musl now installs out/sysroot/lib/musl-pie.specs; checked:
c/hello-musl.c -> ET_DYN with no PT_INTERP. rb-cc is retired.
MREADME.md+2 -2
Mc/README.md+1 -1
Mc/hello-musl.c+1 -1
Mdocs/porting.md+25 -21
Mdocs/proposal.md+1 -1
Mports/README.md+7 -1
Atoolchain/foundry-cc+8 -0
Mtoolchain/musl/build.sh+8 -3
Atoolchain/musl/musl-pie.specs.in+32 -0

9 files changed, 85 insertions, 30 deletions

@@ -35,7 +35,7 @@ the packages the kernel needs plus `make` (all already on the box): | `make test` | headless boot, self-test, ~90 expected-output checks; exit 0 = pass | | `make test-reference` | the same test on the kernel's *own* disk image (the baseline) | | `make compare` | compares our image with the kernel's, file by file |-| `make musl` | downloads (into `cache/`) and builds musl into `out/sysroot` (for M2) |+| `make musl` | downloads (into `cache/`) and builds musl into `out/sysroot`; then `toolchain/foundry-cc` builds static-PIE C programs (M2) | | `make clean` | `rm -rf out` (`make distclean` also empties `cache/`) |  ## What is where@@ -52,7 +52,7 @@ foundry/ │   └── link.ld           puts programs at address 0x80_0040_0000 ├── rootfs/               files copied onto the disk as they are (readme, notes, the self-test) │   └── etc/              passwd, hosts... for the later ext2 root (not in the image yet)-├── toolchain/musl/       musl C library: pinned by SHA256, downloaded into cache/+├── toolchain/           foundry-cc (gcc + musl, static-PIE); musl/ pinned by SHA256, downloaded into cache/ ├── c/                    our own C programs (M2) ├── ports/                other people's C programs, one directory each (dash first) ├── scripts/              mkimage, build-kernel, run-qemu, run-headless, compare-image...
@@ -3,4 +3,4 @@ libc smoke tests for Foundry OS, built against musl. Not in the image yet (milestone M2 adds them to /bin and to the self-test). -- `hello-musl.c`: prints one line; build with `musl-gcc -static-pie`.+- `hello-musl.c`: prints one line; build with `toolchain/foundry-cc` (static-PIE; `make musl` first).
@@ -1,5 +1,5 @@ /* The first Foundry OS C program (milestone M2): plain C against musl.- * Build:  musl-gcc -O2 -static-pie hello-musl.c -o hello-musl+ * Build:  toolchain/foundry-cc -O2 c/hello-musl.c -o out/hello-musl  (after `make musl`)  * (Rustbelt 0.19.0 loads static-PIE programs, so no special link flags.) */ #include <stdio.h> #include <sys/utsname.h>
@@ -4,33 +4,37 @@  ## The toolchain -Rustbelt 0.19.0 loads **static-PIE** programs, so C programs need no special-tricks any more. With Debian's musl (`musl-tools`):+Rustbelt 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-musl-gcc -O2 -static-pie c/hello-musl.c -o out/hello-musl+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 ``` -That makes an `ET_DYN` file with no `PT_INTERP`: a static-PIE. It runs on the-Linux host too, which is handy (configure scripts run their test programs).+`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 old `rb-cc` wrapper from the proposal (crt1.o, `-Ttext-segment`,-`--defsym=_DYNAMIC`, `--no-relax`) was only needed for `ET_EXEC` at-Rustbelt's high user base. Don't use it for new programs. The kernel keeps-one program built that way (`vdsotest`) as a loader test.--## Our own musl (`make musl`)+**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. -`make musl` downloads musl into `cache/` (SHA256-checked, see-`toolchain/musl/`) and installs a static-only musl into `out/sysroot`-(`include/`, `lib/libc.a`, the `crt*.o` start files, and a `bin/musl-gcc`-wrapper).+The binaries also run on the Linux host, which is handy because configure+scripts run their test programs. -**Known gap (M2 to-do):** musl's own generated `musl-gcc` wrapper does not-handle `-static-pie`. Its specs file always adds the dynamic-loader path, so-the result gets a `PT_INTERP` and is refused. Debian's `musl-gcc` handles-this properly, so use that until `toolchain/musl/` ships its own-`musl-gcc.specs` (or a small `foundry-cc` wrapper) with the `static-pie` case.+The old `rb-cc` wrapper from the proposal (crt1.o, `-Ttext-segment`,+`--defsym=_DYNAMIC`, `--no-relax`, for `ET_EXEC` at Rustbelt's high user+base) is retired. The kernel keeps one program built that way (`vdsotest`)+as a loader test.  ## Adding a port @@ -38,7 +42,7 @@ this properly, so use that until `toolchain/musl/` ships its own 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=musl-gcc ./configure --host=x86_64-linux-musl,+ports/<name>/build.sh        unpack from cache/, patch, CC=toolchain/foundry-cc ./configure --host=x86_64-linux-musl,                              make, install into out/staging (busybox/sbase: under /usr/lib/busybox) ``` 
@@ -487,7 +487,7 @@ rustbelt-os/ │   │   ├── fetch.sh                  download + SHA256 check │   │   ├── build.sh                  unpack → patch → configure → make → install into out/sysroot │   │   └── patches/                  0001-*.patch, applied in order (empty at first)-│   └── (no rb-cc: Rustbelt 0.19.0 runs static-PIE, so plain musl-gcc -static-pie works)+│   └── foundry-cc            gcc -specs musl-pie.specs -static-pie (no rb-cc hacks: 0.19.0 runs static-PIE) │ ├── c/                        small C programs of our own (hello-musl.c, mprobe.c = libc smoke tests) │
@@ -8,7 +8,13 @@ ports/<name>/ ├── <name>.sha256   the pinned release tarball: "SHA256  file-name" ├── <name>.url      where to download it (into the git-ignored cache/) ├── patches/        our changes, applied in order (0001-*.patch, ...)-└── build.sh        unpack -> patch -> configure -> make -> install into out/staging+└── build.sh        unpack -> patch -> CC=toolchain/foundry-cc configure -> make -> install into out/staging+```++Every port is built as a **static-PIE** with `toolchain/foundry-cc` (see+docs/porting.md).++``` ```  No tarball is ever committed; `cache/` is git-ignored and every download is
@@ -0,0 +1,8 @@+#!/bin/sh+# foundry-cc: the C compiler for Foundry OS programs and ports.+# Plain gcc + our musl sysroot (make musl), always static-PIE: Rustbelt+# 0.19.0 loads static-PIE, so there are no link tricks (the old rb-cc).+# Based on Clarke's 0.19.0 re-test wrapper (clarke-scratch/tc/rb-cc-pie).+# SYSROOT defaults to out/sysroot of this repo.+SYSROOT=${FOUNDRY_SYSROOT:-$(cd "$(dirname "$0")/.." && pwd)/out/sysroot}+exec gcc -specs "$SYSROOT/lib/musl-pie.specs" -static-pie "$@"
@@ -4,9 +4,11 @@ # the crt*.o start files in lib/, plus a musl-gcc wrapper in bin/). # # Not used by the contract-v1 image yet (milestone M2). Since Rustbelt-# 0.19.0 loads static-PIE programs, the plan is plain-#   out/sysroot/bin/musl-gcc -static-pie hello.c-# with none of the old rb-cc link tricks (see docs/porting.md).+# 0.19.0 loads static-PIE programs, C programs are built with+#   toolchain/foundry-cc hello.c -o hello      (gcc + musl-pie.specs, -static-pie)+# with none of the old rb-cc link tricks (see docs/porting.md). Do NOT use+# the musl-gcc that musl installs into SYSROOT/bin for -static-pie: its+# specs add a PT_INTERP. # # Usage: toolchain/musl/build.sh CACHE_DIR BUILD_DIR SYSROOT set -euo pipefail@@ -24,4 +26,7 @@ cd "$src" ./configure --prefix="$SYSROOT" --syslibdir="$SYSROOT/lib" --disable-shared >/dev/null make -j"$(nproc)" >/dev/null make install >/dev/null+# Our static-PIE specs (musl's own musl-gcc.specs makes a dynamic PIE with+# PT_INTERP, which Rustbelt refuses); toolchain/foundry-cc uses it.+sed "s#@SYSROOT@#$SYSROOT#g" "$HERE/musl-pie.specs.in" > "$SYSROOT/lib/musl-pie.specs" echo "[musl] installed into $SYSROOT ($(ls "$SYSROOT/lib" | wc -l) files in lib/)"
@@ -0,0 +1,32 @@+%rename cpp_options old_cpp_options++*cpp_options:+-nostdinc -isystem @SYSROOT@/include -isystem include%s %(old_cpp_options)++*cc1:+%(cc1_cpu) -nostdinc -isystem @SYSROOT@/include -isystem include%s++*link_libgcc:+-L@SYSROOT@/lib -L .%s++*libgcc:+libgcc.a%s %:if-exists(libgcc_eh.a%s)++*startfile:+%{shared:;static-pie:@SYSROOT@/lib/rcrt1.o; :@SYSROOT@/lib/Scrt1.o} @SYSROOT@/lib/crti.o crtbeginS.o%s++*endfile:+crtendS.o%s @SYSROOT@/lib/crtn.o++*link:+-dynamic-linker /lib/ld-musl-x86_64.so.1 -nostdlib %{shared:-shared} %{static:-static} %{static-pie:-static -pie --no-dynamic-linker} %{rdynamic:-export-dynamic}++*esp_link:+++*esp_options:+++*esp_cpp_options:++