Skip to content

Commit e621797

Browse files
committed
Auto merge of #65241 - tmiasko:no-std-san, r=alexcrichton
build-std compatible sanitizer support ### Motivation When using `-Z sanitizer=*` feature it is essential that both user code and standard library is instrumented. Otherwise the utility of sanitizer will be limited, or its use will be impractical like in the case of memory sanitizer. The recently introduced cargo feature build-std makes it possible to rebuild standard library with arbitrary rustc flags. Unfortunately, those changes alone do not make it easy to rebuild standard library with sanitizers, since runtimes are dependencies of std that have to be build in specific environment, generally not available outside rustbuild process. Additionally rebuilding them requires presence of llvm-config and compiler-rt sources. The goal of changes proposed here is to make it possible to avoid rebuilding sanitizer runtimes when rebuilding the std, thus making it possible to instrument standard library for use with sanitizer with simple, although verbose command: ``` env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=-Zsanitizer=thread cargo test -Zbuild-std --target x86_64-unknown-linux-gnu ``` ### Implementation * Sanitizer runtimes are no long packed into crates. Instead, libraries build from compiler-rt are used as is, after renaming them into `librusc_rt.*`. * rustc obtains runtimes from target libdir for default sysroot, so that they are not required in custom build sysroots created with build-std. * The runtimes are only linked-in into executables to address issue #64629. (in previous design it was hard to avoid linking runtimes into static libraries produced by rustc as demonstrated by sanitizer-staticlib-link test, which still passes despite changes made in #64780). cc @kennytm, @japaric, @Firstyear, @choller
2 parents 1756313 + e88f071 commit e621797

File tree

45 files changed

+398
-665
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+398
-665
lines changed

Cargo.lock

-48
Original file line numberDiff line numberDiff line change
@@ -3345,17 +3345,6 @@ dependencies = [
33453345
"smallvec 1.0.0",
33463346
]
33473347

3348-
[[package]]
3349-
name = "rustc_asan"
3350-
version = "0.0.0"
3351-
dependencies = [
3352-
"alloc",
3353-
"build_helper",
3354-
"cmake",
3355-
"compiler_builtins",
3356-
"core",
3357-
]
3358-
33593348
[[package]]
33603349
name = "rustc_ast_lowering"
33613350
version = "0.0.0"
@@ -3674,17 +3663,6 @@ dependencies = [
36743663
"libc",
36753664
]
36763665

3677-
[[package]]
3678-
name = "rustc_lsan"
3679-
version = "0.0.0"
3680-
dependencies = [
3681-
"alloc",
3682-
"build_helper",
3683-
"cmake",
3684-
"compiler_builtins",
3685-
"core",
3686-
]
3687-
36883666
[[package]]
36893667
name = "rustc_macros"
36903668
version = "0.1.0"
@@ -3746,17 +3724,6 @@ dependencies = [
37463724
"syntax",
37473725
]
37483726

3749-
[[package]]
3750-
name = "rustc_msan"
3751-
version = "0.0.0"
3752-
dependencies = [
3753-
"alloc",
3754-
"build_helper",
3755-
"cmake",
3756-
"compiler_builtins",
3757-
"core",
3758-
]
3759-
37603727
[[package]]
37613728
name = "rustc_parse"
37623729
version = "0.0.0"
@@ -3929,17 +3896,6 @@ dependencies = [
39293896
"syntax",
39303897
]
39313898

3932-
[[package]]
3933-
name = "rustc_tsan"
3934-
version = "0.0.0"
3935-
dependencies = [
3936-
"alloc",
3937-
"build_helper",
3938-
"cmake",
3939-
"compiler_builtins",
3940-
"core",
3941-
]
3942-
39433899
[[package]]
39443900
name = "rustc_typeck"
39453901
version = "0.0.0"
@@ -4301,10 +4257,6 @@ dependencies = [
43014257
"panic_unwind",
43024258
"profiler_builtins",
43034259
"rand 0.7.0",
4304-
"rustc_asan",
4305-
"rustc_lsan",
4306-
"rustc_msan",
4307-
"rustc_tsan",
43084260
"unwind",
43094261
"wasi 0.9.0+wasi-snapshot-preview1",
43104262
]

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

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(

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)]

src/bootstrap/dist.rs

-4
Original file line numberDiff line numberDiff line change
@@ -984,10 +984,6 @@ impl Step for Src {
984984
"src/libcore",
985985
"src/libpanic_abort",
986986
"src/libpanic_unwind",
987-
"src/librustc_asan",
988-
"src/librustc_lsan",
989-
"src/librustc_msan",
990-
"src/librustc_tsan",
991987
"src/libstd",
992988
"src/libunwind",
993989
"src/libtest",

src/bootstrap/doc.rs

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

392392
let run_cargo_rustdoc_for = |package: &str| {
393393
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
394-
compile::std_cargo(builder, &compiler, target, &mut cargo);
394+
compile::std_cargo(builder, target, &mut cargo);
395395

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

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+
}

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)