]> git.lizzy.rs Git - rust.git/commitdiff
Support AddressSanitizer and ThreadSanitizer on x86_64-apple-darwin.
authorkennytm <kennytm@gmail.com>
Mon, 17 Apr 2017 20:22:16 +0000 (04:22 +0800)
committerkennytm <kennytm@gmail.com>
Tue, 25 Apr 2017 02:31:01 +0000 (10:31 +0800)
ASan and TSan are supported on macOS, and this commit enables their
support.

The sanitizers are always built as *.dylib on Apple platforms, so they
cannot be statically linked into the corresponding `rustc_?san.rlib`. The
dylibs are directly copied to `lib/rustlib/x86_64-apple-darwin/lib/`
instead.

Note, although Xcode also ships with their own copies of ASan/TSan dylibs,
we cannot use them due to version mismatch.

There is a caveat: the sanitizer libraries are linked as @rpath, so the
user needs to additionally pass `-C rpath`:

    rustc -Z sanitizer=address -C rpath file.rs
                               ^~~~~~~~

Otherwise there will be a runtime error:

    dyld: Library not loaded: @rpath/libclang_rt.asan_osx_dynamic.dylib
      Referenced from: /path/to/executable
      Reason: image not found
    Abort trap: 6

The next commit includes a temporary change in compiler to force the linker
to emit a usable @rpath.

14 files changed:
.travis.yml
src/bootstrap/compile.rs
src/build_helper/lib.rs
src/librustc/session/config.rs
src/librustc_asan/build.rs
src/librustc_lsan/build.rs
src/librustc_metadata/creader.rs
src/librustc_msan/build.rs
src/librustc_tsan/build.rs
src/libstd/Cargo.toml
src/test/run-make/sanitizer-address/Makefile
src/test/run-make/sanitizer-invalid-target/Makefile
src/test/run-make/sanitizer-leak/Makefile
src/test/run-make/sanitizer-memory/Makefile

index 5d56379dccebf2c9132d695f25425928df01a6bc..c5372609e9bc622eb8f0385fd0db681d906ba0b0 100644 (file)
@@ -54,7 +54,7 @@ matrix:
     # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
     - env: >
         RUST_CHECK_TARGET=check
-        RUST_CONFIGURE_ARGS=--build=x86_64-apple-darwin
+        RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers"
         SRC=.
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
         SCCACHE_ERROR_LOG=/tmp/sccache.log
@@ -98,7 +98,7 @@ matrix:
       install: *osx_install_sccache
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended"
+        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
index cd87b27d4f1aa65650ce896b85ecf0411d4f0c08..6f1de62d07ee3bd9267837281b990205f95050c7 100644 (file)
@@ -115,6 +115,13 @@ pub fn std_link(build: &Build,
     if target.contains("musl") && !target.contains("mips") {
         copy_musl_third_party_objects(build, target, &libdir);
     }
+
+    if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
+        // The sanitizers are only built in stage1 or above, so the dylibs will
+        // be missing in stage0 and causes panic. See the `std()` function above
+        // for reason why the sanitizers are not built in stage0.
+        copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir);
+    }
 }
 
 /// Copies the crt(1,i,n).o startup objects
@@ -126,6 +133,18 @@ fn copy_musl_third_party_objects(build: &Build, target: &str, into: &Path) {
     }
 }
 
+fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
+    for &sanitizer in &["asan", "tsan"] {
+        let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
+        let mut src_path = native_dir.join(sanitizer);
+        src_path.push("build");
+        src_path.push("lib");
+        src_path.push("darwin");
+        src_path.push(&filename);
+        copy(&src_path, &into.join(filename));
+    }
+}
+
 /// Build and prepare startup objects like rsbegin.o and rsend.o
 ///
 /// These are primarily used on Windows right now for linking executables/dlls.
index cb58a916fb7971305675e4d2a2e47ccc949569cd..da00b970da977e950bacdf275403c0a57e0e6ad7 100644 (file)
@@ -198,7 +198,11 @@ pub fn native_lib_boilerplate(src_name: &str,
     let out_dir = env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or(env::var_os("OUT_DIR").unwrap());
     let out_dir = PathBuf::from(out_dir).join(out_name);
     t!(create_dir_racy(&out_dir));
-    println!("cargo:rustc-link-lib=static={}", link_name);
+    if link_name.contains('=') {
+        println!("cargo:rustc-link-lib={}", link_name);
+    } else {
+        println!("cargo:rustc-link-lib=static={}", link_name);
+    }
     println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());
 
     let timestamp = out_dir.join("rustbuild.timestamp");
