Skip to content

Commit ae57259

Browse files
committed
Add bootstrap step for building sanitizer runtimes
1 parent 0c6b1a7 commit ae57259

File tree

6 files changed

+159
-43
lines changed

6 files changed

+159
-43
lines changed

Diff for: src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ impl<'a> Builder<'a> {
343343
tool::Rustdoc,
344344
tool::Clippy,
345345
native::Llvm,
346+
native::Sanitizers,
346347
tool::Rustfmt,
347348
tool::Miri,
348349
native::Lld

Diff for: src/bootstrap/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl Step for Std {
4545
let compiler = builder.compiler(0, builder.config.build);
4646

4747
let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind));
48-
std_cargo(builder, &compiler, target, &mut cargo);
48+
std_cargo(builder, target, &mut cargo);
4949

5050
builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target));
5151
run_cargo(

Diff for: src/bootstrap/compile.rs

+40-40
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Step for Std {
8787
target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter());
8888

8989
let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
90-
std_cargo(builder, &compiler, target, &mut cargo);
90+
std_cargo(builder, target, &mut cargo);
9191

9292
builder.info(&format!(
9393
"Building stage{} std artifacts ({} -> {})",
@@ -153,17 +153,18 @@ fn copy_third_party_objects(
153153
copy_and_stamp(Path::new(&src), "libunwind.a");
154154
}
155155

156+
if builder.config.sanitizers && compiler.stage != 0 {
157+
// The sanitizers are only copied in stage1 or above,
158+
// to avoid creating dependency on LLVM.
159+
target_deps.extend(copy_sanitizers(builder, &compiler, target));
160+
}
161+
156162
target_deps
157163
}
158164

159165
/// Configure cargo to compile the standard library, adding appropriate env vars
160166
/// and such.
161-
pub fn std_cargo(
162-
builder: &Builder<'_>,
163-
compiler: &Compiler,
164-
target: Interned<String>,
165-
cargo: &mut Cargo,
166-
) {
167+
pub fn std_cargo(builder: &Builder<'_>, target: Interned<String>, cargo: &mut Cargo) {
167168
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
168169
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
169170
}
@@ -206,19 +207,6 @@ pub fn std_cargo(
206207
let mut features = builder.std_features();
207208
features.push_str(&compiler_builtins_c_feature);
208209

209-
if compiler.stage != 0 && builder.config.sanitizers {
210-
// This variable is used by the sanitizer runtime crates, e.g.
211-
// rustc_lsan, to build the sanitizer runtime from C code
212-
// When this variable is missing, those crates won't compile the C code,
213-
// so we don't set this variable during stage0 where llvm-config is
214-
// missing
215-
// We also only build the runtimes when --enable-sanitizers (or its
216-
// config.toml equivalent) is used
217-
let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
218-
cargo.env("LLVM_CONFIG", llvm_config);
219-
cargo.env("RUSTC_BUILD_SANITIZERS", "1");
220-
}
221-
222210
cargo
223211
.arg("--features")
224212
.arg(features)
@@ -276,31 +264,43 @@ impl Step for StdLink {
276264
let libdir = builder.sysroot_libdir(target_compiler, target);
277265
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
278266
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
279-
280-
if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
281-
// The sanitizers are only built in stage1 or above, so the dylibs will
282-
// be missing in stage0 and causes panic. See the `std()` function above
283-
// for reason why the sanitizers are not built in stage0.
284-
copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
285-
}
286267
}
287268
}
288269

289-
fn copy_apple_sanitizer_dylibs(
270+
/// Copies sanitizer runtime libraries into target libdir.
271+
fn copy_sanitizers(
290272
builder: &Builder<'_>,
291-
native_dir: &Path,
292-
platform: &str,
293-
into: &Path,
294-
) {
295-
for &sanitizer in &["asan", "tsan"] {
296-
let filename = format!("lib__rustc__clang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
297-
let mut src_path = native_dir.join(sanitizer);
298-
src_path.push("build");
299-
src_path.push("lib");
300-
src_path.push("darwin");
301-
src_path.push(&filename);
302-
builder.copy(&src_path, &into.join(filename));
273+
compiler: &Compiler,
274+
target: Interned<String>,
275+
) -> Vec<PathBuf> {
276+
let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target });
277+
278+
if builder.config.dry_run {
279+
return Vec::new();
280+
}
281+
282+
let mut target_deps = Vec::new();
283+
let libdir = builder.sysroot_libdir(*compiler, target);
284+
285+
for runtime in &runtimes {
286+
let dst = libdir.join(&runtime.name);
287+
builder.copy(&runtime.path, &dst);
288+
289+
if target == "x86_64-apple-darwin" {
290+
// Update the library install name reflect the fact it has been renamed.
291+
let status = Command::new("install_name_tool")
292+
.arg("-id")
293+
.arg(format!("@rpath/{}", runtime.name))
294+
.arg(&dst)
295+
.status()
296+
.expect("failed to execute `install_name_tool`");
297+
assert!(status.success());
298+
}
299+
300+
target_deps.push(dst);
303301
}
302+
303+
target_deps
304304
}
305305

306306
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

Diff for: src/bootstrap/doc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ impl Step for Std {
449449

450450
let run_cargo_rustdoc_for = |package: &str| {
451451
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
452-
compile::std_cargo(builder, &compiler, target, &mut cargo);
452+
compile::std_cargo(builder, target, &mut cargo);
453453

454454
// Keep a whitelist so we do not build internal stdlib crates, these will be
455455
// build by the rustc step later if enabled.

Diff for: src/bootstrap/native.rs

+115
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,118 @@ impl Step for TestHelpers {
546546
.compile("rust_test_helpers");
547547
}
548548
}
549+
550+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
551+
pub struct Sanitizers {
552+
pub target: Interned<String>,
553+
}
554+
555+
impl Step for Sanitizers {
556+
type Output = Vec<SanitizerRuntime>;
557+
558+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
559+
run.path("src/llvm-project/compiler-rt").path("src/sanitizers")
560+
}
561+
562+
fn make_run(run: RunConfig<'_>) {
563+
run.builder.ensure(Sanitizers { target: run.target });
564+
}
565+
566+
/// Builds sanitizer runtime libraries.
567+
fn run(self, builder: &Builder<'_>) -> Self::Output {
568+
let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
569+
if !compiler_rt_dir.exists() {
570+
return Vec::new();
571+
}
572+
573+
let out_dir = builder.native_dir(self.target).join("sanitizers");
574+
let runtimes = supported_sanitizers(&out_dir, self.target);
575+
if runtimes.is_empty() {
576+
return runtimes;
577+
}
578+
579+
let llvm_config = builder.ensure(Llvm { target: builder.config.build });
580+
if builder.config.dry_run {
581+
return runtimes;
582+
}
583+
584+
let done_stamp = out_dir.join("sanitizers-finished-building");
585+
if done_stamp.exists() {
586+
builder.info(&format!(
587+
"Assuming that sanitizers rebuild is not necessary. \
588+
To force a rebuild, remove the file `{}`",
589+
done_stamp.display()
590+
));
591+
return runtimes;
592+
}
593+
594+
builder.info(&format!("Building sanitizers for {}", self.target));
595+
let _time = util::timeit(&builder);
596+
597+
let mut cfg = cmake::Config::new(&compiler_rt_dir);
598+
cfg.target(&self.target);
599+
cfg.host(&builder.config.build);
600+
cfg.profile("Release");
601+
602+
cfg.define("CMAKE_C_COMPILER_TARGET", self.target);
603+
cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
604+
cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
605+
cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
606+
cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
607+
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
608+
cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
609+
cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
610+
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
611+
cfg.define("LLVM_CONFIG_PATH", &llvm_config);
612+
613+
t!(fs::create_dir_all(&out_dir));
614+
cfg.out_dir(out_dir);
615+
616+
for runtime in &runtimes {
617+
cfg.build_target(&runtime.cmake_target);
618+
cfg.build();
619+
}
620+
621+
t!(fs::write(&done_stamp, b""));
622+
623+
runtimes
624+
}
625+
}
626+
627+
#[derive(Clone, Debug)]
628+
pub struct SanitizerRuntime {
629+
/// CMake target used to build the runtime.
630+
pub cmake_target: String,
631+
/// Path to the built runtime library.
632+
pub path: PathBuf,
633+
/// Library filename that will be used rustc.
634+
pub name: String,
635+
}
636+
637+
/// Returns sanitizers available on a given target.
638+
fn supported_sanitizers(out_dir: &Path, target: Interned<String>) -> Vec<SanitizerRuntime> {
639+
let mut result = Vec::new();
640+
match &*target {
641+
"x86_64-apple-darwin" => {
642+
for s in &["asan", "lsan", "tsan"] {
643+
result.push(SanitizerRuntime {
644+
cmake_target: format!("clang_rt.{}_osx_dynamic", s),
645+
path: out_dir
646+
.join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)),
647+
name: format!("librustc_rt.{}.dylib", s),
648+
});
649+
}
650+
}
651+
"x86_64-unknown-linux-gnu" => {
652+
for s in &["asan", "lsan", "msan", "tsan"] {
653+
result.push(SanitizerRuntime {
654+
cmake_target: format!("clang_rt.{}-x86_64", s),
655+
path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)),
656+
name: format!("librustc_rt.{}.a", s),
657+
});
658+
}
659+
}
660+
_ => {}
661+
}
662+
result
663+
}

Diff for: src/bootstrap/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ impl Step for Crate {
16591659
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
16601660
match mode {
16611661
Mode::Std => {
1662-
compile::std_cargo(builder, &compiler, target, &mut cargo);
1662+
compile::std_cargo(builder, target, &mut cargo);
16631663
}
16641664
Mode::Rustc => {
16651665
builder.ensure(compile::Rustc { compiler, target });

0 commit comments

Comments
 (0)