106 lines
5.5 KiB
Markdown
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.
|