Skip to content

Commit d56ab6d

Browse files
committed
Link sanitizer runtimes instead of injecting crate dependencies
1 parent 93c5639 commit d56ab6d

File tree

12 files changed

+165
-188
lines changed

12 files changed

+165
-188
lines changed

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: 95 additions & 36 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));
@@ -163,7 +163,6 @@ fn copy_third_party_objects(builder: &Builder<'_>, compiler: &Compiler, target:
163163
/// Configure cargo to compile the standard library, adding appropriate env vars
164164
/// and such.
165165
pub fn std_cargo(builder: &Builder<'_>,
166-
compiler: &Compiler,
167166
target: Interned<String>,
168167
cargo: &mut Cargo) {
169168
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
@@ -208,21 +207,6 @@ pub fn std_cargo(builder: &Builder<'_>,
208207
let mut features = builder.std_features();
209208
features.push_str(&compiler_builtins_c_feature);
210209

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-
226210
cargo.arg("--features").arg(features)
227211
.arg("--manifest-path")
228212
.arg(builder.src.join("src/libtest/Cargo.toml"));
@@ -281,30 +265,105 @@ impl Step for StdLink {
281265
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
282266
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
283267

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);
268+
if builder.config.sanitizers && compiler.stage != 0 {
269+
// The sanitizers are only copied in stage1 or above,
270+
// to avoid creating dependency on LLVM.
271+
copy_sanitizers(builder, &target_compiler, target);
289272
}
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<'_>, target_compiler: &Compiler, target: Interned<String>) {
278+
let sanitizers = supported_sanitizers(target);
279+
if sanitizers.is_empty() {
280+
return;
281+
}
282+
283+
let llvm_config: PathBuf = builder.ensure(native::Llvm {
284+
target: target_compiler.host,
285+
});
286+
if builder.config.dry_run {
287+
return;
288+
}
289+
290+
// The compiler-rt installs sanitizer runtimes into clang resource directory.
291+
let clang_resourcedir = clang_resourcedir(&llvm_config);
292+
let libdir = builder.sysroot_libdir(*target_compiler, target);
293+
294+
for (path, name) in &sanitizers {
295+
let src = clang_resourcedir.join(path);
296+
let dst = libdir.join(name);
297+
if !src.exists() {
298+
println!("Ignoring missing runtime: {}", src.display());
299+
continue;
300+
}
301+
builder.copy(&src, &dst);
302+
303+
if target == "x86_64-apple-darwin" {
304+
// Update the library install name reflect the fact it has been renamed.
305+
let status = Command::new("install_name_tool")
306+
.arg("-id")
307+
.arg(format!("@rpath/{}", name))
308+
.arg(&dst)
309+
.status()
310+
.expect("failed to execute `install_name_tool`");
311+
assert!(status.success());
312+
}
313+
}
314+
}
315+
316+
/// Returns path to clang's resource directory.
317+
fn clang_resourcedir(llvm_config: &Path) -> PathBuf {
318+
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
319+
let llvm_version = llvm_version.trim();
320+
let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir"));
321+
let llvm_libdir = PathBuf::from(llvm_libdir.trim());
322+
323+
// Determine CLANG_VERSION by stripping LLVM_VERSION_SUFFIX from LLVM_VERSION.
324+
let mut non_digits = 0;
325+
let mut third_non_digit = llvm_version.len();
326+
for (i, c) in llvm_version.char_indices() {
327+
if !c.is_digit(10) {
328+
non_digits += 1;
329+
if non_digits == 3 {
330+
third_non_digit = i;
331+
break;
332+
}
333+
}
334+
}
335+
let clang_version = &llvm_version[..third_non_digit];
336+
llvm_libdir.join("clang").join(&clang_version)
337+
}
338+
339+
/// Returns a list of paths to sanitizer libraries supported on given target,
340+
/// and corresponding names we plan to give them when placed in target libdir.
341+
///
342+
/// Returned paths are relative to clang's resource directory.
343+
fn supported_sanitizers(target: Interned<String>) -> Vec<(PathBuf, String)> {
344+
let sanitizers = &["asan", "lsan", "msan", "tsan"];
345+
let mut result = Vec::new();
346+
match &*target {
347+
"x86_64-apple-darwin" => {
348+
let srcdir = Path::new("lib/darwin");
349+
for s in sanitizers {
350+
let src = format!("libclang_rt.{}_osx_dynamic.dylib", s);
351+
let dst = format!("librustc_rt.{}.dylib", s);
352+
result.push((srcdir.join(src), dst));
353+
}
354+
355+
}
356+
"x86_64-unknown-linux-gnu" => {
357+
let srcdir = Path::new("lib/linux");
358+
for s in sanitizers {
359+
let src = format!("libclang_rt.{}-x86_64.a", s);
360+
let dst = format!("librustc_rt.{}.a", s);
361+
result.push((srcdir.join(src), dst));
362+
}
363+
}
364+
_ => {}
307365
}
366+
result
308367
}
309368

310369
#[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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ impl Step for Llvm {
186186
enabled_llvm_projects.push("compiler-rt");
187187
}
188188

189+
if builder.config.sanitizers {
190+
enabled_llvm_projects.push("compiler-rt");
191+
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
192+
// Avoids building instrumented version of libcxx.
193+
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
194+
}
195+
189196
if builder.config.lldb_enabled {
190197
enabled_llvm_projects.push("clang");
191198
enabled_llvm_projects.push("lldb");

src/bootstrap/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,7 @@ impl Step for Crate {
17641764
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
17651765
match mode {
17661766
Mode::Std => {
1767-
compile::std_cargo(builder, &compiler, target, &mut cargo);
1767+
compile::std_cargo(builder, target, &mut cargo);
17681768
}
17691769
Mode::Rustc => {
17701770
builder.ensure(compile::Rustc { compiler, target });

src/librustc/session/mod.rs

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

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

src/librustc_codegen_ssa/back/link.rs

Lines changed: 44 additions & 44 deletions
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)