# M0: Arch Linux x86_64 build host [Documentation index](README.md) · [First build](getting-started.md) · [Troubleshooting](troubleshooting.md) This is the technical reference for the build foundation used by later milestones. The [rootfs guide](rootfs.md) adds assembly, Python and isolated ARM execution requirements. If you are setting up the repository for the first time, follow [Your first build](getting-started.md) for an ordered walkthrough with expected output. For editing, rebuilding, and configuration changes after setup, use [Development](development.md). ## Scope and pinned inputs M0 validates the cross-build environment. The scoped [Dasung integration](dasung.md) adds a controller package and service definitions without implementing a full OS. M1 adds the configured FDS rootfs; M2 adds native init and the generic ARM VM. M3 adds static FDS tools. M4 cross-builds the Pi kernel and creates initramfs, SYSTEM GPT/EROFS and a FAT32 boot partition; see [Boot images](boot.md). M6 adds cartridge management and M7 adds writable DATA. M8 adds the optional WindowMaker stack and on-demand Ethernet. M12 adds the [complete internal disk](internal-storage.md), independent recovery and persistent machine settings. The [signature tools](releases.md) have passed acceptance; the [frozen-input workflow](reproducible-builds.md) and signed local 0.1.0 release have passed two complete offline builds and artifact/archive verification. The [desktop guide](desktop.md) explains the new dependencies and the development image used for virtual display tests. See [Native init](init.md) for the additional test dependencies. An ARM host and a connected Pi are not required. | Input | Pin | | --- | --- | | void-packages | Git submodule and `VOID_PACKAGES_COMMIT`: `02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1` | | Static XBPS host tools | Versioned `0.60.4_1.x86_64-musl` URL and a SHA-256 digest stored in this repository | | Rust | `rust-toolchain.toml`: 1.98.0, minimal profile, rustfmt | | Rust target | `aarch64-unknown-linux-musl` | | Rust crate dependencies | Smoketest has none; all workspace dependencies, including shared Clap CLI parsing, are pinned in `Cargo.lock`, fetched at bootstrap, and built with `--locked --offline` | | XBPS test package | `srcpkgs/hello/template` in the pinned Void tree: GNU hello 2.12.3_1 | | Pi kernel | `packages/fds-kernel/template`: 6.12.87, pinned Raspberry Pi source archive and FDS config delta | | Pi firmware | `config/pi-firmware.conf`: signed 20260715_1 package with a pinned SHA-256 | The musl ABI of the static host tools does not determine the target ABI. Scripts explicitly use `XBPS_ARCH=x86_64` for the glibc build container, then run `./xbps-src -a aarch64 pkg hello` to build the ARM glibc package. Local ARM package indexing also explicitly sets `XBPS_ARCH=aarch64`. ## Separate generic Linux workstation tools `make workstation` builds native `fds-cartridge` and `fds-emulator` without the Void/Arch bootstrap. Their runtime dependencies are QEMU (`qemu-system-aarch64` and `qemu-img`), erofs-utils, xz and bubblewrap. A software recipe chooses its cross compiler. The shared archive reader adds Rust `tar` 0.4.46 and its `filetime` dependency; both are locked. No new daemon or host build tools are installed in the Pi base image. Follow [the workstation guide](workstation.md). The full OS build prerequisites below retain their existing scope. ## Host dependencies Run as an ordinary user on x86_64 Arch Linux with unprivileged user namespaces. Rootfs construction now requires Linux 6.7+ with namespaced `binfmt_misc` support. The M5 build uses a private handler and static QEMU for child execution; it does not register an ARM interpreter in the workstation's global registry. The checkout path must not contain whitespace, a limitation of upstream xbps-src. Allow several GB of disk space and provide a network connection. ```sh sudo pacman -S --needed bash coreutils binutils git curl make file tar xz gzip zstd \ bubblewrap rustup ca-certificates findutils diffutils grep sed gawk util-linux ``` Alternatively, run `./tools/bootstrap-host --install-deps`. Only this explicit option invokes sudo/pacman. Default `make bootstrap` checks dependencies and exits nonzero when one is missing, without installing host system packages. Dependency rationale: - bash, coreutils, findutils, diffutils, grep, sed, gawk, and util-linux provide the build scripts and upstream build system with their required shell tools. - git pins upstream source; curl and CA certificates download HTTPS inputs. - tar, xz, gzip, and zstd extract archives and XBPS packages. - file and binutils inspect ELF artifacts; make provides the build entry points. - bubblewrap runs the build container as an ordinary user. - rustup installs the pinned Rust toolchain and target. rustfmt checks source formatting and is not a runtime dependency. Rust's bundled `rust-lld` and target-provided static musl/CRT are sufficient for the current pure-Rust smoketest. No Zig, Docker, OpenSSL crate, or additional musl runtime is required. The imported Dasung controller bundles libusb and uses a Void musl C cross compiler inside the container; see [its dependency rationale](dasung.md#dependencies-and-source-provenance). Glibc C libraries cannot simply be reused in static-musl binaries. ## Exact commands Run sequentially from the repository root: ```sh make bootstrap make smoke-test make check ``` Bootstrap initializes the pinned submodule, verifies and extracts XBPS, checks the tools' x86_64 static ELF properties, installs the pinned Rust toolchain and target, fetches the locked workspace crates, copies `config/xbps-src.conf` into the local Void checkout, then runs the equivalent of: ```sh export PATH="$PWD/.host/xbps/usr/bin:$PATH" export XBPS_ARCH=x86_64 cd vendor/void-packages ./xbps-src -A x86_64 binary-bootstrap ``` `binary-bootstrap` installs upstream **base-chroot** as a compilation environment. It is separate from the FDS `fds-base` target rootfs built in M1. Builds run through bubblewrap without starting Void services on Arch. Repeated bootstrap reuses the masterdir and verified downloads. The script checks the container architecture, installed base-chroot package, and execution of GCC inside the container. To reproduce the two builds individually from the repository root: ```sh ./tools/cargo-build --locked --offline --release \ --target aarch64-unknown-linux-musl -p fds-smoketest ./tools/build-package hello ``` The latter command sets the local XBPS PATH, checks the Void pin, and executes this command within the submodule: ```sh ./xbps-src -a aarch64 pkg hello ``` xbps-src installs the aarch64 GNU cross toolchain that runs on x86_64, together with the target glibc sysroot. No Pi compilation is needed. The pinned template's checksum verifies the package source archive. ## Validation ```sh file target/aarch64-unknown-linux-musl/release/fds-smoketest readelf -hW target/aarch64-unknown-linux-musl/release/fds-smoketest readelf -lW target/aarch64-unknown-linux-musl/release/fds-smoketest readelf -dW target/aarch64-unknown-linux-musl/release/fds-smoketest readelf -VW target/aarch64-unknown-linux-musl/release/fds-smoketest ldd target/aarch64-unknown-linux-musl/release/fds-smoketest ``` Rust acceptance requires ELF64, little endian, AArch64, executable type EXEC/DYN, static linkage reported by `file`, no `PT_INTERP`, no `DT_NEEDED`, and no `GLIBC_*` version requirements. Static PIE is also acceptable. The pinned Rust target, self-contained linking configuration, and compile-time target checks establish musl provenance; an ELF's static label alone does not identify its libc. `ldd` is supplemental diagnostic output. A static executable normally produces `not a dynamic executable` with exit status 1. **An x86_64 host's ldd alone cannot prove that an ARM executable is static.** XBPS validation checks architecture=aarch64 in the local package index, then extracts `usr/bin/hello` from the generated package. It requires AArch64, `/lib/ld-linux-aarch64.so.1`, and `libc.so.6`. An architecture suffix in a package filename is not sufficient evidence. `make check` verifies rejection of the wrong architecture, dynamic executables passed as static, a static-musl executable passed as a glibc package, non-ELF input, missing artifacts, invalid arguments, Void pin drift, and modified upstream files. It also tests overlay protection and runs rustfmt. Pipelines enable pipefail so `tee` cannot hide a failed build. Optionally install `sudo pacman -S --needed qemu-user` to execute the ARM Rust binary during smoke-test and compare its output. When qemu-aarch64 is absent, the script explicitly reports SKIP. M0's required acceptance is actual cross-compilation plus ELF/package inspection. Static inspection is not an ARM runtime or Pi boot test. ## Inputs, caches, and overlays - `.host/xbps/`: project-local host tools, not installed into `/usr`. - `vendor/void-packages/masterdir-x86_64/`: glibc build container. - `vendor/void-packages/hostdir/`: download caches, cross packages, and build output. - `out/packages/`: exported aarch64 test package and its repository index. - `out/manifests/void-build-packages.txt`: packages remaining installed after the build. Cross toolchains and temporary dependencies removed by xbps-src remain traceable through cache digests and build logs. - `out/manifests/void-package-inputs.sha256`: digests of cached `.xbps` inputs. - `out/manifests/artifacts.sha256`: validation artifact digests. - `out/logs/`: actual command logs, including logs from failed attempts. Place FDS package overlays at `packages/fds-*/template`. `tools/prepare-void` copies only directories containing a template and refuses to replace tracked upstream packages. These copies are visible inside the build container. Base, base-files, init, CLI, kernel and Dasung overlays are active; future directories without templates remain inactive. If an overlay changes and preparation reports a stale copy, inspect and preserve any unique edits before moving the specific generated copy out of `srcpkgs/` and retrying. Modified upstream tracked files cause immediate failure. Conflicting local build configuration also causes failure and must be reconciled explicitly. See [configuration and overlay troubleshooting](troubleshooting.md#configuration-or-overlay-conflict) for the procedure. Updating Void requires an explicit change to both the submodule gitlink and `VOID_PACKAGES_COMMIT`, followed by validation and a commit containing both. Do not fetch master and silently change the pin. Replacing the static host XBPS archive also requires updating both its versioned URL and checksum. ## Known limitations 1. Source templates, Rust, and XBPS tools are pinned; **the complete Void binary repository is not snapshotted**. Initial bootstrap, xbps-src build-container updates, and missing build dependencies still use signed rolling repositories. Cached inputs, version inventories, and digests support auditing but cannot guarantee future upstream retention. M12 preserves the exact selected packages, sources and build tools through the accepted [snapshot and offline workflow](reproducible-builds.md). Its two fresh builds produced 27 matching artifacts. This freezes the release inputs, not the entire rolling Void repository; the original M0 check alone does not establish reproducibility. 2. Bootstrap supports x86_64 Arch Linux. Other host distributions and ARM hosts have not been validated. Unprivileged user namespaces must be available. 3. M4 tests the actual Pi kernel and initramfs-to-EROFS handoff in a generic ARM VM. This does not emulate Pi firmware, RP1, physical bays or the display. Media burning and physical boot-performance measurements remain separate work. 4. `opt-level=z` is the plan's initial setting, not a proven fastest setting. Compare optimization levels later using real programs and hardware. 5. During the initial upstream bootstrap, bubblewrap's mounted `/tmp` produced `rm: cannot remove '//tmp': Device or resource busy` in the cleanup phase. Upstream returned success; the subsequent container execution, cross build, and public bootstrap command passed. The upstream source was left unchanged. ## Upstream references - [Pinned Void xbps-src usage and foreign host documentation](https://github.com/void-linux/void-packages/blob/02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1/README.md) - [Pinned aarch64 cross profile](https://github.com/void-linux/void-packages/blob/02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1/common/cross-profiles/aarch64.sh) - [Void static XBPS](https://docs.voidlinux.org/xbps/troubleshooting/static.html) - [Official static tool SHA-256 checksums](https://repo-default.voidlinux.org/static/sha256sums.txt) - [Rust aarch64-unknown-linux-musl](https://doc.rust-lang.org/rustc/platform-support/aarch64-unknown-linux-musl.html) - [Rust self-contained linking](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-self-contained)