Skip to content

tsan: support macOS arm64 hosts alongside Linux x86_64 - #7238

Merged
danlapid merged 1 commit into
mainfrom
dlapid/tsanMacos
Sep 4, 2026
Merged

tsan: support macOS arm64 hosts alongside Linux x86_64#7238
danlapid merged 1 commit into
mainfrom
dlapid/tsanMacos

Conversation

@danlapid

@danlapid danlapid commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #7214. Generalizes the TSan configuration so it also runs on Apple Silicon Macs (where most of us develop); Linux behavior is unchanged.

What changes

  • sanitizer_thread_platform inherits the host (like the asan platform) instead of pinning linux/x86_64.
  • The Rust toolchain becomes a tsan_rust_toolchain macro (build/rust/tsan_toolchain.bzl) instantiated for Linux x86_64 (attributes unchanged) and macOS arm64 (mirroring rules_rust's generated aarch64-apple-darwin toolchain). --extra_toolchains lists both; a select() alias keeps //build/rust:tsan_rust_std working for tsan-test.
  • .bazelrc: portable flags move to tsan-common; --config=tsan (Linux) adds the GNU-ld-only -l:libunwind.so.8 and is otherwise identical to before; --config=tsan-macos is the macOS entry point. bazelrc can't make one linkopt OS-conditional, hence the two entry points.

Three issues found while porting (the first affects Linux too)

  1. Hermeticity: cargo reads config.toml from every ancestor of the sandbox, which lives under $HOME on both platforms (~/.cache/bazel, ~/Library/Caches/bazel) — my ~/.cargo/config.toml's rustc-wrapper = "sccache" broke the build-std action. RUSTC_WRAPPER/RUSTC_WORKSPACE_WRAPPER are now explicitly cleared.
  2. rustc passes -nodefaultlibs to the linker driver; Darwin's clang then also omits the sanitizer runtime that -Zexternal-clangrt relies on it to supply (Linux clang adds it regardless). The macOS toolchain adds -Cdefault-linker-libraries=yes so every Rust-driven link gets libclang_rt.tsan_osx_dynamic.dylib — sharing Clang's runtime with C++, as on Linux. The build-std script's throwaway test binary links Rust's own runtime instead for the same reason (link-only flag; rlibs unaffected).
  3. build_std.sh used bash-4-only ${var^^}/mapfile and GNU realpath -m; macOS ships bash 3.2. Now portable. The runtime-library filter matches the librustc-nightly_rt.tsan.* stem (.a on Linux, .dylib on macOS).

Verified on macOS arm64: bazel test --config=tsan-macos //src/rust/tsan-test/... //src/rust/cxx/kj-rs/tests/... — 7/7 pass; the binaries carry __tsan_* symbols and link libclang_rt.tsan_osx_dynamic.dylib; tsan-integration-test confirms the rebuilt stdlib is instrumented and a racy Rust binary is detected. Not re-verified on Linux (no x86_64 box here) — the Linux net flags and toolchain attributes are unchanged, but a CI/local run there would be welcome.

🤖 Generated with Claude Code

@danlapid
danlapid requested review from a team as code owners September 3, 2026 22:31
Generalizes the ThreadSanitizer configuration so it also runs on Apple Silicon
Macs, where most workerd development happens. Linux behavior is unchanged.

- The tsan platform inherits the host (like the asan one) instead of pinning
  linux/x86_64, and the single Rust toolchain becomes a tsan_rust_toolchain
  macro instantiated per host: Linux x86_64 (unchanged attributes) and macOS
  arm64 (mirroring rules_rust's generated aarch64-apple-darwin toolchain).
  --extra_toolchains lists both; resolution picks the host's. A select() alias
  keeps //build/rust:tsan_rust_std working for tsan-test.
- .bazelrc: the portable flags move to `tsan-common`; `--config=tsan` (Linux)
  adds the GNU-ld-only unwinder linkopt and is otherwise identical to before;
  `--config=tsan-macos` is the macOS entry point. bazelrc cannot make a single
  linkopt OS-conditional, hence two entry points.
- rustc passes -nodefaultlibs to the linker driver, and unlike Linux, Darwin's
  clang then also omits the sanitizer runtime that -Zexternal-clangrt expects
  it to supply. The macOS toolchain adds -Cdefault-linker-libraries=yes so
  every Rust-driven link gets libclang_rt.tsan_osx_dynamic.dylib, sharing
  Clang's runtime with C++ exactly as on Linux. For the same reason the
  build-std script's throwaway test binary links Rust's own runtime instead
  (the flag is link-only and never affects the rlibs we keep).
- build_std.sh hermeticity: cargo reads config.toml from every ancestor of the
  sandbox, which lives under $HOME on both Linux (~/.cache/bazel) and macOS
  (~/Library/Caches/bazel), so a developer's ~/.cargo/config.toml leaked in
  (observed: rustc-wrapper = "sccache"). RUSTC_WRAPPER and
  RUSTC_WORKSPACE_WRAPPER are now explicitly cleared.
- build_std.sh portability: no bash-4-only ${var^^}/mapfile (macOS ships bash
  3.2) and no GNU realpath -m; the runtime-library filter matches the
  librustc-nightly_rt.tsan.* stem (a .a on Linux, a .dylib on macOS).

Verified on macOS arm64: `bazel test --config=tsan-macos //src/rust/tsan-test/...
//src/rust/cxx/kj-rs/tests/...` passes, binaries carry __tsan_* symbols and
link libclang_rt.tsan_osx_dynamic.dylib, and tsan-integration-test confirms the
rebuilt standard library is instrumented and a racy Rust binary is detected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from maizatskyi/2026-09-01-tsan-config to main September 3, 2026 23:13
@danlapid
danlapid merged commit d3a915b into main Sep 4, 2026
21 checks passed
@danlapid
danlapid deleted the dlapid/tsanMacos branch September 4, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants