Skip to content

Commit d5a5c32

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#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 rust-lang#64780). * When custom llvm-config is specified during build process, the sanitizer runtimes will be obtained from there instead of begin rebuilding from sources in src/llvm-project/compiler-rt. This should be preferable since runtimes used should match instrumentation passes. For example there have been nine version of address sanitizer ABI. Note this marked as a draft PR, because it is currently untested on OS X (I would appreciate any help there). cc @kennytm, @japaric, @Firstyear, @choller
2 parents fbd078a + dd4d6a9 commit d5a5c32

File tree

44 files changed

+335
-649
lines changed

Some content is hidden

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

44 files changed

+335
-649
lines changed

Cargo.lock

-48
Original file line numberDiff line numberDiff line change
@@ -3388,17 +3388,6 @@ dependencies = [
33883388
"smallvec",
33893389
]
33903390

3391-
[[package]]
3392-
name = "rustc_asan"
3393-
version = "0.0.0"
3394-
dependencies = [
3395-
"alloc",
3396-
"build_helper",
3397-
"cmake",
3398-
"compiler_builtins",
3399-
"core",
3400-
]
3401-
34023391
[[package]]
34033392
name = "rustc_codegen_llvm"
34043393
version = "0.0.0"
@@ -3595,17 +3584,6 @@ dependencies = [
35953584
"cc",
35963585
]
35973586

3598-
[[package]]
3599-
name = "rustc_lsan"
3600-
version = "0.0.0"
3601-
dependencies = [
3602-
"alloc",
3603-
"build_helper",
3604-
"cmake",
3605-
"compiler_builtins",
3606-
"core",
3607-
]
3608-
36093587
[[package]]
36103588
name = "rustc_macros"
36113589
version = "0.1.0"
@@ -3660,17 +3638,6 @@ dependencies = [
36603638
"syntax_pos",
36613639
]
36623640

3663-
[[package]]
3664-
name = "rustc_msan"
3665-
version = "0.0.0"
3666-
dependencies = [
3667-
"alloc",
3668-
"build_helper",
3669-
"cmake",
3670-
"compiler_builtins",
3671-
"core",
3672-
]
3673-
36743641
[[package]]
36753642
name = "rustc_passes"
36763643
version = "0.0.0"
@@ -3785,17 +3752,6 @@ dependencies = [
37853752
"syntax_pos",
37863753
]
37873754

3788-
[[package]]
3789-
name = "rustc_tsan"
3790-
version = "0.0.0"
3791-
dependencies = [
3792-
"alloc",
3793-
"build_helper",
3794-
"cmake",
3795-
"compiler_builtins",
3796-
"core",
3797-
]
3798-
37993755
[[package]]
38003756
name = "rustc_typeck"
38013757
version = "0.0.0"
@@ -4150,10 +4106,6 @@ dependencies = [
41504106
"panic_unwind",
41514107
"profiler_builtins",
41524108
"rand 0.7.0",
4153-
"rustc_asan",
4154-
"rustc_lsan",
4155-
"rustc_msan",
4156-
"rustc_tsan",
41574109
"unwind",
41584110
"wasi",
41594111
]

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

