Skip to content

Commit 9a9b5e0

Browse files
committed
Auto merge of #65241 - tmiasko:no-std-san, r=nikomatsakis
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 90b957a + 4313f70 commit 9a9b5e0

File tree

43 files changed

+407
-646
lines changed

Some content is hidden

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

43 files changed

+407
-646
lines changed

Cargo.lock

-48
Original file line numberDiff line numberDiff line change
@@ -3420,17 +3420,6 @@ dependencies = [
34203420
"smallvec 1.0.0",
34213421
]
34223422

3423-
[[package]]
3424-
name = "rustc_asan"
3425-
version = "0.0.0"
3426-
dependencies = [
3427-
"alloc",
3428-
"build_helper",
3429-
"cmake",
3430-
"compiler_builtins",
3431-
"core",
3432-
]
3433-
34343423
[[package]]
34353424
name = "rustc_codegen_llvm"
34363425
version = "0.0.0"
@@ -3653,17 +3642,6 @@ dependencies = [
36533642
"cc",
36543643
]
36553644

3656-
[[package]]
3657-
name = "rustc_lsan"
3658-
version = "0.0.0"
3659-
dependencies = [
3660-
"alloc",
3661-
"build_helper",
3662-
"cmake",
3663-
"compiler_builtins",
3664-
"core",
3665-
]
3666-
36673645
[[package]]
36683646
name = "rustc_macros"
36693647
version = "0.1.0"
@@ -3723,17 +3701,6 @@ dependencies = [
37233701
"syntax_pos",
37243702
]
37253703

3726-
[[package]]
3727-
name = "rustc_msan"
3728-
version = "0.0.0"
3729-
dependencies = [
3730-
"alloc",
3731-
"build_helper",
3732-
"cmake",
3733-
"compiler_builtins",
3734-
"core",
3735-
]
3736-
37373704
[[package]]
37383705
name = "rustc_parse"
37393706
version = "0.0.0"
@@ -3880,17 +3847,6 @@ dependencies = [
38803847
"syntax_pos",
38813848
]
38823849

3883-
[[package]]
3884-
name = "rustc_tsan"
3885-
version = "0.0.0"
3886-
dependencies = [
3887-
"alloc",
3888-
"build_helper",
3889-
"cmake",
3890-
"compiler_builtins",
3891-
"core",
3892-
]
3893-
38943850
[[package]]
38953851
name = "rustc_typeck"
38963852
version = "0.0.0"
@@ -4254,10 +4210,6 @@ dependencies = [
42544210
"panic_unwind",
42554211
"profiler_builtins",
42564212
"rand 0.7.0",
4257-
"rustc_asan",
4258-
"rustc_lsan",
4259-
"rustc_msan",
4260-
"rustc_tsan",
42614213
"unwind",
42624214
"wasi 0.9.0+wasi-snapshot-preview1",
42634215
]

src/bootstrap/builder.rs

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

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

+41-38
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/dist.rs

-4
Original file line numberDiff line numberDiff line change
@@ -961,10 +961,6 @@ impl Step for Src {
961961
"src/libcore",
962962
"src/libpanic_abort",
963963
"src/libpanic_unwind",
964-
"src/librustc_asan",
965-
"src/librustc_lsan",
966-
"src/librustc_msan",
967-
"src/librustc_tsan",
968964
"src/libstd",
969965
"src/libunwind",
970966
"src/libtest",

src/bootstrap/doc.rs

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

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

+1-1
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)