diff --git a/PLAN.md b/PLAN.md index 41028cf..7f6aaf8 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,7 +1,7 @@ # AArch64 ELF-to-WebAssembly Browser Runtime ## Engineering Build Plan and Agent Handoff -**Status:** Phase 8 in progress; browser Cargo cancellation checkpoint complete +**Status:** Phase 8 complete; Phase 9 next **Primary implementation language:** Rust **Initial browser target:** Google Chrome **Guest architecture:** AArch64, little-endian, Linux userspace @@ -1905,7 +1905,9 @@ The offline Cargo dependency and persistent-cache checkpoint is complete. A chec Minimal Cargo build-script execution is now complete. The locked offline fixture uses a Rust `build.rs` child to read `OUT_DIR`, write generated Rust source, and send `cargo:rerun-if-changed` through Cargo's captured stdout; the final package includes that source in a normal static musl `std` binary, prints the dependency-backed result, and exits 42. A restored-snapshot replay reports both `itoa` and the root package as `Fresh` without rerunning the script, and the cached ELF passes again. Reaching the child added lane-wise NEON integer negation and unsigned widening add-across semantics with exact compiler/startup opcode regressions. The reproducible verifier gives the cold three-target compile a 1.5-billion-instruction bound while retaining a smaller cache-replay bound. Procedural macros, native dependencies, compiler-spawning or host-probing build scripts, and browser build cancellation remain the next Phase 8 work. -Browser Cargo cancellation is now covered by a reproducible Chromium verifier. It imports the completed toolchain/project/registry snapshot, completes a guest `cargo clean`, starts a real cold offline build, and terminates the active Worker while that build is running. Guest filesystem changes remain private to the Worker until a completed execution atomically replaces the OPFS snapshot; after cancellation, the replacement Worker becomes ready and the committed snapshot's size, modification time, and boundary digest remain unchanged. Restarting large compiler Workers is deferred to the next browser task so Chromium can reclaim the terminated Worker's Wasm state first, while the existing infinite-loop cancellation stays immediate. Procedural macros plus explicit compatibility diagnostics for native dependencies and compiler-spawning or host-probing build scripts remain the next Phase 8 work. +Browser Cargo cancellation is now covered by a reproducible Chromium verifier. It imports the completed toolchain/project/registry snapshot, completes a guest `cargo clean`, starts a real cold offline build, and terminates the active Worker while that build is running. Guest filesystem changes remain private to the Worker until a completed execution atomically replaces the OPFS snapshot; after cancellation, the replacement Worker becomes ready and the committed snapshot's size, modification time, and boundary digest remain unchanged. Restarting large compiler Workers is deferred to the next browser task so Chromium can reclaim the terminated Worker's Wasm state first, while the existing infinite-loop cancellation stays immediate. Explicit compatibility diagnostics for native dependencies and compiler-spawning or host-probing build scripts were the final Phase 8 acceptance gap. + +Phase 8's compatibility-policy checkpoint is complete. The Rust-only image installs small static AArch64 guard executables under the common C/C++ compiler, native discovery/build, shell, and host-probing command names. A build script that crosses the supported locked pure-Rust/minimal target-local tier now receives a stable `binarrow compatibility error` for either native dependencies or host probing instead of an ambiguous missing-executable failure; the verifier runs both guard classes through the same guest filesystem and `execve` path used by build-script children. The supported tier, minimal build scripts, offline registry cache, incremental persistence, successful source-to-ELF paths, and browser cancellation durability now satisfy every Phase 8 acceptance criterion. Procedural macros and actual native-dependency compilation remain intentionally outside this tier rather than blocking Phase 8; Phase 9 is next. Do not begin the full web IDE before item 30 passes. diff --git a/toolchains/rust-musl/README.md b/toolchains/rust-musl/README.md index d94cb9b..7374cc0 100644 --- a/toolchains/rust-musl/README.md +++ b/toolchains/rust-musl/README.md @@ -56,11 +56,17 @@ compiler output therefore cannot replace the last completed workspace. This checkpoint supports locked, pure-Rust dependencies that compile for the packaged `aarch64-unknown-linux-musl` host and minimal target-local build scripts that use the supported process, pipe, environment, and filesystem -surface. Procedural macros, host-probing build scripts, native library -discovery, C/C++ compilation, and dependencies that require network access are -not yet part of the compatibility contract. The intentionally minimal -registry overlay is a fixture for deterministic offline resolution, not a -general registry mirror. +surface. The Rust-only toolchain installs compatibility guards for common +native compilers, native discovery/build tools, shells, and host-probing tools. +If a build script invokes one, it exits with a stable `binarrow compatibility +error` explaining which compatibility tier was exceeded instead of surfacing +an ambiguous missing-program error. The verifier executes representatives of +both guard classes through the same guest `execve` path used by build scripts. + +Procedural macros, host-probing build scripts, native library discovery, C/C++ +compilation, and dependencies that require network access are not yet part of +the compatibility contract. The intentionally minimal registry overlay is a +fixture for deterministic offline resolution, not a general registry mirror. Rustc's bounded cooperative thread topology includes its signal waiter, compiler worker, nested helpers, and coordinator/worker pair. Futex wait/wake, diff --git a/toolchains/rust-musl/build.sh b/toolchains/rust-musl/build.sh index 79f8451..54e92e0 100755 --- a/toolchains/rust-musl/build.sh +++ b/toolchains/rust-musl/build.sh @@ -18,6 +18,8 @@ rust_tmp=$workspace_directory/.tmp/rust-tmp compiler_rt=$work_directory/compiler-rt.a exported_compiler_rt=$work_directory/compiler-rt-exported.a libgcc_compat=$work_directory/libgcc-compat.o +native_dependency_guard=$work_directory/native-dependency-guard +host_probe_guard=$work_directory/host-probe-guard dist_url=https://static.rust-lang.org/dist/2026-01-22 rustc_archive=rustc-1.93.0-aarch64-unknown-linux-musl.tar.xz rustc_sha256=5371915850179d910d3eca32cb8f9240c336a1fdc830286242daf4b26227295f @@ -58,6 +60,8 @@ mkdir -p \ "$archive_directory" \ "$extract_directory" \ "$image_root" \ + "$image_root/bin" \ + "$image_root/usr/local/bin" \ "$os_cache_directory" \ "$os_temp_directory" \ "$zig_cache_directory/local" \ @@ -156,6 +160,29 @@ find "$image_root/usr/local/lib" -type f -name '*.so' \ "$llvm_objcopy" --strip-all "$image_root/usr/local/lib/libgcc_s.so.1" "$llvm_objcopy" --strip-all \ "$image_root/usr/local/lib/rustlib/aarch64-unknown-linux-musl/bin/rust-lld" + +"$zig" cc \ + -target aarch64-linux-musl \ + -static \ + -Os \ + -s \ + "$toolchain_directory/native-dependency-guard.c" \ + -o "$native_dependency_guard" +for tool in cc c++ gcc g++ clang clang++ pkg-config cmake make; do + cp "$native_dependency_guard" "$image_root/usr/local/bin/$tool" +done +"$zig" cc \ + -target aarch64-linux-musl \ + -static \ + -Os \ + -s \ + "$toolchain_directory/host-probe-guard.c" \ + -o "$host_probe_guard" +for tool in sh bash uname git; do + cp "$host_probe_guard" "$image_root/usr/local/bin/$tool" +done +cp "$host_probe_guard" "$image_root/bin/sh" + rm -f \ "$image_root/usr/local/bin/rustdoc" \ "$image_root/usr/local/bin/rust-gdb" \ diff --git a/toolchains/rust-musl/host-probe-guard.c b/toolchains/rust-musl/host-probe-guard.c new file mode 100644 index 0000000..92e6067 --- /dev/null +++ b/toolchains/rust-musl/host-probe-guard.c @@ -0,0 +1,9 @@ +#include + +int main(void) { + static const char message[] = + "binarrow compatibility error: host-probing build-script commands are " + "unsupported by the browser Rust toolchain\n"; + (void)write(STDERR_FILENO, message, sizeof(message) - 1); + return 86; +} diff --git a/toolchains/rust-musl/native-dependency-guard.c b/toolchains/rust-musl/native-dependency-guard.c new file mode 100644 index 0000000..a80e525 --- /dev/null +++ b/toolchains/rust-musl/native-dependency-guard.c @@ -0,0 +1,9 @@ +#include + +int main(void) { + static const char message[] = + "binarrow compatibility error: native C/C++ dependencies are " + "unsupported by the Rust-only toolchain\n"; + (void)write(STDERR_FILENO, message, sizeof(message) - 1); + return 86; +} diff --git a/toolchains/rust-musl/verify.sh b/toolchains/rust-musl/verify.sh index d3164da..5fc26bd 100755 --- a/toolchains/rust-musl/verify.sh +++ b/toolchains/rust-musl/verify.sh @@ -60,6 +60,49 @@ if [ "$version" != "rustc 1.93.0 (254b59607 2026-01-19)" ]; then exit 1 fi +verify_compatibility_guard() { + tool_path=$1 + expected_diagnostic=$2 + set +e + guard_output=$( + "$runner" run \ + --instruction-budget 1000000 \ + --syscall-budget 200 \ + --memory-limit 67108864 \ + --filesystem-limit 838860800 \ + --filesystem-install "$system_image" \ + --filesystem-install "$filesystem_image" \ + --argv0 "$tool_path" \ + "$exec_helper" "$tool_path" 2>&1 + ) + guard_status=$? + set -e + if [ "$guard_status" -ne 86 ]; then + echo "compatibility guard exited with status $guard_status" >&2 + echo "$guard_output" >&2 + exit 1 + fi + case "$guard_output" in + *"$expected_diagnostic"*) ;; + *) + echo "missing compatibility diagnostic: $expected_diagnostic" >&2 + echo "$guard_output" >&2 + exit 1 + ;; + esac +} + +verify_compatibility_guard \ + /usr/local/bin/cc \ + "binarrow compatibility error: native C/C++ dependencies are unsupported by the Rust-only toolchain" +verify_compatibility_guard \ + /bin/sh \ + "binarrow compatibility error: host-probing build-script commands are unsupported by the browser Rust toolchain" +if [ "${BINARROW_VERIFY_COMPATIBILITY_ONLY:-0}" = 1 ]; then + echo "AArch64 Rust compatibility guard fixtures passed" + exit 0 +fi + "$runner" image pack --guest-root /project "$project_directory" "$project_image" "$runner" run \ --instruction-budget 150000000 \