Rust on MontaukOS
Rust programs are built for x86_64-unknown-montauk, a built-in target of a
forked rustc with a native std (target_os = "montauk", not a Unix). Both
std programs and no_std programs (on montauk-rt) are supported.
Setup (once)
# rustup, plus a nightly whose cargo the linked toolchain borrows
curl --proto '=https' -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none
~/.cargo/bin/rustup toolchain install nightly
# The fork: rust-lang/rust, branch `montauk` (bootstrap.toml is set up)
cd ~/dev/mtk/rust
./x build library --stage 1 --target x86_64-unknown-linux-gnu,x86_64-unknown-montauk
rustup toolchain link montauk ~/dev/mtk/rust/build/host/stage1
The stage1 build takes about five minutes (it downloads CI LLVM). After
changing only library/, rebuild with
./x build library --stage 1 --target x86_64-unknown-montauk (seconds); the
link stays valid.
rust-toolchain.toml selects the montauk toolchain for this directory. It
has no cargo of its own; rustup falls back to the nightly's cargo.
make programs builds the Rust programs when that toolchain is linked, and
prints a notice and skips them otherwise. It never downloads a toolchain
(RUSTUP_AUTO_INSTALL=0).
Building
make -C programs rust # cargo build --release, copies to bin/os/*.elf
cd programs/rust && cargo build --release
Output: target/x86_64-unknown-montauk/release/<name>, installed as
programs/bin/os/<name>.elf. Add new binaries to RUST_PROGRAMS in
programs/GNUmakefile and to the workspace members.
The GCC driver links crt1/crti/crtbegin, the libc and libgcc from
toolchain/sysroot, the same as ports. After changing programs/lib/libc,
re-run toolchain/build-montauk-toolchain.sh so Rust programs pick it up.
make rust relinks the programs when the sysroot libc or the toolchain's std
is newer than the last build (cargo tracks neither).
Layout
| Path | Purpose |
|---|---|
.cargo/config.toml |
target = x86_64-unknown-montauk, linker = x86_64-montauk-gcc |
rust-toolchain.toml |
the linked montauk toolchain |
ruststd/ |
std smoke test (ruststd, ruststd net <host> [port]) |
montauk-rt/ |
no_std runtime: syscalls, allocator, print!, argv, threads, futex Mutex, panic handler |
rusthello/ |
no_std smoke test (rusthello, rusthello panic, rusthello panic-thread, rusthello fault-thread) |
The std port (in the fork)
| Fork path | What |
|---|---|
compiler/rustc_target/src/spec/base/montauk.rs, targets/x86_64_unknown_montauk.rs |
Target: static non-PIE ET_EXEC, no red zone, local-exec TLS, panic=abort, linker x86_64-montauk-gcc |
library/std/src/sys/pal/montauk/ |
Platform layer: abi.rs (syscall numbers, status codes, SYSCALL wrappers) |
library/std/src/sys/*/montauk.rs |
alloc, env, fs, io errors, net, paths, process, random, stdio, futex, thread, time |
library/std/src/os/montauk/ |
std::os::montauk::ffi::{OsStrExt, OsStringExt} |
std calls the kernel directly. The libc is used only where C code in the same
process must agree: the heap (malloc), the environment table (setenv),
exit (atexit handlers) and line-buffered terminal input (read(0)). The
syscall numbers in abi.rs are a hand-kept subset of Api/Syscall.hpp.
Platform behaviour
- Errors: the raw OS error of an
io::Erroris the kernel status code (positive,montauk/statuscodes.h);ErrorKindis mapped from it. - Paths:
0:/os/x.elfis absolute; joining onto it replaces the base. Search paths (PATH,env::split_paths) are separated by;, not:. - Files: kernel handles have no position, so
Filekeeps its own cursor.OpenOptions::truncatedeletes and recreates the file;set_lencan only grow a file. No links, permissions are read-only,fsyncis a no-op. - Threads: stacks are mapped with
SYS_MMAP_ANON(no guard page). A detached thread is reaped (joined, stack freed) by the next spawn or join, so its kernel slot does not leak.available_parallelism()is an error (the kernel does not report a CPU count).std::process::exitfrom any thread ends the process (libcexit->SYS_EXIT_GROUP). - Sync:
Mutex,RwLock,Condvar,Onceand thread parking use the generic futex implementations overSYS_FUTEX_WAIT/SYS_FUTEX_WAKE. thread_local!uses native#[thread_local](PT_TLS); destructors run when a spawned thread exits (not on the main thread).- Time:
Instanthas 1 ms resolution;SystemTimecomes from the wall clock (local time minus the timezone offset) with 1 s resolution. - Processes:
Commandsupports arguments, environment,current_dir(entered in the parent for the duration of the spawn),wait,try_waitandkill. Only inherited stdio is supported; piped/null stdio andoutput()returnUnsupported, because a redirected child's output stream disappears with the child. A bare program name is searched inPATH, else0:/osand0:/games;.elfmay be omitted.ExitStatusshows abnormal ends as "terminated by signal N". - Net: IPv4 only. TCP connect/accept/read/write with read timeouts and
non-blocking mode, UDP send_to/recv_from/connect, DNS via
SYS_RESOLVE. No socket options, nopeek, noshutdown(Write), and accepted connections do not know the peer's address. panic = "abort": a panic on any thread prints the message and kills the whole process (exit status "signal 6").HashMap's random keys come fromSYS_GETRANDOM, which is not a CSPRNG.