@@ -209,6 +213,21 @@ pub fn native_lib_boilerplate(src_name: &str,
     }
 }
 
+pub fn sanitizer_lib_boilerplate(sanitizer_name: &str) -> Result<NativeLibBoilerplate, ()> {
+    let (link_name, search_path) = match &*env::var("TARGET").unwrap() {
+        "x86_64-unknown-linux-gnu" => (
+            format!("clang_rt.{}-x86_64", sanitizer_name),
+            "build/lib/linux",
+        ),
+        "x86_64-apple-darwin" => (
+            format!("dylib=clang_rt.{}_osx_dynamic", sanitizer_name),
+            "build/lib/darwin",
+        ),
+        _ => return Err(()),
+    };
+    native_lib_boilerplate("compiler-rt", sanitizer_name, &link_name, search_path)
+}
+
 fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
     t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
         let meta = t!(e.metadata());
index fadb1844008c09ca0b2269f4ff61675563eb2568..462fd57cbf17f4a7de6a0d8cfb066bc983597304 100644 (file)
@@ -51,7 +51,7 @@ pub struct Config {
     pub uint_type: UintTy,
 }
 
-#[derive(Clone, Hash)]
+#[derive(Clone, Hash, Debug)]
 pub enum Sanitizer {
     Address,
     Leak,
index 2df2e001e6ff2d75594ac65896cd7e27852ba421..3a80baa0485f56cf15b32243f61d960f340d2996 100644 (file)
 extern crate cmake;
 
 use std::env;
-use build_helper::native_lib_boilerplate;
+use build_helper::sanitizer_lib_boilerplate;
 
 use cmake::Config;
 
 fn main() {
     if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
-        let native = match native_lib_boilerplate("compiler-rt", "asan", "clang_rt.asan-x86_64",
-                                                  "build/lib/linux") {
+        let native = match sanitizer_lib_boilerplate("asan") {
             Ok(native) => native,
             _ => return,
         };
index 005163f41026c39c1a99860cdd523f9f4bb4da7f..da53571a243905fe7ac7d7fc6063a001fbffdb55 100644 (file)
 extern crate cmake;
 
 use std::env;
-use build_helper::native_lib_boilerplate;
+use build_helper::sanitizer_lib_boilerplate;
 
 use cmake::Config;
 
 fn main() {
     if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
-        let native = match native_lib_boilerplate("compiler-rt", "lsan", "clang_rt.lsan-x86_64",
-                                                  "build/lib/linux") {
+        let native = match sanitizer_lib_boilerplate("lsan") {
             Ok(native) => native,
             _ => return,
         };
index 7bc0e8a512be02eb589a1d3036c494dd72b31e3a..966e814e3379075741b2809585814f95e5a5624b 100644 (file)
@@ -799,11 +799,26 @@ fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
 
     fn inject_sanitizer_runtime(&mut self) {
         if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
-            // Sanitizers can only be used with x86_64 Linux executables linked
-            // to `std`
-            if self.sess.target.target.llvm_target != "x86_64-unknown-linux-gnu" {
-                self.sess.err(&format!("Sanitizers only work with the \
-                                        `x86_64-unknown-linux-gnu` target."));
+            // Sanitizers can only be used on some tested platforms with
+            // executables linked to `std`
+            const ASAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
+                                                      "x86_64-apple-darwin"];
+            const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
+                                                      "x86_64-apple-darwin"];
+            const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
+            const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
+
+            let supported_targets = match *sanitizer {
+                Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
+                Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
+                Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
+                Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
+            };
+            if !supported_targets.contains(&&*self.sess.target.target.llvm_target) {
+                self.sess.err(&format!("{:?}Sanitizer only works with the `{}` target",
+                    sanitizer,
+                    supported_targets.join("` or `")
+                ));
                 return
             }
 
index c438b5250463b8aa4ce446fad1d3f32fbcc8bc1a..dcadbe86966e71eeab8adf40f4e2ad44d6d045e8 100644 (file)
 extern crate cmake;
 
 use std::env;
-use build_helper::native_lib_boilerplate;
+use build_helper::sanitizer_lib_boilerplate;
 
 use cmake::Config;
 
 fn main() {
     if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
-        let native = match native_lib_boilerplate("compiler-rt", "msan", "clang_rt.msan-x86_64",
-                                                  "build/lib/linux") {
+        let native = match sanitizer_lib_boilerplate("msan") {
             Ok(native) => native,
             _ => return,
         };
index 055b344d2e9479da49ee6bc7533d2152bce9d67a..5ea52f17a0fdedd73686ddd140f40fbb12d535a3 100644 (file)
 extern crate cmake;
 
 use std::env;
-use build_helper::native_lib_boilerplate;
+use build_helper::sanitizer_lib_boilerplate;
 
 use cmake::Config;
 
 fn main() {
     if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
-        let native = match native_lib_boilerplate("compiler-rt", "tsan", "clang_rt.tsan-x86_64",
-                                                  "build/lib/linux") {
+        let native = match sanitizer_lib_boilerplate("tsan") {
             Ok(native) => native,
             _ => return,
         };
index 46511452a72371c8658078987a37745218a6db64..717892be2abad5f4eb4bf9855527453d81a5228e 100644 (file)
@@ -23,6 +23,10 @@ compiler_builtins = { path = "../libcompiler_builtins" }
 std_unicode = { path = "../libstd_unicode" }
 unwind = { path = "../libunwind" }
 
+[target.x86_64-apple-darwin.dependencies]
+rustc_asan = { path = "../librustc_asan" }
+rustc_tsan = { path = "../librustc_tsan" }
+
 [target.x86_64-unknown-linux-gnu.dependencies]
 rustc_asan = { path = "../librustc_asan" }
 rustc_lsan = { path = "../librustc_lsan" }
index 5931145f3a47d014033ff1a1744043d4edc0d729..61b25df1451b336895e189500c5a0fbb5335ae4a 100644 (file)
@@ -1,11 +1,19 @@
 -include ../tools.mk
 
-# NOTE the address sanitizer only supports x86_64 linux
-ifdef SANITIZER_SUPPORT
-all:
-       $(RUSTC) -g -Z sanitizer=address -Z print-link-args overflow.rs | grep -q librustc_asan
-       $(TMPDIR)/overflow 2>&1 | grep -q stack-buffer-overflow
+# NOTE the address sanitizer only supports x86_64 linux and macOS
+
+ifeq ($(TARGET),x86_64-apple-darwin)
+ASAN_SUPPORT=$(SANITIZER_SUPPORT)
+EXTRA_RUSTFLAG=-C rpath
 else
-all:
+ifeq ($(TARGET),x86_64-unknown-linux-gnu)
+ASAN_SUPPORT=$(SANITIZER_SUPPORT)
+EXTRA_RUSTFLAG=
+endif
+endif
 
+all:
+ifeq ($(ASAN_SUPPORT),1)
+       $(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | grep -q librustc_asan
+       $(TMPDIR)/overflow 2>&1 | grep -q stack-buffer-overflow
 endif
index 6a1ce8bab2fb6c064668dd3792d970a50aa30616..82e32f0995202e02e5990024745326fb20c4abcf 100644 (file)
@@ -1,4 +1,4 @@
 -include ../tools.mk
 
 all:
-       $(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | grep -q 'Sanitizers only work with the `x86_64-unknown-linux-gnu` target'
+       $(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | grep -q 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target'
index f02d948fdc84f58460e9fe409e1cfd541e48bc7e..b18dd1d45eda48fd6cd014d392bf1ec60a39fbed 100644 (file)
@@ -1,10 +1,10 @@
 -include ../tools.mk
 
-ifdef SANITIZER_SUPPORT
 all:
+ifeq ($(TARGET),x86_64-unknown-linux-gnu)
+ifdef SANITIZER_SUPPORT
        $(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | grep -q librustc_lsan
        $(TMPDIR)/leak 2>&1 | grep -q 'detected memory leaks'
-else
-all:
-
 endif
+endif
+
index 08682e5975e5163e80843f3b5a2c14f90e727092..7502ef0e7a7b750467ecde8e32e013b21d87557d 100644 (file)
@@ -1,10 +1,10 @@
 -include ../tools.mk
 
-ifdef SANITIZER_SUPPORT
 all:
+ifeq ($(TARGET),x86_64-unknown-linux-gnu)
+ifdef SANITIZER_SUPPORT
        $(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | grep -q librustc_msan
        $(TMPDIR)/uninit 2>&1 | grep -q use-of-uninitialized-value
-else
-all:
-
 endif
+endif
+