chore(bazel): replace hermetic_cc_toolchain (zig cc) with hermetic-llvm - #10991
Conversation
Swap the hermetic C/C++ toolchain from Uber's hermetic_cc_toolchain 4.3.0 (zig cc, consumed via an archive_override carrying three local patches) to the BCR module `llvm` 0.8.16 (https://github.com/hermeticbuild/hermetic-llvm), a prebuilt clang/lld (LLVM 22.1.8) zero-sysroot cross toolchain. Toolchain wiring: * hermetic-llvm's extension has no extra_target_settings support, so the toolchains are registered through wrapper toolchain()s in //bazel that are additionally gated on //bazel:use_hermetic_cc. --hermetic_cc=false (and thus --config=afl's host-toolchain fallback) keeps working unchanged. * macOS goes hermetic too: hermetic-llvm downloads the macOS SDK from Apple's CDN, removing the old blocker for hermetic Darwin toolchains. This also keeps wasm/canister builds working on macs, which previously relied on zig's exec-unconstrained none_wasm toolchain. * The default glibc floor moves from gnu.2.31 (zig) to hermetic-llvm's default gnu.2.28. Zig-specific debt removed: * hermetic_cc_toolchain_strip.patch: clang emits deterministic debug info; dropping rust.strip_level_select restores the rules_rust default (opt = -Cstrip=debuginfo), which keeps symbols and sheds DWARF without the out-of-band host `strip -S`. * hermetic_cc_toolchain_cache_dir.patch: clang/lld has no compile cache, so the zig cache race workaround and ALL /tmp/zig-cache plumbing (bazelrc repo_env/sandbox mounts, dev container mounts, CI mkdirs, bazel_clean.sh) are deleted with no replacement. * hermetic_cc_toolchain_lto.patch: upstream lld does not warn on rustc's -Wl,-O1 (that was zig's lld fork). * cc_rs.patch: cc-rs's standard LLVM triples are correct for clang; the zig triple rewriting is gone (crate annotation removed + repin). Fallout fixed along the way: * rs/canister_sandbox's backtrace.c includes <libunwind.h>, which zig bundled but zero-sysroot clang does not: the new //bazel:libunwind_include (via bazel/cc_headers_dir.bzl) exposes @llvm-project//libunwind headers to the cc-rs build script; LLVM libunwind is statically linked by the toolchain's runtime libs, so -lunwind resolves with no new runtime dep. * aws-lc-sys 0.42.0's memcmp compiler probe ignores CFLAGS and cannot compile under a zero-sysroot toolchain; aws-lc-rs 1.17.3 / aws-lc-sys 0.43.0 skip the probe for clang (the bug it probes for is GCC-specific), so bump instead of patching. * rustc 1.97's linker_messages lint surfaces benign clang driver chatter (`--unwindlib=none` unused); allow the lint. A driver-level -Wno- flag is not an option since wasm targets link through rust-lld directly. * rules_cc 0.2.18 -> 0.2.22 and rules_go 0.61.1 -> 0.62.0, matching what MVS resolves with llvm@0.8.16 in the graph. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the Bazel C/C++ hermetic toolchain from Uber’s hermetic_cc_toolchain (zig cc) to hermetic-llvm (llvm BCR module 0.8.16), updates supporting Bazel/Rust wiring, and removes all zig-cache plumbing.
Changes:
- Replace zig-based hermetic toolchain registration with hermetic-llvm toolchain extension + wrapper
toolchain()targets gated by//bazel:use_hermetic_cc. - Remove
/tmp/zig-cachemounts/setup and delete now-unneeded zig-specific patches/config. - Add a small Bazel rule to expose
libunwindheaders to cc-rs build scripts and bumpaws-lc-*to versions that work under the new toolchain assumptions.
Reviewed changes
Copilot reviewed 21 out of 25 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| third_party/BUILD.libvirt.bazel | Updates rationale/comments for --allow-shlib-undefined under the new hermetic toolchain context. |
| third_party/BUILD.libsystemd.bazel | Updates rationale/comments for --allow-shlib-undefined under the new hermetic toolchain context. |
| third_party/BUILD.e2fsprogs.bazel | Simplifies compatibility comment (tools are Linux-only in practice). |
| rs/canister_sandbox/BUILD.bazel | Adds CFLAGS include path + data dep to supply libunwind.h for cc-rs compilation. |
| MODULE.bazel | Swaps module deps to llvm@0.8.16, bumps rules_cc/rules_go, and reworks toolchain extension setup/registration. |
| ci/container/init.sh | Removes creation/permissioning of /tmp/zig-cache. |
| ci/container/Dockerfile | Updates linker-related comment to reflect clang/lld toolchain; keeps ld.lld symlink. |
| ci/container/container-run.sh | Removes zig-cache bind mount and directory creation. |
| Cargo.lock | Bumps aws-lc-rs/aws-lc-sys versions/checksums. |
| Cargo.Bazel.toml.lock | Mirrors the aws-lc-* version/checksum bumps for Bazel cargo integration. |
| Cargo.Bazel.json.lock | Updates crate metadata/links for aws-lc-* and removes cc patching entries. |
| bazel/rust.MODULE.bazel | Removes zig-era strip override and cc crate patch; updates sanitizer comment. |
| bazel/hermetic_cc_toolchain_strip.patch | Deletes zig-toolchain determinism workaround patch. |
| bazel/hermetic_cc_toolchain_lto.patch | Deletes zig-lld warning suppression patch. |
| bazel/hermetic_cc_toolchain_cache_dir.patch | Deletes zig-cache race workaround patch. |
| bazel/fuzz_testing.bzl | Updates sanitizer comment to reflect clang toolchain context. |
| bazel/conf/.bazelrc.build | Removes zig-cache-related sandbox/cache config; adds rustc lint allow + macOS archiver flag. |
| bazel/cc_rs.patch | Deletes zig target-triple rewrite patch for cc-rs. |
| bazel/cc_headers_dir.bzl | Introduces rule to materialize headers into a directory artifact for build scripts. |
| bazel/BUILD.bazel | Adds libunwind_include target and wrapper hermetic-llvm toolchains gated on :use_hermetic_cc. |
| bazel/bazel_clean.sh | Removes cleanup of /tmp/zig-cache. |
| .github/workflows/ci-main.yml | Removes CI step creating /tmp/zig-cache. |
| .github/actions/bazel/bin/bazel | Removes zig-cache setup from the GitHub Action wrapper. |
| .devcontainer/devcontainer.json | Removes zig-cache directory creation and bind mount. |
| .claude/skills/run-in-dev-container/SKILL.md | Updates documentation to remove zig-cache mention. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…a8e56c181e7b2ff9fa5b4d2a4f7ec ic-build: sha256:2b18bc15d1c2f503e125cf52a2bfda9f2bcd2f85d9bb34a600834fc69e18b76f ic-dev: sha256:162a908c48e6bdf0611077cc252ebe53ccb76f8e37c724e82e3cf847d38c4ea0
|
Run URL: https://github.com/dfinity/ic/actions/runs/30750926226 New container images with tag: |
…cOS wasm-only
Address the CI failures of the hermetic-llvm migration and the Copilot
review comment.
Host -L leaks (Bazel Test All / Build IC / repro-check failures):
cargo build scripts that probe host libraries via pkg-config emit the host
library dir (-L/usr/lib/x86_64-linux-gnu) ahead of the hermetic toolchain's
library search paths. lld then resolves `-lc` against the HOST glibc while
the crt objects come from the hermetic glibc — an incompatible mix
(undefined __libc_csu_init/__libc_csu_fini, host GLIBC_ symbol versions).
zig cc was immune because it resolved libc internally, ignoring -L order.
Fixes, per crate:
* pcre2-sys: build the bundled pcre2 (PCRE2_SYS_STATIC=1) — strictly more
hermetic than linking the host pcre2.
* libusb1-sys: build the bundled libusb ("vendored" feature); disable the
libudev probe so it uses the netlink hotplug backend.
* devicemapper-sys, libcryptsetup-rs-sys, libsystemd-sys: genuinely host
libraries — link them by explicit path via cc_import host-import repos
(the existing @libsystemd pattern; new @DevMapper and @cryptsetup repos),
and route the pkg-config probe through a wrapper
(bazel/pkg_config_no_link_metadata.sh) that strips -L/-l from its output
while keeping version checks and -I include paths for bindgen.
* libcryptsetup-rs, virt-sys: probe-only build scripts; same wrapper.
glibc floor (planned contingency): with the host dirs gone, links surfaced
`undefined symbol: gettid` — gettid is glibc 2.30, hermetic-llvm defaults
to a 2.28 sysroot. Pin gnu.2.31 (parity with the zig toolchains) via
//:linux-glibc-2.31 (host-parented platform) and build:linux
--platforms/--host_platform. Verified: hermetic toolchain resolves on the
pinned platform, --hermetic_cc=false still falls through to the local
toolchain, and produced binaries stay at the 2.31 symbol floor.
macOS (Bazel Test arm64-darwin failure): hermetic-llvm's macOS support
(trimmed Apple SDK sysroot, resource-dir handling) is not yet mature enough
for the full darwin build — configure-based cargo build scripts
(tikv-jemalloc-sys) fail to find SDK headers. Scope the hermetic macOS
toolchains down to wasm32 (canister) targets only, which need no SDK; native
macOS targets stay on the system Xcode toolchain exactly as before this
migration. The arm64-linux job was fail-fast collateral of the darwin
failure.
Also reword the ci/container/Dockerfile lld comment to cover the
--hermetic_cc=false fallback (Copilot review).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 47 changed files in this pull request and generated no new comments.
Suppressed comments (2)
bazel/BUILD.bazel:89
- The wrapper toolchains construct OS constraint labels from the string "macos" (via "@platforms//os:" + exec_os/target_os). Elsewhere in the repo, macOS constraints are consistently referenced as
@platforms//os:osx(e.g.//:macos-intelin BUILD.bazel), so these toolchains may not match on Darwin and wasm-on-mac toolchain fallback could break.
exec_compatible_with = [
"@platforms//os:" + exec_os,
"@platforms//cpu:" + exec_cpu,
],
target_compatible_with = [
"@platforms//os:" + target_os,
"@platforms//cpu:" + target_cpu,
],
MODULE.bazel:1387
- The comment "Use libvirt from the host environment" now sits above the
cryptsetup/devmapperrepositories, which is misleading and makes it harder to understand why these repos are declared. Consider adding a dedicated comment for cryptsetup/devmapper and keeping the libvirt comment directly above the libvirt repo.
# Use libvirt from the host environment
# Used for managing virtual machines programmatically.
new_local_repository(
name = "cryptsetup",
build_file = "@//third_party:BUILD.cryptsetup.bazel",
path = "/usr",
)
…0097cae40fa542b56e2c891b30a66 ic-build: sha256:dd6ecee0bd582a060c658698602df96215f70885d88e702f47d35adf1e6938ab ic-dev: sha256:6368c561a3e2bf9a6765ff7978f4ea5f742e8ee27c4c957cbaad2c5c078661a7
…s it
hermetic-llvm links with --unwindlib=none and stubs out libgcc_s and
libunwind with empty archives: the only provider of the _Unwind_*
symbols rust std needs is the toolchain's static runtime
(cc_toolchain.static_runtime_lib = LLVM's libc++/libc++abi/libunwind
archives). rules_rust drops that runtime in two places — zig cc was
immune because it always injected its own unwinder. Both are fixed by
a patch on the rules_rust fork (bazel/rules_rust_static_runtime.patch,
to be upstreamed to dfinity/rules_rust and dropped on the next bump):
1. Every rust_doc_test failed to link with "undefined symbol:
_Unwind_Resume" (~34 of the 50 Bazel Test All failures, and the
single arm64-linux failure): rustdoc test links pass
include_link_flags = False, which also skipped the static runtime's
-lstatic flags, and the doc-test writer didn't rewrite the runtime's
-Lnative search paths (which live under the toolchain's
configuration root) into runfiles-relative form.
2. rust_shared_library (cdylib) targets silently linked no unwinder —
the cause of all 17 failing system tests (canister_http_*,
firewall_*, api_bn_decentralization_*): rules_rust links the
*dynamic* C++ runtime into dylib/cdylib targets, and hermetic-llvm's
dynamic runtime consists of versioned files (libunwind.so.1) that
-lunwind never matches, so lld silently fell back to the empty stub
archive (shared-library links don't error on undefined symbols). At
runtime, dlopen of libnss_icos.so failed with "undefined symbol:
_Unwind_GetIP", GuestOS could not resolve the "hostos" hostname
(provided by that NSS plugin), nft -f rejected the
orchestrator-generated firewall ruleset (it whitelists "hostos" by
name in "ip6 saddr { hostos }"), and the ip6 filter table never
appeared — which is exactly what those tests wait for. Fixed by
preferring the static runtime for dylib/cdylib links whenever the
toolchain provides one; local and Xcode toolchains provide none and
keep their current dynamic-runtime behavior.
Verified locally: all 58 rust_doc_test targets pass; libnss_icos.so
dlopens cleanly with all 7 NSS symbols exported and no undefined
_Unwind_*; canister_http_correctness_test, firewall_priority_test and
api_bn_decentralization_test pass end-to-end on Farm; the ledger
canister and pocket-ic builds are pure action-cache hits (the patch
changes no non-cdylib, non-doc-test link); bazel build //... --nobuild
and buildifier are clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 44 out of 48 changed files in this pull request and generated no new comments.
Suppressed comments (2)
bazel/cc_headers_dir.bzl:24
- The run_shell script doesn't enable
set -e/pipefail, so a failedcp/mkdircould be silently ignored and the action would still succeed with an incomplete header directory. Addingset -euo pipefailmakes failures propagate and avoids hard-to-debug missing-header errors later in the build.
command = """
out="$1"; strip="$2"; shift 2
for f in "$@"; do
MODULE.bazel:1382
- This comment block now sits above the
cryptsetup/devmappernew_local_repository()declarations, so it no longer accurately describes the code that follows (it reads as ifcryptsetupis libvirt). Updating the comment to cover all the host-provided libraries here will avoid confusion when editing this section later.
# Use libvirt from the host environment
# Used for managing virtual machines programmatically.
With -Zsanitizer=address, rustc automatically links its bundled ASan runtime (librustc-stable_rt.asan.a). The zig toolchain mishandled that mechanism, so DEFAULT_SANITIZERS suppressed it with -Zexternal-clangrt and manually re-added the same archive via a -Clink-arg with a hardcoded path — one that baked in the compilation mode (k8-opt), the bzlmod canonical repo name of rules_rust, the toolchain channel and the target arch, breaking silently whenever any of those changed. With a plain clang/lld driver the default mechanism just works, so both flags go away and only -Zsanitizer=address remains. Verified: a libfuzzer target builds with exactly one ASan runtime (nm: one defined __asan_init, none undefined) and runs 100 iterations with live coverage feedback; an AFL target builds green (that path never used DEFAULT_SANITIZERS at link — afl-clang-lto brings the host ASan runtime); bazel build //... --nobuild and buildifier are clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 49 changed files in this pull request and generated no new comments.
Suppressed comments (1)
bazel/cc_headers_dir.bzl:30
- The
run_shellscript doesn't enableset -e, so a failedmkdir/cpinside the loop could be ignored and the action might still exit 0 (depending on the last command executed). This could silently produce an incomplete headers directory and lead to confusing downstream compile failures. Consider enablingset -euat the top of the script so the action fails immediately on errors.
command = """
out="$1"; strip="$2"; shift 2
for f in "$@"; do
rel="${f#*"$strip"}"
[ "$rel" = "$f" ] && continue
mkdir -p "$out/$(dirname "$rel")"
cp -L "$f" "$out/$rel"
done
""",
The if/else was buffering pkg-config's output only to suppress it on failure and re-raise the status by hand. `pipefail` already propagates a failing pkg-config through the pipeline, and pkg-config writes nothing to stdout when it fails, so the buffering bought nothing. Dropping the command substitution also stops normalizing the trailing newline, which the `printf '%s\n'` was re-adding even for modes that emit zero bytes. The pkg_config crate only ever runs `--libs --cflags` and `--modversion`, all of which emit a trailing newline, so the output is byte-identical for every invocation this wrapper actually sees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The only libusb consumer is vsock_lib's rusb dependency (the HSM-attach flow). The Bazel build already compiles the bundled libusb (vendored, netlink hotplug backend) since the hermetic-llvm migration, and cargo builds now do the same: the `vendored` feature is enabled on rusb in both vsock_lib's Cargo.toml (for cargo) and the rusb crate.spec (for Bazel; feature resolution then puts it on libusb1-sys, replacing the crate_features annotation). With neither build probing the system library, the container package can go. Verified: the lockfile resolves libusb1-sys with the vendored feature; vsock_host links libusb statically with no dynamic libusb NEEDED; `cargo check --all-targets --all-features -p vsock_lib` passes (libusb1-sys's build.rs also falls back to the bundled source on its own when pkg-config finds nothing, so cargo builds keep working even without the feature); bazel build //... --nobuild and buildifier are clean. Cargo.lock is unchanged (features don't affect it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Would
|
## Problem `repro-check --guestos` fails for every commit since 9e29205 (the zig cc → hermetic-llvm switch, dfinity#10991): the locally built GuestOS `update-img.tar.zst` hashes differently from the CI/CDN artifact (e.g. local `fc824312…` vs CDN `2f31b0e5…` at 9e29205), while being perfectly stable across rebuilds on the same machine. ## Root cause 9e29205 set `PCRE2_SYS_STATIC=1` on `pcre2-sys` (to fix the host-library `-L` leak class under the hermetic toolchain), which makes the crate's build script compile the bundled PCRE2 C sources. That `build.rs` enumerates the sources with an **unsorted `std::fs::read_dir`**, so the member order of the produced `libpcre2.a` is the build machine's filesystem iteration order — stable per machine, but different across machines (ext4 iterates in name-hash order with a per-filesystem seed chosen at mkfs time). The order leaks into `open_rootfs` (the only pcre2 consumer shipped in an IC-OS image, via `partition_tools`): lld records one `STT_FILE` symbol per loaded archive member, in load order. All pcre2 *code* is GC'd (`--gc-sections` — nothing in `open_rootfs` calls it), so the binaries differ **only** in the order of their `pcre2_*.c` `STT_FILE` symtab entries and the corresponding `.strtab` bytes. Diffing the local vs CDN images file-by-file: `/opt/ic/bin/open_rootfs` is the **only** differing file in the entire rootfs (boot partition differences are the cascade: the initramfs embeds `open_rootfs`, `boot_args` carries the root's verity hash, and `launch-measurements.json` follows). Why the "Build Determinism" job didn't catch it: (a) on 9e29205 (and its parent) it was **skipped** — `needs: [build-ic, bazel-test-all]` with no `if:`, and Bazel Test All's *test* step failed after the artifacts had already been uploaded; (b) even when it runs (verified green on later master commits with the defect present), it compares two builds within the uniformly-provisioned dind fleet, which agrees with itself on readdir order — only a build outside the fleet exposes the difference. ## Fix Patch the crate's `build.rs` to collect and sort the sources before handing them to `cc::Build` (`bazel/pcre2-sys.patch`, wired via `crate.annotation` like the existing `rustix`/`askama` determinism patches, lockfile repinned via `./bin/bazel-pin.sh`). Same file set, deterministic order → the archive is byte-identical regardless of the build machine. Upstream PR: BurntSushi/rust-pcre2#58 ## Verification - `patch -p1` applies cleanly against pristine pcre2-sys 0.2.8; `bazel build //... --nobuild` and `bazel run //:buildifier` pass. - `bazel build --config=local //rs/ic_os/open_rootfs:open_rootfs` in the dev container: the rebuilt `libpcre2.a` member order is sorted (`ar t | sort -c`), i.e. no longer filesystem-dependent (before the patch it matched the machine's raw readdir order 1:1). - Definitive confirmation will be the first `repro-check --guestos` against a CI build that includes this commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Replaces Uber's
hermetic_cc_toolchain4.3.0 (zig cc) with hermetic-llvm — BCR modulellvm0.8.16, a prebuilt clang/lld (LLVM 22.1.8) zero-sysroot cross toolchain — and removes every/tmp/zig-cachereference.Why
hermetic_cc_toolchainrequired a zig cache (/tmp/zig-cache) which proved problematic and had to be patched. A newer version fixed the need for the patch but it failed because of another reason on RBE.Additionally
zig ccrequired four local patches and a pile of plumbing that all disappear with a plain clang/lld toolchain:hermetic_cc_toolchain_strip.patch(out-of-band hoststrip -Sfor determinism) +rust.strip_level_select(none)opt = -Cstrip=debuginfokeeps symbols, sheds DWARFhermetic_cc_toolchain_cache_dir.patch(zig cache race under RBE) + all/tmp/zig-cachemounts/mkdirshermetic_cc_toolchain_lto.patch(zig's lld fork warned on rustc's-Wl,-O1)-O1silentlycc_rs.patch(rewrote LLVM triples into zig naming)Toolchain wiring
extra_target_settings, so registration goes through wrappertoolchain()s in//bazelgated on the existing//bazel:use_hermetic_cc.--hermetic_cc=false/--config=aflbehave exactly as before (verified with--toolchain_resolution_debug: wrappers rejected → falls through tolocal_config_cc).none_wasmtoolchain had no exec constraints, so canister builds on macs relied on it; themacos_*→none_wasm32wrappers keep that working (wasm needs no Apple SDK). Native macOS targets stay on the system Xcode toolchain: hermetic-llvm's macOS support (trimmed SDK sysroot, resource-dir handling) is not yet mature enough for the full darwin build — the first CI round failed intikv-jemalloc-sys's configure-based build script (math.hnot found).gnu.2.31(zig parity) via//:linux-glibc-2.31(a@platforms//host-parented platform) +build:linux --platforms/--host_platform. The 2.28 default was tried first per plan, butgettid(glibc 2.30) users inrs/ic_oshit the documented contingency.-Lleak class (found by CI): cargo build scripts probing host libs via pkg-config inject-L/usr/lib/x86_64-linux-gnuahead of the hermetic search paths, making-lcresolve to the host glibc while crt objects come from the hermetic one (undefined __libc_csu_init/__libc_csu_fini). zig was immune (it resolved libc internally). Fixed per crate: pcre2-sys and libusb1-sys now build their bundled C sources (more hermetic than before); devicemapper-sys / libcryptsetup-rs-sys / libsystemd-sys link the host library by explicit path viacc_importhost-import repos (the existing@libsystemdpattern) with a pkg-config wrapper (bazel/pkg_config_no_link_metadata.sh) that strips-L/-lfrom probe output while keeping version checks and bindgen include paths; virt-sys / libcryptsetup-rs are probe-only and just get the wrapper.Missing-unwinder link class (found by CI, round 2)
hermetic-llvm links with
--unwindlib=noneand stubs outlibgcc_s/libunwindwith empty archives; the only provider of the_Unwind_*symbols rust std needs is the toolchain's static runtime (cc_toolchain.static_runtime_lib= LLVM's libc++/libc++abi/libunwind archives). rules_rust drops that runtime in two places — zig was immune because zig cc always injected its own unwinder. Both fixed by a patch on the rules_rust fork (bazel/rules_rust_static_runtime.patch; TODO: upstream to dfinity/rules_rust and drop on the next fork bump):rust_doc_testfailed to link (undefined symbol: _Unwind_Resume) — ~34 of the 50 "Bazel Test All" failures and the single arm64-linux failure. rustdoc test links passinclude_link_flags = False, which also skipped the static runtime's-lstaticflags; the doc-test writer additionally didn't rewrite the runtime's-Lnativesearch paths (which live under the toolchain's configuration root) into runfiles-relative form for the test-time link.rust_shared_librarysilently linked no unwinder — the actual cause of all 17 failing system tests (canister_http_*,firewall_*,api_bn_decentralization_*). rules_rust links the dynamic C++ runtime into dylib/cdylib targets, and hermetic-llvm's dynamic runtime consists of versioned files (libunwind.so.1) that-lunwindnever matches, so lld silently fell back to the empty stub archive (shared-library links don't error on undefined symbols). At runtime: dlopen oflibnss_icos.sofailed withundefined symbol: _Unwind_GetIP→ GuestOS couldn't resolve thehostoshostname (provided by that NSS plugin) →nft -frejected the orchestrator-generated ruleset (it whitelistshostosby name:ip6 saddr { hostos }) → theip6 filtertable never appeared, which is exactly what those tests wait for. Diagnosed by SSHing into a keepalive testnet node:reload_nftables.servicein a restart loop withError: Could not resolve hostname. Fix: prefer the static runtime for dylib/cdylib links whenever the toolchain provides one (local/Xcode toolchains provide none and keep their dynamic-runtime behavior).Fallout fixed along the way
rs/canister_sandbox'sbacktrace.cincludes<libunwind.h>(zig bundled it): new//bazel:libunwind_include(viabazel/cc_headers_dir.bzl) exposes@llvm-project//libunwindheaders to the cc-rs build script. LLVM libunwind is statically linked by the toolchain runtime libs — verified no new runtime.sodependency.CFLAGSand can't compile under a zero-sysroot toolchain. Bumped aws-lc-rs 1.17.3 / aws-lc-sys 0.43.0, where the probe is (correctly) skipped for clang — the miscompile it probes for is GCC-specific.linker_messageslint surfaced benign clang driver chatter on every link; the lint is allowed in bazelrc (linker errors still fail the build).libusb-1.0-0-devdropped from the dev container: the only consumer (vsock_lib's rusb, for HSM attach) now builds the bundled libusb in both build systems — thevendoredfeature is enabled on the rusbcrate.spec(Bazel) and in vsock_lib's Cargo.toml (cargo). Verified: vsock_host links libusb statically with no dynamic libusbNEEDED;cargo check -p vsock_libpasses.DEFAULT_SANITIZERSdropped the zig-era-Zexternal-clangrt+ hardcoded-Clink-arg=…librustc-stable_rt.asan.apair — rustc's default mechanism links the same bundled ASan runtime itself under clang/lld, without a path that baked in the compilation mode, the rules_rust canonical repo name, the toolchain channel and the arch. Verified: libfuzzer target links exactly one ASan runtime (nm: one defined__asan_init) and runs 100 iterations; AFL target builds green (unaffected by design).rules_cc0.2.18 → 0.2.22,rules_go0.61.1 → 0.62.0 (matching MVS resolution withllvm@0.8.16in the graph).🤖 Generated with Claude Code