Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Development guide

Toolchain

  • Rust 1.85+ (MSRV), edition 2024 — rust-toolchain.toml pins stable + rustfmt + clippy.
  • Single crate, single dependency (libc). The loader/ sub-crate is built by build.rs with a direct rustc --edition=2024 invocation (it is no_std/freestanding, not a Cargo workspace member).
  • Syscall-number tables are generated by build.rs from data/sysnums-*.txt into OUT_DIR/sysnums.rs.
$ cargo build            # debug
$ cargo build --release  # release → target/release/proot
$ cargo test             # unit tests

Quality gates (CI-enforced)

$ cargo fmt --all -- --check
$ cargo clippy --all-targets --locked -- -D warnings
$ cargo test --locked
$ cargo deny check
$ cargo build --release --locked
$ cargo doc --no-deps --document-private-items

rustfmt.toml: edition 2024, 100 columns, Unix newlines. Cargo.toml [lints]: unsafe_op_in_unsafe_fn = "deny" + clippy::all warn (CI: -D warnings). deny.toml: license allow-list, advisory DB, banned sources.

The parity rule

Every src/ file is a named port of a C unit (comments say so: // port of tracee/event.c). When changing behavior:

  1. Find the C counterpart — the reference checkout is the spec.
  2. Keep C-identical errno semantics: innermost -errno returns, sys::clear_errno() before calls judged by errno == 0, process_vm_* fast-path success must clear a potentially stale errno (the C code does this inside the mem primitives — a subtle rule that already bit us once).
  3. Keep C’s verdicts for weird cases — they are almost always load-bearing (e.g. deliver_sigtrap, INHERIT_PARENT → shared, reconf.tracee == NULL runner resolution).

The unsafe policy

Where unsafe may live, in descending preference:

  1. src/sys.rs — the only home for libc calls. New kernel boundary? Add a wrapper, // SAFETY: note, keep C status/errno return.
  2. Documented islands — execve/elf.rs union accessors, tracee/event.rs kernel-struct reads, syscall/netlink.rs CStr.
  3. loader/ — freestanding, inherently unsafe.

Hard rules: no static mut (atomics/thread_local!), no transmute (sys::as_bytes, offset_of!), no raw pointers in safe signatures (use usize words), // SAFETY: on every block, and unsafe_op_in_unsafe_fn is denied crate-wide.

Project layout

src/
  cli/mod.rs        option parsing, launch, run loop
  tracee/           registry, event loop, regs, mem, seccomp, ABI
  ptrace/           ptrace + wait *emulation* (guest ptrace API)
  syscall/          enter/exit pipeline, sockets, netlink, seccomp
  path/             canon, bindings, glue, proc_emul, temp, f2fs
  execve/           ELF, ldso, shebang, auxv, aoxp, enter/exit
  extension/        Event/AnyExtension + 8 extensions (sysvipc sub-mod)
  fpath.rs          FixedPath — PATH_MAX buffer with C semantics
  sys.rs            all libc wrappers (the unsafe home)
  sysnum.rs         per-ABI syscall tables (generated)
  arch.rs note.rs util.rs
loader/             no_std execve-completion stub
data/               sysnums-*.txt tables
tests/              (empty — unit tests are #[cfg(test)] in-file;
                     integration = upstream C suite)
docs/               this book
doc/proot.1         man page

Adding a syscall

  1. Find its sysnum entry in data/sysnums-<abi>.txt (regen happens at build time).
  2. Implement translate_xxx_enter/_exit in syscall/enter.rs or exit.rs (or a focused module like socket.rs).
  3. Route args via tracee::mem (peek/poke primitives); translate paths via path::canon.
  4. Add an integration test upstream and/or a #[cfg(test)] unit test for pure logic.

Adding an extension

See Extension framework → Writing an extension.

Debugging

  • -v 9 = full syscall trace on stderr; PROOT_VERBOSE equivalent.
  • PROOT_NO_SECCOMP=1 — rule out the accelerator when a stop is misbehaving.
  • strace -f -e ptrace ./proot … — watch the tracer’s own ptrace.
  • RUST_BACKTRACE=1 — panics (should not happen — report them).

Commit & PR conventions

  • Small focused commits, imperative subject; why in the body.
  • CI must be green: fmt, clippy, tests, deny, docs.
  • Behavioral changes need the upstream suite green (see Testing).