Files
MontaukOS/programs/rust/README.md
T

106 lines
5.5 KiB
Markdown

# 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/<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::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.