Skip to content

Commit 87c3eed

Browse files
committed
Link sanitizer runtimes instead of injecting crate dependencies
1 parent c1b5249 commit 87c3eed

File tree

12 files changed

+155
-188
lines changed

12 files changed

+155
-188
lines changed

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/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
@@ -217,6 +217,13 @@ impl Step for Llvm {
217217
enabled_llvm_projects.push("compiler-rt");
218218
}
219219

220+
if builder.config.sanitizers {
221+
enabled_llvm_projects.push("compiler-rt");
222+
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
223+
// Avoids building instrumented version of libcxx.
224+
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
225+
}
226+
220227
if want_lldb {
221228
enabled_llvm_projects.push("clang");
222229
enabled_llvm_projects.push("lldb");

src/bootstrap/test.rs

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

src/librustc/session/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,17 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
12961296
with `-Cpanic=unwind` on Windows when targeting MSVC. \
12971297
See https://github.com/rust-lang/rust/issues/61002 for details.");
12981298
}
1299+
1300+
// Sanitizers can only be used on some tested platforms.
1301+
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
1302+
const SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
1303+
if !SUPPORTED_TARGETS.contains(&&*sess.opts.target_triple.triple()) {
1304+
sess.err(&format!("{:?}Sanitizer only works with the `{}` target",
1305+
sanitizer,
1306+
SUPPORTED_TARGETS.join("` or `")
1307+
));
1308+
}
1309+
}
12991310
}
13001311

13011312
/// Hash value constructed out of all the `-C metadata` arguments passed to the

src/librustc_codegen_ssa/back/link.rs

+44-44
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
522522

523523
{
524524
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
525+
link_sanitizer_runtime(sess, crate_type, &mut *linker);
525526
link_args::<B>(&mut *linker, flavor, sess, crate_type, tmpdir,
526527
out_filename, codegen_results);
527528
cmd = linker.finalize();
@@ -725,6 +726,49 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
725726
}
726727
}
727728

729+
fn link_sanitizer_runtime(sess: &Session,
730+
crate_type: config::CrateType,
731+
linker: &mut dyn Linker) {
732+
let sanitizer = match &sess.opts.debugging_opts.sanitizer {
733+
Some(s) => s,
734+
None => return,
735+
};
736+
737+
if crate_type != config::CrateType::Executable {
738+
return;
739+
}
740+
741+
let name = match sanitizer {
742+
Sanitizer::Address => "asan",
743+
Sanitizer::Leak => "lsan",
744+
Sanitizer::Memory => "msan",
745+
Sanitizer::Thread => "tsan",
746+
};
747+
748+
let default_sysroot = filesearch::get_or_default_sysroot();
749+
let default_tlib = filesearch::make_target_lib_path(
750+
&default_sysroot, sess.opts.target_triple.triple());
751+
752+
match sess.opts.target_triple.triple() {
753+
"x86_64-apple-darwin" => {
754+
// On Apple platforms, the sanitizer is always built as a dylib, and
755+
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
756+
// rpath to the library as well (the rpath should be absolute, see
757+
// PR #41352 for details).
758+
let filename = format!("librustc_rt.{}.dylib", name);
759+
let rpath = default_tlib.to_str().expect("non-utf8 component in path");
760+
linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
761+
linker.link_dylib(Symbol::intern(&filename));
762+
}
763+
"x86_64-unknown-linux-gnu" => {
764+
let filename = format!("librustc_rt.{}.a", name);
765+
let path = default_tlib.join(&filename);
766+
linker.link_whole_rlib(&path);
767+
}
768+
_ => {}
769+
}
770+
}
771+
728772
/// Returns a boolean indicating whether the specified crate should be ignored
729773
/// during LTO.
730774
///
@@ -1391,11 +1435,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
13911435
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
13921436
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
13931437
}
1394-
_ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) &&
1395-
crate_type == config::CrateType::Executable => {
1396-
// Link the sanitizer runtimes only if we are actually producing an executable
1397-
link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum);
1398-
}
13991438
// compiler-builtins are always placed last to ensure that they're
14001439
// linked correctly.
14011440
_ if codegen_results.crate_info.compiler_builtins == Some(cnum) => {
@@ -1435,45 +1474,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
14351474
}
14361475
}
14371476

1438-
// We must link the sanitizer runtime using -Wl,--whole-archive but since
1439-
// it's packed in a .rlib, it contains stuff that are not objects that will
1440-
// make the linker error. So we must remove those bits from the .rlib before
1441-
// linking it.
1442-
fn link_sanitizer_runtime<'a, B: ArchiveBuilder<'a>>(cmd: &mut dyn Linker,
1443-
sess: &'a Session,
1444-
codegen_results: &CodegenResults,
1445-
tmpdir: &Path,
1446-
cnum: CrateNum) {
1447-
let src = &codegen_results.crate_info.used_crate_source[&cnum];
1448-
let cratepath = &src.rlib.as_ref().unwrap().0;
1449-
1450-
if sess.target.target.options.is_like_osx {
1451-
// On Apple platforms, the sanitizer is always built as a dylib, and
1452-
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
1453-
// rpath to the library as well (the rpath should be absolute, see
1454-
// PR #41352 for details).
1455-
//
1456-
// FIXME: Remove this logic into librustc_*san once Cargo supports it
1457-
let rpath = cratepath.parent().unwrap();
1458-
let rpath = rpath.to_str().expect("non-utf8 component in path");
1459-
cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
1460-
}
1461-
1462-
let dst = tmpdir.join(cratepath.file_name().unwrap());
1463-
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
1464-
archive.update_symbols();
1465-
1466-
for f in archive.src_files() {
1467-
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
1468-
archive.remove_file(&f);
1469-
}
1470-
}
1471-
1472-
archive.build();
1473-
1474-
cmd.link_whole_rlib(&dst);
1475-
}
1476-
14771477
// Adds the static "rlib" versions of all crates to the command line.
14781478
// There's a bit of magic which happens here specifically related to LTO and
14791479
// dynamic libraries. Specifically:

0 commit comments

Comments
 (0)