# 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) ```bash # 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 ```bash 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/`, installed as `programs/bin/os/.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 [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::Error` is the kernel status code (positive, `montauk/statuscodes.h`); `ErrorKind` is mapped from it. - Paths: `0:/os/x.elf` is absolute; joining onto it replaces the base. Search paths (`PATH`, `env::split_paths`) are separated by `;`, not `:`. - Files: kernel handles have no position, so `File` keeps its own cursor. `OpenOptions::truncate` deletes and recreates the file; `set_len` can only grow a file. No links, permissions are read-only, `fsync` is 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::exit` from any thread ends the process (libc `exit` -> `SYS_EXIT_GROUP`). - Sync: `Mutex`, `RwLock`, `Condvar`, `Once` and thread parking use the generic futex implementations over `SYS_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: `Instant` has 1 ms resolution; `SystemTime` comes from the wall clock (local time minus the timezone offset) with 1 s resolution. - Processes: `Command` supports arguments, environment, `current_dir` (entered in the parent for the duration of the spawn), `wait`, `try_wait` and `kill`. Only inherited stdio is supported; piped/null stdio and `output()` return `Unsupported`, because a redirected child's output stream disappears with the child. A bare program name is searched in `PATH`, else `0:/os` and `0:/games`; `.elf` may be omitted. `ExitStatus` shows 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, no `peek`, no `shutdown(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 from `SYS_GETRANDOM`, which is not a CSPRNG.