Skip to content

Commit 92000b9

Browse files
committed
Add bootstrap step for building sanitizer runtimes
1 parent 4abe0e5 commit 92000b9

File tree

6 files changed

+168
-41
lines changed

6 files changed

+168
-41
lines changed

src/bootstrap/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ impl<'a> Builder<'a> {
357357
tool::Rustdoc,
358358
tool::Clippy,
359359
native::Llvm,
360+
native::Sanitizers,
360361
tool::Rustfmt,
361362
tool::Miri,
362363
native::Lld

src/bootstrap/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl Step for Std {
4848
let compiler = builder.compiler(0, builder.config.build);
4949

5050
let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind));
51-
std_cargo(builder, &compiler, target, &mut cargo);
51+
std_cargo(builder, target, &mut cargo);
5252

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

src/bootstrap/compile.rs

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl Step for Std {
9494
target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter());
9595

9696
let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
97-
std_cargo(builder, &compiler, target, &mut cargo);
97+
std_cargo(builder, target, &mut cargo);
9898

9999
builder.info(&format!("Building stage{} std artifacts ({} -> {})", compiler.stage,
100100
&compiler.host, target));
@@ -157,13 +157,18 @@ fn copy_third_party_objects(builder: &Builder<'_>, compiler: &Compiler, target:
157157
copy_and_stamp(Path::new(&src), "libunwind.a");
158158
}
159159

160+
if builder.config.sanitizers && compiler.stage != 0 {
161+
// The sanitizers are only copied in stage1 or above,
162+
// to avoid creating dependency on LLVM.
163+
target_deps.extend(copy_sanitizers(builder, &compiler, target));
164+
}
165+
160166
target_deps
161167
}
162168

163169
/// Configure cargo to compile the standard library, adding appropriate env vars
164170
/// and such.
165171
pub fn std_cargo(builder: &Builder<'_>,
166-
compiler: &Compiler,
167172
target: Interned<String>,
168173
cargo: &mut Cargo) {
169174
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
@@ -208,21 +213,6 @@ pub fn std_cargo(builder: &Builder<'_>,
208213
let mut features = builder.std_features();
209214
features.push_str(&compiler_builtins_c_feature);
210215

211-
if compiler.stage != 0 && builder.config.sanitizers {
212-
// This variable is used by the sanitizer runtime crates, e.g.
213-
// rustc_lsan, to build the sanitizer runtime from C code
214-
// When this variable is missing, those crates won't compile the C code,
215-
// so we don't set this variable during stage0 where llvm-config is
216-
// missing
217-
// We also only build the runtimes when --enable-sanitizers (or its
218-
// config.toml equivalent) is used
219-
let llvm_config = builder.ensure(native::Llvm {
220-
target: builder.config.build,
221-
});
222-
cargo.env("LLVM_CONFIG", llvm_config);
223-
cargo.env("RUSTC_BUILD_SANITIZERS", "1");
224-
}
225-
226216
cargo.arg("--features").arg(features)
227217
.arg("--manifest-path")
228218
.arg(builder.src.join("src/libtest/Cargo.toml"));
@@ -280,31 +270,44 @@ impl Step for StdLink {
280270
let libdir = builder.sysroot_libdir(target_compiler, target);
281271
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
282272
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
283-
284-
if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
285-
// The sanitizers are only built in stage1 or above, so the dylibs will
286-
// be missing in stage0 and causes panic. See the `std()` function above
287-
// for reason why the sanitizers are not built in stage0.
288-
copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
289-
}
290273
}
291274
}
292275

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

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

src/bootstrap/doc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ impl Step for Std {
458458

459459
let run_cargo_rustdoc_for = |package: &str| {
460460
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
461-
compile::std_cargo(builder, &compiler, target, &mut cargo);
461+
compile::std_cargo(builder, target, &mut cargo);
462462

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

src/bootstrap/native.rs

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

src/bootstrap/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1769,7 +1769,7 @@ impl Step for Crate {
17691769
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
17701770
match mode {
17711771
Mode::Std => {
1772-
compile::std_cargo(builder, &compiler, target, &mut cargo);
1772+
compile::std_cargo(builder, target, &mut cargo);
17731773
}
17741774
Mode::Rustc => {
17751775
builder.ensure(compile::Rustc { compiler, target });

0 commit comments

Comments
 (0)