Skip to content

Commit 0c6b1a7

Browse files
committed
Link sanitizer runtimes instead of injecting crate dependencies
1 parent 36d0812 commit 0c6b1a7

File tree

9 files changed

+74
-175
lines changed

9 files changed

+74
-175
lines changed

src/librustc_codegen_ssa/back/link.rs

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
531531

532532
{
533533
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
534+
link_sanitizer_runtime(sess, crate_type, &mut *linker);
534535
link_args::<B>(
535536
&mut *linker,
536537
flavor,
@@ -735,6 +736,47 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
735736
}
736737
}
737738

739+
fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) {
740+
let sanitizer = match &sess.opts.debugging_opts.sanitizer {
741+
Some(s) => s,
742+
None => return,
743+
};
744+
745+
if crate_type != config::CrateType::Executable {
746+
return;
747+
}
748+
749+
let name = match sanitizer {
750+
Sanitizer::Address => "asan",
751+
Sanitizer::Leak => "lsan",
752+
Sanitizer::Memory => "msan",
753+
Sanitizer::Thread => "tsan",
754+
};
755+
756+
let default_sysroot = filesearch::get_or_default_sysroot();
757+
let default_tlib =
758+
filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple());
759+
760+
match sess.opts.target_triple.triple() {
761+
"x86_64-apple-darwin" => {
762+
// On Apple platforms, the sanitizer is always built as a dylib, and
763+
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
764+
// rpath to the library as well (the rpath should be absolute, see
765+
// PR #41352 for details).
766+
let libname = format!("rustc_rt.{}", name);
767+
let rpath = default_tlib.to_str().expect("non-utf8 component in path");
768+
linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
769+
linker.link_dylib(Symbol::intern(&libname));
770+
}
771+
"x86_64-unknown-linux-gnu" => {
772+
let filename = format!("librustc_rt.{}.a", name);
773+
let path = default_tlib.join(&filename);
774+
linker.link_whole_rlib(&path);
775+
}
776+
_ => {}
777+
}
778+
}
779+
738780
/// Returns a boolean indicating whether the specified crate should be ignored
739781
/// during LTO.
740782
///
@@ -1415,12 +1457,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
14151457
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
14161458
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
14171459
}
1418-
_ if codegen_results.crate_info.sanitizer_runtime == Some(cnum)
1419-
&& crate_type == config::CrateType::Executable =>
1420-
{
1421-
// Link the sanitizer runtimes only if we are actually producing an executable
1422-
link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum);
1423-
}
14241460
// compiler-builtins are always placed last to ensure that they're
14251461
// linked correctly.
14261462
_ if codegen_results.crate_info.compiler_builtins == Some(cnum) => {
@@ -1457,47 +1493,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
14571493
}
14581494
}
14591495

1460-
// We must link the sanitizer runtime using -Wl,--whole-archive but since
1461-
// it's packed in a .rlib, it contains stuff that are not objects that will
1462-
// make the linker error. So we must remove those bits from the .rlib before
1463-
// linking it.
1464-
fn link_sanitizer_runtime<'a, B: ArchiveBuilder<'a>>(
1465-
cmd: &mut dyn Linker,
1466-
sess: &'a Session,
1467-
codegen_results: &CodegenResults,
1468-
tmpdir: &Path,
1469-
cnum: CrateNum,
1470-
) {
1471-
let src = &codegen_results.crate_info.used_crate_source[&cnum];
1472-
let cratepath = &src.rlib.as_ref().unwrap().0;
1473-
1474-
if sess.target.target.options.is_like_osx {
1475-
// On Apple platforms, the sanitizer is always built as a dylib, and
1476-
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
1477-
// rpath to the library as well (the rpath should be absolute, see
1478-
// PR #41352 for details).
1479-
//
1480-
// FIXME: Remove this logic into librustc_*san once Cargo supports it
1481-
let rpath = cratepath.parent().unwrap();
1482-
let rpath = rpath.to_str().expect("non-utf8 component in path");
1483-
cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
1484-
}
1485-
1486-
let dst = tmpdir.join(cratepath.file_name().unwrap());
1487-
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
1488-
archive.update_symbols();
1489-
1490-
for f in archive.src_files() {
1491-
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
1492-
archive.remove_file(&f);
1493-
}
1494-
}
1495-
1496-
archive.build();
1497-
1498-
cmd.link_whole_rlib(&dst);
1499-
}
1500-
15011496
// Adds the static "rlib" versions of all crates to the command line.
15021497
// There's a bit of magic which happens here specifically related to LTO and
15031498
// dynamic libraries. Specifically:

src/librustc_metadata/creader.rs

Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob
66
use rustc::hir::map::Definitions;
77
use rustc::middle::cstore::DepKind;
88
use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn};
9-
use rustc::session::config::{self, Sanitizer};
9+
use rustc::session::config;
1010
use rustc::session::search_paths::PathKind;
1111
use rustc::session::{CrateDisambiguator, Session};
1212
use rustc::ty::TyCtxt;
@@ -674,108 +674,6 @@ impl<'a> CrateLoader<'a> {
674674
self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime());
675675
}
676676

677-
fn inject_sanitizer_runtime(&mut self) {
678-
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
679-
// Sanitizers can only be used on some tested platforms with
680-
// executables linked to `std`
681-
const ASAN_SUPPORTED_TARGETS: &[&str] =
682-
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
683-
const TSAN_SUPPORTED_TARGETS: &[&str] =
684-
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
685-
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
686-
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
687-
688-
let supported_targets = match *sanitizer {
689-
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
690-
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
691-
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
692-
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
693-
};
694-
if !supported_targets.contains(&&*self.sess.opts.target_triple.triple()) {
695-
self.sess.err(&format!(
696-
"{:?}Sanitizer only works with the `{}` target",
697-
sanitizer,
698-
supported_targets.join("` or `")
699-
));
700-
return;
701-
}
702-
703-
// firstyear 2017 - during testing I was unable to access an OSX machine
704-
// to make this work on different crate types. As a result, today I have
705-
// only been able to test and support linux as a target.
706-
if self.sess.opts.target_triple.triple() == "x86_64-unknown-linux-gnu" {
707-
if !self.sess.crate_types.borrow().iter().all(|ct| {
708-
match *ct {
709-
// Link the runtime
710-
config::CrateType::Executable => true,
711-
// This crate will be compiled with the required
712-
// instrumentation pass
713-
config::CrateType::Staticlib
714-
| config::CrateType::Rlib
715-
| config::CrateType::Dylib
716-
| config::CrateType::Cdylib => false,
717-
_ => {
718-
self.sess.err(&format!(
719-
"Only executables, staticlibs, \
720-
cdylibs, dylibs and rlibs can be compiled with \
721-
`-Z sanitizer`"
722-
));
723-
false
724-
}
725-
}
726-
}) {
727-
return;
728-
}
729-
} else {
730-
if !self.sess.crate_types.borrow().iter().all(|ct| {
731-
match *ct {
732-
// Link the runtime
733-
config::CrateType::Executable => true,
734-
// This crate will be compiled with the required
735-
// instrumentation pass
736-
config::CrateType::Rlib => false,
737-
_ => {
738-
self.sess.err(&format!(
739-
"Only executables and rlibs can be \
740-
compiled with `-Z sanitizer`"
741-
));
742-
false
743-
}
744-
}
745-
}) {
746-
return;
747-
}
748-
}
749-
750-
let mut uses_std = false;
751-
self.cstore.iter_crate_data(|_, data| {
752-
if data.name() == sym::std {
753-
uses_std = true;
754-
}
755-
});
756-
757-
if uses_std {
758-
let name = Symbol::intern(match sanitizer {
759-
Sanitizer::Address => "rustc_asan",
760-
Sanitizer::Leak => "rustc_lsan",
761-
Sanitizer::Memory => "rustc_msan",
762-
Sanitizer::Thread => "rustc_tsan",
763-
});
764-
info!("loading sanitizer: {}", name);
765-
766-
let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Explicit, None);
767-
let data = self.cstore.get_crate_data(cnum);
768-
769-
// Sanity check the loaded crate to ensure it is indeed a sanitizer runtime
770-
if !data.is_sanitizer_runtime() {
771-
self.sess.err(&format!("the crate `{}` is not a sanitizer runtime", name));
772-
}
773-
} else {
774-
self.sess.err("Must link std to be compiled with `-Z sanitizer`");
775-
}
776-
}
777-
}
778-
779677
fn inject_profiler_runtime(&mut self) {
780678
if self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled() {
781679
info!("loading profiler");
@@ -927,7 +825,6 @@ impl<'a> CrateLoader<'a> {
927825
}
928826

929827
pub fn postprocess(&mut self, krate: &ast::Crate) {
930-
self.inject_sanitizer_runtime();
931828
self.inject_profiler_runtime();
932829
self.inject_allocator_crate(krate);
933830
self.inject_panic_runtime(krate);

src/librustc_session/session.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,32 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
11241124
See https://github.com/rust-lang/rust/issues/61002 for details.",
11251125
);
11261126
}
1127+
1128+
// Sanitizers can only be used on some tested platforms.
1129+
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
1130+
const ASAN_SUPPORTED_TARGETS: &[&str] =
1131+
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
1132+
const TSAN_SUPPORTED_TARGETS: &[&str] =
1133+
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
1134+
const LSAN_SUPPORTED_TARGETS: &[&str] =
1135+
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
1136+
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
1137+
1138+
let supported_targets = match *sanitizer {
1139+
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
1140+
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
1141+
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
1142+
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
1143+
};
1144+
1145+
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
1146+
sess.err(&format!(
1147+
"{:?}Sanitizer only works with the `{}` target",
1148+
sanitizer,
1149+
supported_targets.join("` or `")
1150+
));
1151+
}
1152+
}
11271153
}
11281154

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

src/test/run-make-fulldeps/sanitizer-address/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ endif
2323
endif
2424

2525
all:
26-
$(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) librustc_asan
26+
$(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) rustc_rt.asan
2727
# Verify that stack buffer overflow is detected:
2828
$(TMPDIR)/overflow 2>&1 | $(CGREP) stack-buffer-overflow
2929
# Verify that variable name is included in address sanitizer report:

src/test/run-make-fulldeps/sanitizer-invalid-cratetype/Makefile

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/test/run-make-fulldeps/sanitizer-invalid-cratetype/hello.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/test/run-make-fulldeps/sanitizer-invalid-target/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
all:
44
$(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | \
5-
$(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target'
5+
$(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target'

src/test/run-make-fulldeps/sanitizer-leak/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
# FIXME(#46126) ThinLTO for libstd broke this test
88

99
all:
10-
$(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) librustc_lsan
10+
$(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) rustc_rt.lsan
1111
$(TMPDIR)/leak 2>&1 | $(CGREP) 'detected memory leaks'

src/test/run-make-fulldeps/sanitizer-memory/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# only-x86_64
66

77
all:
8-
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) librustc_msan
8+
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) rustc_rt.msan
99
$(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value
10-
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) librustc_msan
10+
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) rustc_rt.msan
1111
$(TMPDIR)/maybeuninit 2>&1 | $(CGREP) use-of-uninitialized-value

0 commit comments

Comments
 (0)