diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 3a14b3e71c4..00c8e72a8f6 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -343,6 +343,7 @@ impl<'a> Builder<'a> { tool::Rustdoc, tool::Clippy, native::Llvm, + native::Sanitizers, tool::Rustfmt, tool::Miri, native::Lld diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index d4016f16fa9..b76515763fb 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -45,7 +45,7 @@ impl Step for Std { let compiler = builder.compiler(0, builder.config.build); let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind)); - std_cargo(builder, &compiler, target, &mut cargo); + std_cargo(builder, target, &mut cargo); builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target)); run_cargo( diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 7f0bb5813a4..eced03506ab 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -87,7 +87,7 @@ impl Step for Std { target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); - std_cargo(builder, &compiler, target, &mut cargo); + std_cargo(builder, target, &mut cargo); builder.info(&format!( "Building stage{} std artifacts ({} -> {})", @@ -153,17 +153,18 @@ fn copy_third_party_objects( copy_and_stamp(Path::new(&src), "libunwind.a"); } + if builder.config.sanitizers && compiler.stage != 0 { + // The sanitizers are only copied in stage1 or above, + // to avoid creating dependency on LLVM. + target_deps.extend(copy_sanitizers(builder, &compiler, target)); + } + target_deps } /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. -pub fn std_cargo( - builder: &Builder<'_>, - compiler: &Compiler, - target: Interned, - cargo: &mut Cargo, -) { +pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Cargo) { if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { cargo.env("MACOSX_DEPLOYMENT_TARGET", target); } @@ -206,19 +207,6 @@ pub fn std_cargo( let mut features = builder.std_features(); features.push_str(&compiler_builtins_c_feature); - if compiler.stage != 0 && builder.config.sanitizers { - // This variable is used by the sanitizer runtime crates, e.g. - // rustc_lsan, to build the sanitizer runtime from C code - // When this variable is missing, those crates won't compile the C code, - // so we don't set this variable during stage0 where llvm-config is - // missing - // We also only build the runtimes when --enable-sanitizers (or its - // config.toml equivalent) is used - let llvm_config = builder.ensure(native::Llvm { target: builder.config.build }); - cargo.env("LLVM_CONFIG", llvm_config); - cargo.env("RUSTC_BUILD_SANITIZERS", "1"); - } - cargo .arg("--features") .arg(features) @@ -276,31 +264,43 @@ impl Step for StdLink { let libdir = builder.sysroot_libdir(target_compiler, target); let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); - - if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" { - // The sanitizers are only built in stage1 or above, so the dylibs will - // be missing in stage0 and causes panic. See the `std()` function above - // for reason why the sanitizers are not built in stage0. - copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir); - } } } -fn copy_apple_sanitizer_dylibs( +/// Copies sanitizer runtime libraries into target libdir. +fn copy_sanitizers( builder: &Builder<'_>, - native_dir: &Path, - platform: &str, - into: &Path, -) { - for &sanitizer in &["asan", "tsan"] { - let filename = format!("lib__rustc__clang_rt.{}_{}_dynamic.dylib", sanitizer, platform); - let mut src_path = native_dir.join(sanitizer); - src_path.push("build"); - src_path.push("lib"); - src_path.push("darwin"); - src_path.push(&filename); - builder.copy(&src_path, &into.join(filename)); + compiler: &Compiler, + target: Interned, +) -> Vec { + let runtimes: Vec = builder.ensure(native::Sanitizers { target }); + + if builder.config.dry_run { + return Vec::new(); } + + let mut target_deps = Vec::new(); + let libdir = builder.sysroot_libdir(*compiler, target); + + for runtime in &runtimes { + let dst = libdir.join(&runtime.name); + builder.copy(&runtime.path, &dst); + + if target == "x86_64-apple-darwin" { + // Update the library install name reflect the fact it has been renamed. + let status = Command::new("install_name_tool") + .arg("-id") + .arg(format!("@rpath/{}", runtime.name)) + .arg(&dst) + .status() + .expect("failed to execute `install_name_tool`"); + assert!(status.success()); + } + + target_deps.push(dst); + } + + target_deps } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 8cd7fc2c172..4d18199dc8a 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -449,7 +449,7 @@ impl Step for Std { let run_cargo_rustdoc_for = |package: &str| { let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc"); - compile::std_cargo(builder, &compiler, target, &mut cargo); + compile::std_cargo(builder, target, &mut cargo); // Keep a whitelist so we do not build internal stdlib crates, these will be // build by the rustc step later if enabled. diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 2a4e9903e55..ce977f1bbc4 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -546,3 +546,118 @@ impl Step for TestHelpers { .compile("rust_test_helpers"); } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Sanitizers { + pub target: Interned, +} + +impl Step for Sanitizers { + type Output = Vec; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/llvm-project/compiler-rt").path("src/sanitizers") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Sanitizers { target: run.target }); + } + + /// Builds sanitizer runtime libraries. + fn run(self, builder: &Builder<'_>) -> Self::Output { + let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt"); + if !compiler_rt_dir.exists() { + return Vec::new(); + } + + let out_dir = builder.native_dir(self.target).join("sanitizers"); + let runtimes = supported_sanitizers(&out_dir, self.target); + if runtimes.is_empty() { + return runtimes; + } + + let llvm_config = builder.ensure(Llvm { target: builder.config.build }); + if builder.config.dry_run { + return runtimes; + } + + let done_stamp = out_dir.join("sanitizers-finished-building"); + if done_stamp.exists() { + builder.info(&format!( + "Assuming that sanitizers rebuild is not necessary. \ + To force a rebuild, remove the file `{}`", + done_stamp.display() + )); + return runtimes; + } + + builder.info(&format!("Building sanitizers for {}", self.target)); + let _time = util::timeit(&builder); + + let mut cfg = cmake::Config::new(&compiler_rt_dir); + cfg.target(&self.target); + cfg.host(&builder.config.build); + cfg.profile("Release"); + + cfg.define("CMAKE_C_COMPILER_TARGET", self.target); + cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF"); + cfg.define("COMPILER_RT_BUILD_CRT", "OFF"); + cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF"); + cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF"); + cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON"); + cfg.define("COMPILER_RT_BUILD_XRAY", "OFF"); + cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"); + cfg.define("COMPILER_RT_USE_LIBCXX", "OFF"); + cfg.define("LLVM_CONFIG_PATH", &llvm_config); + + t!(fs::create_dir_all(&out_dir)); + cfg.out_dir(out_dir); + + for runtime in &runtimes { + cfg.build_target(&runtime.cmake_target); + cfg.build(); + } + + t!(fs::write(&done_stamp, b"")); + + runtimes + } +} + +#[derive(Clone, Debug)] +pub struct SanitizerRuntime { + /// CMake target used to build the runtime. + pub cmake_target: String, + /// Path to the built runtime library. + pub path: PathBuf, + /// Library filename that will be used rustc. + pub name: String, +} + +/// Returns sanitizers available on a given target. +fn supported_sanitizers(out_dir: &Path, target: Interned) -> Vec { + let mut result = Vec::new(); + match &*target { + "x86_64-apple-darwin" => { + for s in &["asan", "lsan", "tsan"] { + result.push(SanitizerRuntime { + cmake_target: format!("clang_rt.{}_osx_dynamic", s), + path: out_dir + .join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)), + name: format!("librustc_rt.{}.dylib", s), + }); + } + } + "x86_64-unknown-linux-gnu" => { + for s in &["asan", "lsan", "msan", "tsan"] { + result.push(SanitizerRuntime { + cmake_target: format!("clang_rt.{}-x86_64", s), + path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)), + name: format!("librustc_rt.{}.a", s), + }); + } + } + _ => {} + } + result +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b5c8de057d0..10e07489e12 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1659,7 +1659,7 @@ impl Step for Crate { let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand()); match mode { Mode::Std => { - compile::std_cargo(builder, &compiler, target, &mut cargo); + compile::std_cargo(builder, target, &mut cargo); } Mode::Rustc => { builder.ensure(compile::Rustc { compiler, target });