+85-37
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl Step for Std {
9494
copy_third_party_objects(builder, &compiler, target);
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));
@@ -155,7 +155,6 @@ fn copy_third_party_objects(builder: &Builder<'_>, compiler: &Compiler, target:
155155
/// Configure cargo to compile the standard library, adding appropriate env vars
156156
/// and such.
157157
pub fn std_cargo(builder: &Builder<'_>,
158-
compiler: &Compiler,
159158
target: Interned<String>,
160159
cargo: &mut Cargo) {
161160
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
@@ -200,22 +199,6 @@ pub fn std_cargo(builder: &Builder<'_>,
200199
let mut features = builder.std_features();
201200
features.push_str(&compiler_builtins_c_feature);
202201

203-
if compiler.stage != 0 && builder.config.sanitizers {
204-
// This variable is used by the sanitizer runtime crates, e.g.
205-
// rustc_lsan, to build the sanitizer runtime from C code
206-
// When this variable is missing, those crates won't compile the C code,
207-
// so we don't set this variable during stage0 where llvm-config is
208-
// missing
209-
// We also only build the runtimes when --enable-sanitizers (or its
210-
// config.toml equivalent) is used
211-
let llvm_config = builder.ensure(native::Llvm {
212-
target: builder.config.build,
213-
emscripten: false,
214-
});
215-
cargo.env("LLVM_CONFIG", llvm_config);
216-
cargo.env("RUSTC_BUILD_SANITIZERS", "1");
217-
}
218-
219202
cargo.arg("--features").arg(features)
220203
.arg("--manifest-path")
221204
.arg(builder.src.join("src/libtest/Cargo.toml"));
@@ -274,30 +257,95 @@ impl Step for StdLink {
274257
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
275258
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
276259

277-
if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
278-
// The sanitizers are only built in stage1 or above, so the dylibs will
279-
// be missing in stage0 and causes panic. See the `std()` function above
280-
// for reason why the sanitizers are not built in stage0.
281-
copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
260+
if builder.config.sanitizers && target_compiler.stage != 0 {
261+
// The sanitizers are only copied in stage1 or above,
262+
// to avoid creating dependency on LLVM.
263+
copy_sanitizers(builder, &target_compiler, target);
282264
}
283265
}
284266
}
285267

286-
fn copy_apple_sanitizer_dylibs(
287-
builder: &Builder<'_>,
288-
native_dir: &Path,
289-
platform: &str,
290-
into: &Path,
291-
) {
292-
for &sanitizer in &["asan", "tsan"] {
293-
let filename = format!("lib__rustc__clang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
294-
let mut src_path = native_dir.join(sanitizer);
295-
src_path.push("build");
296-
src_path.push("lib");
297-
src_path.push("darwin");
298-
src_path.push(&filename);
299-
builder.copy(&src_path, &into.join(filename));
268+
/// Copies sanitizer runtime libraries into target libdir.
269+
fn copy_sanitizers(builder: &Builder<'_>, compiler: &Compiler, target: Interned<String>) {
270+
let llvm_config = builder.ensure(native::Llvm {
271+
target,
272+
emscripten: false,
273+
});
274+
275+
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
276+
let llvm_version = llvm_version.trim();
277+
278+
// The compiler-rt uses CLANG_VERSION as a part of COMPILER_RT_INSTALL_PATH.
279+
// The CLANG_VERSION is based on LLVM_VERSION but it does not not include
280+
// LLVM_VERSION_SUFFIX. On the other hand value returned from llvm-config
281+
// --version does include it, so lets strip it to obtain CLANG_VERSION.
282+
let mut non_digits = 0;
283+
let mut third_non_digit = llvm_version.len();
284+
for (i, c) in llvm_version.char_indices() {
285+
if !c.is_digit(10) {
286+
non_digits += 1;
287+
if non_digits == 3 {
288+
third_non_digit = i;
289+
break;
290+
}
291+
}
292+
}
293+
let llvm_version = &llvm_version[..third_non_digit];
294+
295+
let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir"));
296+
let llvm_libdir = PathBuf::from(llvm_libdir.trim());
297+
298+
let clang_resourcedir = llvm_libdir.join("clang").join(&llvm_version);
299+
let sanitizers = supported_sanitizers(&clang_resourcedir, target);
300+
let libdir = builder.sysroot_libdir(*compiler, target);
301+
302+
for (src, name) in &sanitizers {
303+
let dst = libdir.join(name);
304+
if !src.exists() {
305+
println!("Ignoring missing runtime: {}", src.display());
306+
continue;
307+
}
308+
builder.copy(&src, &dst);
309+
310+
if target == "x86_64-apple-darwin" {
311+
// Update the library install name reflect the fact it has been renamed.
312+
let status = Command::new("install_name_tool")
313+
.arg("-id")
314+
.arg(format!("@rpath/{}", name))
315+
.arg(&dst)
316+
.status()
317+
.expect("failed to execute `install_name_tool`");
318+
assert!(status.success());
319+
}
320+
}
321+
}
322+
323+
/// Returns a list of paths to sanitizer libraries supported on given target,
324+
/// and corresponding names we plan to give them when placed in target libdir.
325+
fn supported_sanitizers(resourcedir: &Path, target: Interned<String>) -> Vec<(PathBuf, String)> {
326+
let sanitizers = &["asan", "lsan", "msan", "tsan"];
327+
let mut result = Vec::new();
328+
match &*target {
329+
"x86_64-apple-darwin" => {
330+
let srcdir = resourcedir.join("lib/darwin");
331+
for s in sanitizers {
332+
let src = format!("libclang_rt.{}_osx_dynamic.dylib", s);
333+
let dst = format!("librustc_rt.{}.dylib", s);
334+
result.push((srcdir.join(src), dst));
335+
}
336+
337+
}
338+
"x86_64-unknown-linux-gnu" => {
339+
let srcdir = resourcedir.join("lib/linux");
340+
for s in sanitizers {
341+
let src = format!("libclang_rt.{}-x86_64.a", s);
342+
let dst = format!("librustc_rt.{}.a", s);
343+
result.push((srcdir.join(src), dst));
344+
}
345+
}
346+
_ => {}
300347
}
348+
result
301349
}
302350

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

src/bootstrap/dist.rs

-4
Original file line numberDiff line numberDiff line change
@@ -901,10 +901,6 @@ impl Step for Src {
901901
"src/libcore",
902902
"src/libpanic_abort",
903903
"src/libpanic_unwind",
904-
"src/librustc_asan",
905-
"src/librustc_lsan",
906-
"src/librustc_msan",
907-
"src/librustc_tsan",
908904
"src/libstd",
909905
"src/libunwind",
910906
"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

+7
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ impl Step for Llvm {
209209
enabled_llvm_projects.push("compiler-rt");
210210
}
211211

212+
if builder.config.sanitizers {
213+
enabled_llvm_projects.push("compiler-rt");
214+
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
215+
// Avoids building instrumented version of libcxx.
216+
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
217+
}
218+
212219
if want_lldb {
213220
enabled_llvm_projects.push("clang");
214221
enabled_llvm_projects.push("lldb");

src/bootstrap/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1760,7 +1760,7 @@ impl Step for Crate {
17601760
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
17611761
match mode {
17621762
Mode::Std => {
1763-
compile::std_cargo(builder, &compiler, target, &mut cargo);
1763+
compile::std_cargo(builder, target, &mut cargo);
17641764
}
17651765
Mode::Rustc => {
17661766
builder.ensure(compile::Rustc { compiler, target });

0 commit comments

Comments
 (0)