diff options
| author | bors <bors@rust-lang.org> | 2020-01-10 23:26:21 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-01-10 23:26:21 +0000 |
| commit | e6217972644588a3be4fecb85b195f17b0220047 (patch) | |
| tree | 09dc5a518f57f77a7456b0be78cc747adc7d1790 /src/librustc_codegen_ssa/back | |
| parent | 175631311716d7dfeceec40d2587cde7142ffa8c (diff) | |
| parent | e88f071ed373f1eb572dee6bc6898508425126e5 (diff) | |
Auto merge of #65241 - tmiasko:no-std-san, r=alexcrichton
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 #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 #64780). cc @kennytm, @japaric, @firstyear, @choller
Diffstat (limited to 'src/librustc_codegen_ssa/back')
| -rw-r--r-- | src/librustc_codegen_ssa/back/link.rs | 89 |
1 files changed, 42 insertions, 47 deletions
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 253225f308e..17582862c4d 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -531,6 +531,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( { let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu); + link_sanitizer_runtime(sess, crate_type, &mut *linker); link_args::<B>( &mut *linker, flavor, @@ -735,6 +736,47 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( } } +fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) { + let sanitizer = match &sess.opts.debugging_opts.sanitizer { + Some(s) => s, + None => return, + }; + + if crate_type != config::CrateType::Executable { + return; + } + + let name = match sanitizer { + Sanitizer::Address => "asan", + Sanitizer::Leak => "lsan", + Sanitizer::Memory => "msan", + Sanitizer::Thread => "tsan", + }; + + let default_sysroot = filesearch::get_or_default_sysroot(); + let default_tlib = + filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple()); + + match sess.opts.target_triple.triple() { + "x86_64-apple-darwin" => { + // On Apple platforms, the sanitizer is always built as a dylib, and + // LLVM will link to `@rpath/*.dylib`, so we need to specify an + // rpath to the library as well (the rpath should be absolute, see + // PR #41352 for details). + let libname = format!("rustc_rt.{}", name); + let rpath = default_tlib.to_str().expect("non-utf8 component in path"); + linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); + linker.link_dylib(Symbol::intern(&libname)); + } + "x86_64-unknown-linux-gnu" => { + let filename = format!("librustc_rt.{}.a", name); + let path = default_tlib.join(&filename); + linker.link_whole_rlib(&path); + } + _ => {} + } +} + /// Returns a boolean indicating whether the specified crate should be ignored /// during LTO. /// @@ -1415,12 +1457,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum); } - _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) - && crate_type == config::CrateType::Executable => - { - // Link the sanitizer runtimes only if we are actually producing an executable - link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum); - } // compiler-builtins are always placed last to ensure that they're // linked correctly. _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => { @@ -1457,47 +1493,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } } - // We must link the sanitizer runtime using -Wl,--whole-archive but since - // it's packed in a .rlib, it contains stuff that are not objects that will - // make the linker error. So we must remove those bits from the .rlib before - // linking it. - fn link_sanitizer_runtime<'a, B: ArchiveBuilder<'a>>( - cmd: &mut dyn Linker, - sess: &'a Session, - codegen_results: &CodegenResults, - tmpdir: &Path, - cnum: CrateNum, - ) { - let src = &codegen_results.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - if sess.target.target.options.is_like_osx { - // On Apple platforms, the sanitizer is always built as a dylib, and - // LLVM will link to `@rpath/*.dylib`, so we need to specify an - // rpath to the library as well (the rpath should be absolute, see - // PR #41352 for details). - // - // FIXME: Remove this logic into librustc_*san once Cargo supports it - let rpath = cratepath.parent().unwrap(); - let rpath = rpath.to_str().expect("non-utf8 component in path"); - cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath)); - archive.update_symbols(); - - for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { - archive.remove_file(&f); - } - } - - archive.build(); - - cmd.link_whole_rlib(&dst); - } - // Adds the static "rlib" versions of all crates to the command line. // There's a bit of magic which happens here specifically related to LTO and // dynamic libraries. Specifically: |
