First Rust std image on IRIX 7
Status: bring-up result. 2026-10-25.
Result
A static Rust std binary now runs on IRIX 7 under iris. It prints through the
kernel console, reads its initial argument vector, and exits with status 0:
userelf: entry=0x10000360
user: entering user mode
hello from std
arg=hello
slept_ms=10
time=1792872464
file=hello from the IRIX 7 ramfs
write=ok
after_write=rewritten by std
chdir=ok
cwd=/etc
relative=Welcome to IRIX 7.
stat_size=17
entry=hello.txt
entry=etc
entry=dev
user: exit syscall code=0x00000000
kernel: userland returned to kernel console
The unstripped ELF has no undefined symbols. It is linked at
0x10000000, is a 32-bit big-endian MIPS ELF with n32 ABI flags, and is
stripped for the 256 KiB target-2 image.
This is the S1 gate from the Rust std research: it proves the target,
toolchain, libc ABI shim, loader and minimal syscall path. It is not a complete
POSIX environment. The production C ABI layer is now the
relibc fork; its n32 Linux/QEMU gate passes
(report).
What was added
crates/irix7-libc: a provisional 42-symbol C ABI shim forstdbring-up. It implements the abort-mode hello symbol set: console write, a bump allocator, string/memory primitives, single-threaded pthread mutex/cond/key stubs, and_Unwind_*stubs. The libc strategy is to fork relibc later; this crate is a validation shim, not the final libc.user/std-hello: the first staticstdprobe.- Kernel syscall cases for
open,close,fcntl,dup,getcwd,poll,signal,getentropy,clock_gettime,getpid,read,fstatandstat. - A tiny bring-up fd table (
kernel/src/fd.rs) for console,/dev/null, two ramfs files (/hello.txt,/etc/motd), directory iteration andchdir. - A 256 KiB loader image, a four-page user stack and 64 KiB SCSI read chunks.
- A fixed 1 MiB heap backed by two 256 KiB page pairs, so std’s allocator does not exhaust the 48-entry JTLB with 4 KiB mappings.
- The kernel stack top moved from
0x88200000to0x88300000. The old top left too little gap above.bss; deep boot stacks could overwrite the loader’s pair counter and cause an intermittent out-of-bounds cleanup panic. clock_gettime:CLOCK_REALTIMEfrom the RTC/NTP wall clock andCLOCK_MONOTONICfrom CP0 Count.nanosleepas a CP0 Count busy-wait inside the syscall, enough forstd::thread::sleep.std::fs::metadata,std::fs::read_dir,std::fs::readandstd::fs::write.chdir/getcwdand relative path lookup in the tiny namespace.- NUL-terminated initial
argv/envpstrings. Without the terminators,std::env::args()walked off the top of the user stack. has-thread-local: falseon the native target, keeping pthread-key TLS.
Reproduction outline
The std source and libc declarations come from the patched spike tree
prepared by tools/std-spike/build-std.sh. The image is built with:
cargo build --release -Zbuild-std=std,panic_abort -Zbuild-std-features= \
-Zjson-target-spec \
--target targets/mips3-sgi-irix7-native-n32.json
with -Clink-arg=-Tuser/hello/linker.ld, -e__start, --gc-sections, and
empty libc.a/libm.a/librt.a/libpthread.a archives so rustc’s default
Unix -l flags resolve to our shim. The image is stripped, padded to 256 KiB
and attached at SCSI target 2.
Limitations
- The Stage 0 CPU/MMU gate now runs the image with the correct
KSU=userandUXbits, the XTLB vector, R4000 PageMask encodings, cacheable LL/SC pages and a 64-bit trap frame. It is still a single-address-space bring-up path, not a process isolation boundary. - Single-threaded only;
pthread_createis not implemented. - A small two-file ramfs with a root directory and mutable file data; no general filesystem or process creation.
getentropyreturns zeroes (fine for the smoke test, not a secure RNG).- Signals are no-ops; no termios; no sockets.
forkis not present; the process model will beposix_spawn.- The real libc will be a relibc fork with an
irix7backend, a 64-bittime_t/off_tn32 ABI, and the MIPS n32 CRT/TLS/pthread port.
Next gates
(bring-up done)std::fsover a ramfs andstd::timewith real clocks.std::threadwith native threads/TLS.std::process::Commandoverposix_spawn.- termios + poll + Reedline.
- Brush, then a reduced Nushell on 128 MB.