Support AddressSanitizer and ThreadSanitizer on x86_64-apple-darwin.
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.
This commit is contained in:
parent
93d57d64b8
commit
00dff0aa59
14 changed files with 98 additions and 37 deletions
|
@ -54,7 +54,7 @@ matrix:
|
||||||
# version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
|
# version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
|
||||||
- env: >
|
- env: >
|
||||||
RUST_CHECK_TARGET=check
|
RUST_CHECK_TARGET=check
|
||||||
RUST_CONFIGURE_ARGS=--build=x86_64-apple-darwin
|
RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers"
|
||||||
SRC=.
|
SRC=.
|
||||||
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
|
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
|
||||||
SCCACHE_ERROR_LOG=/tmp/sccache.log
|
SCCACHE_ERROR_LOG=/tmp/sccache.log
|
||||||
|
@ -98,7 +98,7 @@ matrix:
|
||||||
install: *osx_install_sccache
|
install: *osx_install_sccache
|
||||||
- env: >
|
- env: >
|
||||||
RUST_CHECK_TARGET=dist
|
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=.
|
SRC=.
|
||||||
DEPLOY=1
|
DEPLOY=1
|
||||||
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
|
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
|
||||||
|
|
|
@ -115,6 +115,13 @@ pub fn std_link(build: &Build,
|
||||||
if target.contains("musl") && !target.contains("mips") {
|
if target.contains("musl") && !target.contains("mips") {
|
||||||
copy_musl_third_party_objects(build, target, &libdir);
|
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
|
/// 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
|
/// Build and prepare startup objects like rsbegin.o and rsend.o
|
||||||
///
|
///
|
||||||
/// These are primarily used on Windows right now for linking executables/dlls.
|
/// These are primarily used on Windows right now for linking executables/dlls.
|
||||||
|
|
|
@ -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 = env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or(env::var_os("OUT_DIR").unwrap());
|
||||||
let out_dir = PathBuf::from(out_dir).join(out_name);
|
let out_dir = PathBuf::from(out_dir).join(out_name);
|
||||||
t!(create_dir_racy(&out_dir));
|
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());
|
println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());
|
||||||
|
|
||||||
let timestamp = out_dir.join("rustbuild.timestamp");
|
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 {
|
fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
|
||||||
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
|
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
|
||||||
let meta = t!(e.metadata());
|
let meta = t!(e.metadata());
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub struct Config {
|
||||||
pub uint_type: UintTy,
|
pub uint_type: UintTy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash, Debug)]
|
||||||
pub enum Sanitizer {
|
pub enum Sanitizer {
|
||||||
Address,
|
Address,
|
||||||
Leak,
|
Leak,
|
||||||
|
|
|
@ -12,14 +12,13 @@ extern crate build_helper;
|
||||||
extern crate cmake;
|
extern crate cmake;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use build_helper::native_lib_boilerplate;
|
use build_helper::sanitizer_lib_boilerplate;
|
||||||
|
|
||||||
use cmake::Config;
|
use cmake::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||||
let native = match native_lib_boilerplate("compiler-rt", "asan", "clang_rt.asan-x86_64",
|
let native = match sanitizer_lib_boilerplate("asan") {
|
||||||
"build/lib/linux") {
|
|
||||||
Ok(native) => native,
|
Ok(native) => native,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,14 +12,13 @@ extern crate build_helper;
|
||||||
extern crate cmake;
|
extern crate cmake;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use build_helper::native_lib_boilerplate;
|
use build_helper::sanitizer_lib_boilerplate;
|
||||||
|
|
||||||
use cmake::Config;
|
use cmake::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||||
let native = match native_lib_boilerplate("compiler-rt", "lsan", "clang_rt.lsan-x86_64",
|
let native = match sanitizer_lib_boilerplate("lsan") {
|
||||||
"build/lib/linux") {
|
|
||||||
Ok(native) => native,
|
Ok(native) => native,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -799,11 +799,26 @@ impl<'a> CrateLoader<'a> {
|
||||||
|
|
||||||
fn inject_sanitizer_runtime(&mut self) {
|
fn inject_sanitizer_runtime(&mut self) {
|
||||||
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
|
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
|
||||||
// Sanitizers can only be used with x86_64 Linux executables linked
|
// Sanitizers can only be used on some tested platforms with
|
||||||
// to `std`
|
// executables linked to `std`
|
||||||
if self.sess.target.target.llvm_target != "x86_64-unknown-linux-gnu" {
|
const ASAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
|
||||||
self.sess.err(&format!("Sanitizers only work with the \
|
"x86_64-apple-darwin"];
|
||||||
`x86_64-unknown-linux-gnu` target."));
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,13 @@ extern crate build_helper;
|
||||||
extern crate cmake;
|
extern crate cmake;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use build_helper::native_lib_boilerplate;
|
use build_helper::sanitizer_lib_boilerplate;
|
||||||
|
|
||||||
use cmake::Config;
|
use cmake::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||||
let native = match native_lib_boilerplate("compiler-rt", "msan", "clang_rt.msan-x86_64",
|
let native = match sanitizer_lib_boilerplate("msan") {
|
||||||
"build/lib/linux") {
|
|
||||||
Ok(native) => native,
|
Ok(native) => native,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,14 +12,13 @@ extern crate build_helper;
|
||||||
extern crate cmake;
|
extern crate cmake;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use build_helper::native_lib_boilerplate;
|
use build_helper::sanitizer_lib_boilerplate;
|
||||||
|
|
||||||
use cmake::Config;
|
use cmake::Config;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
|
||||||
let native = match native_lib_boilerplate("compiler-rt", "tsan", "clang_rt.tsan-x86_64",
|
let native = match sanitizer_lib_boilerplate("tsan") {
|
||||||
"build/lib/linux") {
|
|
||||||
Ok(native) => native,
|
Ok(native) => native,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,10 @@ compiler_builtins = { path = "../libcompiler_builtins" }
|
||||||
std_unicode = { path = "../libstd_unicode" }
|
std_unicode = { path = "../libstd_unicode" }
|
||||||
unwind = { path = "../libunwind" }
|
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]
|
[target.x86_64-unknown-linux-gnu.dependencies]
|
||||||
rustc_asan = { path = "../librustc_asan" }
|
rustc_asan = { path = "../librustc_asan" }
|
||||||
rustc_lsan = { path = "../librustc_lsan" }
|
rustc_lsan = { path = "../librustc_lsan" }
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
-include ../tools.mk
|
-include ../tools.mk
|
||||||
|
|
||||||
# NOTE the address sanitizer only supports x86_64 linux
|
# NOTE the address sanitizer only supports x86_64 linux and macOS
|
||||||
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
|
|
||||||
else
|
|
||||||
all:
|
|
||||||
|
|
||||||
|
ifeq ($(TARGET),x86_64-apple-darwin)
|
||||||
|
ASAN_SUPPORT=$(SANITIZER_SUPPORT)
|
||||||
|
EXTRA_RUSTFLAG=-C rpath
|
||||||
|
else
|
||||||
|
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
|
endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-include ../tools.mk
|
-include ../tools.mk
|
||||||
|
|
||||||
all:
|
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'
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
-include ../tools.mk
|
-include ../tools.mk
|
||||||
|
|
||||||
ifdef SANITIZER_SUPPORT
|
|
||||||
all:
|
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
|
$(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'
|
$(TMPDIR)/leak 2>&1 | grep -q 'detected memory leaks'
|
||||||
else
|
|
||||||
all:
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
-include ../tools.mk
|
-include ../tools.mk
|
||||||
|
|
||||||
ifdef SANITIZER_SUPPORT
|
|
||||||
all:
|
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
|
$(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
|
$(TMPDIR)/uninit 2>&1 | grep -q use-of-uninitialized-value
|
||||||
else
|
|
||||||
all:
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue