Skip to content

Commit 4abe0e5

Browse files
committed
Link sanitizer runtimes instead of injecting crate dependencies
1 parent 81ee92c commit 4abe0e5

File tree

7 files changed

+74
-149
lines changed

7 files changed

+74
-149
lines changed

src/librustc_codegen_ssa/back/link.rs

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
523523

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

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

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

src/librustc_metadata/creader.rs

Lines changed: 1 addition & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_data_structures::sync::Lrc;
99
use rustc_index::vec::IndexVec;
1010
use rustc::middle::cstore::DepKind;
1111
use rustc::session::{Session, CrateDisambiguator};
12-
use rustc::session::config::{Sanitizer, self};
12+
use rustc::session::config;
1313
use rustc_target::spec::{PanicStrategy, TargetTriple};
1414
use rustc::session::search_paths::PathKind;
1515
use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn};
@@ -639,105 +639,6 @@ impl<'a> CrateLoader<'a> {
639639
&|data| data.needs_panic_runtime());
640640
}
641641

642-
fn inject_sanitizer_runtime(&mut self) {
643-
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
644-
// Sanitizers can only be used on some tested platforms with
645-
// executables linked to `std`
646-
const ASAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
647-
"x86_64-apple-darwin"];
648-
const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
649-
"x86_64-apple-darwin"];
650-
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
651-
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
652-
653-
let supported_targets = match *sanitizer {
654-
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
655-
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
656-
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
657-
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
658-
};
659-
if !supported_targets.contains(&&*self.sess.opts.target_triple.triple()) {
660-
self.sess.err(&format!("{:?}Sanitizer only works with the `{}` target",
661-
sanitizer,
662-
supported_targets.join("` or `")
663-
));
664-
return
665-
}
666-
667-
// firstyear 2017 - during testing I was unable to access an OSX machine
668-
// to make this work on different crate types. As a result, today I have
669-
// only been able to test and support linux as a target.
670-
if self.sess.opts.target_triple.triple() == "x86_64-unknown-linux-gnu" {
671-
if !self.sess.crate_types.borrow().iter().all(|ct| {
672-
match *ct {
673-
// Link the runtime
674-
config::CrateType::Executable => true,
675-
// This crate will be compiled with the required
676-
// instrumentation pass
677-
config::CrateType::Staticlib |
678-
config::CrateType::Rlib |
679-
config::CrateType::Dylib |
680-
config::CrateType::Cdylib =>
681-
false,
682-
_ => {
683-
self.sess.err(&format!("Only executables, staticlibs, \
684-
cdylibs, dylibs and rlibs can be compiled with \
685-
`-Z sanitizer`"));
686-
false
687-
}
688-
}
689-
}) {
690-
return
691-
}
692-
} else {
693-
if !self.sess.crate_types.borrow().iter().all(|ct| {
694-
match *ct {
695-
// Link the runtime
696-
config::CrateType::Executable => true,
697-
// This crate will be compiled with the required
698-
// instrumentation pass
699-
config::CrateType::Rlib => false,
700-
_ => {
701-
self.sess.err(&format!("Only executables and rlibs can be \
702-
compiled with `-Z sanitizer`"));
703-
false
704-
}
705-
}
706-
}) {
707-
return
708-
}
709-
}
710-
711-
let mut uses_std = false;
712-
self.cstore.iter_crate_data(|_, data| {
713-
if data.name() == sym::std {
714-
uses_std = true;
715-
}
716-
});
717-
718-
if uses_std {
719-
let name = Symbol::intern(match sanitizer {
720-
Sanitizer::Address => "rustc_asan",
721-
Sanitizer::Leak => "rustc_lsan",
722-
Sanitizer::Memory => "rustc_msan",
723-
Sanitizer::Thread => "rustc_tsan",
724-
});
725-
info!("loading sanitizer: {}", name);
726-
727-
let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Explicit, None);
728-
let data = self.cstore.get_crate_data(cnum);
729-
730-
// Sanity check the loaded crate to ensure it is indeed a sanitizer runtime
731-
if !data.is_sanitizer_runtime() {
732-
self.sess.err(&format!("the crate `{}` is not a sanitizer runtime",
733-
name));
734-
}
735-
} else {
736-
self.sess.err("Must link std to be compiled with `-Z sanitizer`");
737-
}
738-
}
739-
}
740-
741642
fn inject_profiler_runtime(&mut self) {
742643
if self.sess.opts.debugging_opts.profile ||
743644
self.sess.opts.cg.profile_generate.enabled()
@@ -888,7 +789,6 @@ impl<'a> CrateLoader<'a> {
888789
}
889790

890791
pub fn postprocess(&mut self, krate: &ast::Crate) {
891-
self.inject_sanitizer_runtime();
892792
self.inject_profiler_runtime();
893793
self.inject_allocator_crate(krate);
894794
self.inject_panic_runtime(krate);

src/librustc_session/session.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,31 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
12171217
with `-Cpanic=unwind` on Windows when targeting MSVC. \
12181218
See https://github.com/rust-lang/rust/issues/61002 for details.");
12191219
}
1220+
1221+
// Sanitizers can only be used on some tested platforms.
1222+
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
1223+
const ASAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
1224+
"x86_64-apple-darwin"];
1225+
const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
1226+
"x86_64-apple-darwin"];
1227+
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
1228+
"x86_64-apple-darwin"];
1229+
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
1230+
1231+
let supported_targets = match *sanitizer {
1232+
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
1233+
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
1234+
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
1235+
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
1236+
};
1237+
1238+
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
1239+
sess.err(&format!("{:?}Sanitizer only works with the `{}` target",
1240+
sanitizer,
1241+
supported_targets.join("` or `")
1242+
));
1243+
}
1244+
}
12201245
}
12211246

12221247
/// 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) librustc_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-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) librustc_rt.lsan
1111
$(TMPDIR)/leak 2>&1 | $(CGREP) 'detected memory leaks'

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
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) librustc_rt.msan
99
$(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value

0 commit comments

Comments
 (0)