Skip to content

Commit 0ee56f6

Browse files
committed
Auto merge of rust-lang#41352 - kennytm:macos-sanitizers, r=alexcrichton
Support AddressSanitizer and ThreadSanitizer on x86_64-apple-darwin [ASan](https://clang.llvm.org/docs/AddressSanitizer.html#supported-platforms) and [TSan](https://clang.llvm.org/docs/ThreadSanitizer.html#supported-platforms) 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/` (due to https://reviews.llvm.org/D6018), so the user needs to additionally pass `-C rpath`:~~ **Edit:** Passing rpath is now automatic.
2 parents b0a4074 + 164fd69 commit 0ee56f6

File tree

17 files changed

+115
-39
lines changed

17 files changed

+115
-39
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ matrix:
5454
# version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
5555
- env: >
5656
RUST_CHECK_TARGET=check
57-
RUST_CONFIGURE_ARGS=--build=x86_64-apple-darwin
57+
RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers"
5858
SRC=.
5959
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
6060
SCCACHE_ERROR_LOG=/tmp/sccache.log
@@ -98,7 +98,7 @@ matrix:
9898
install: *osx_install_sccache
9999
- env: >
100100
RUST_CHECK_TARGET=dist
101-
RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended"
101+
RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers"
102102
SRC=.
103103
DEPLOY=1
104104
RUSTC_RETRY_LINKER_ON_SEGFAULT=1

src/bootstrap/compile.rs

+19
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ pub fn std_link(build: &Build,
115115
if target.contains("musl") && !target.contains("mips") {
116116
copy_musl_third_party_objects(build, target, &libdir);
117117
}
118+
119+
if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
120+
// The sanitizers are only built in stage1 or above, so the dylibs will
121+
// be missing in stage0 and causes panic. See the `std()` function above
122+
// for reason why the sanitizers are not built in stage0.
123+
copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir);
124+
}
118125
}
119126

120127
/// 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) {
126133
}
127134
}
128135

136+
fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
137+
for &sanitizer in &["asan", "tsan"] {
138+
let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
139+
let mut src_path = native_dir.join(sanitizer);
140+
src_path.push("build");
141+
src_path.push("lib");
142+
src_path.push("darwin");
143+
src_path.push(&filename);
144+
copy(&src_path, &into.join(filename));
145+
}
146+
}
147+
129148
/// Build and prepare startup objects like rsbegin.o and rsend.o
130149
///
131150
/// These are primarily used on Windows right now for linking executables/dlls.

src/bootstrap/metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fn build_krate(build: &mut Build, krate: &str) {
5858
// the dependency graph and what `-p` arguments there are.
5959
let mut cargo = Command::new(&build.cargo);
6060
cargo.arg("metadata")
61+
.arg("--format-version").arg("1")
6162
.arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml"));
6263
let output = output(&mut cargo);
6364
let output: Output = json::decode(&output).unwrap();

src/build_helper/lib.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@ pub fn native_lib_boilerplate(src_name: &str,
198198
let out_dir = env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or(env::var_os("OUT_DIR").unwrap());
199199
let out_dir = PathBuf::from(out_dir).join(out_name);
200200
t!(create_dir_racy(&out_dir));
201-
println!("cargo:rustc-link-lib=static={}", link_name);
201+
if link_name.contains('=') {
202+
println!("cargo:rustc-link-lib={}", link_name);
203+
} else {
204+
println!("cargo:rustc-link-lib=static={}", link_name);
205+
}
202206
println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());
203207

204208
let timestamp = out_dir.join("rustbuild.timestamp");
@@ -209,6 +213,21 @@ pub fn native_lib_boilerplate(src_name: &str,
209213
}
210214
}
211215

216+
pub fn sanitizer_lib_boilerplate(sanitizer_name: &str) -> Result<NativeLibBoilerplate, ()> {
217+
let (link_name, search_path) = match &*env::var("TARGET").unwrap() {
218+
"x86_64-unknown-linux-gnu" => (
219+
format!("clang_rt.{}-x86_64", sanitizer_name),
220+
"build/lib/linux",
221+
),
222+
"x86_64-apple-darwin" => (
223+
format!("dylib=clang_rt.{}_osx_dynamic", sanitizer_name),
224+
"build/lib/darwin",
225+
),
226+
_ => return Err(()),
227+
};
228+
native_lib_boilerplate("compiler-rt", sanitizer_name, &link_name, search_path)
229+
}
230+
212231
fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
213232
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
214233
let meta = t!(e.metadata());

src/librustc/session/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub struct Config {
5151
pub uint_type: UintTy,
5252
}
5353

54-
#[derive(Clone, Hash)]
54+
#[derive(Clone, Hash, Debug)]
5555
pub enum Sanitizer {
5656
Address,
5757
Leak,

src/librustc_asan/build.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ extern crate build_helper;
1212
extern crate cmake;
1313

1414
use std::env;
15-
use build_helper::native_lib_boilerplate;
15+
use build_helper::sanitizer_lib_boilerplate;
1616

1717
use cmake::Config;
1818

1919
fn main() {
2020
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
21-
let native = match native_lib_boilerplate("compiler-rt", "asan", "clang_rt.asan-x86_64",
22-
"build/lib/linux") {
21+
let native = match sanitizer_lib_boilerplate("asan") {
2322
Ok(native) => native,
2423
_ => return,
2524
};

src/librustc_lsan/build.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ extern crate build_helper;
1212
extern crate cmake;
1313

1414
use std::env;
15-
use build_helper::native_lib_boilerplate;
15+
use build_helper::sanitizer_lib_boilerplate;
1616

1717
use cmake::Config;
1818

1919
fn main() {
2020
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
21-
let native = match native_lib_boilerplate("compiler-rt", "lsan", "clang_rt.lsan-x86_64",
22-
"build/lib/linux") {
21+
let native = match sanitizer_lib_boilerplate("lsan") {
2322
Ok(native) => native,
2423
_ => return,
2524
};

src/librustc_metadata/creader.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -799,11 +799,26 @@ impl<'a> CrateLoader<'a> {
799799

800800
fn inject_sanitizer_runtime(&mut self) {
801801
if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
802-
// Sanitizers can only be used with x86_64 Linux executables linked
803-
// to `std`
804-
if self.sess.target.target.llvm_target != "x86_64-unknown-linux-gnu" {
805-
self.sess.err(&format!("Sanitizers only work with the \
806-
`x86_64-unknown-linux-gnu` target."));
802+
// Sanitizers can only be used on some tested platforms with
803+
// executables linked to `std`
804+
const ASAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
805+
"x86_64-apple-darwin"];
806+
const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu",
807+
"x86_64-apple-darwin"];
808+
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
809+
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
810+
811+
let supported_targets = match *sanitizer {
812+
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
813+
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
814+
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
815+
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
816+
};
817+
if !supported_targets.contains(&&*self.sess.target.target.llvm_target) {
818+
self.sess.err(&format!("{:?}Sanitizer only works with the `{}` target",
819+
sanitizer,
820+
supported_targets.join("` or `")
821+
));
807822
return
808823
}
809824

src/librustc_msan/build.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ extern crate build_helper;
1212
extern crate cmake;
1313

1414
use std::env;
15-
use build_helper::native_lib_boilerplate;
15+
use build_helper::sanitizer_lib_boilerplate;
1616

1717
use cmake::Config;
1818

1919
fn main() {
2020
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
21-
let native = match native_lib_boilerplate("compiler-rt", "msan", "clang_rt.msan-x86_64",
22-
"build/lib/linux") {
21+
let native = match sanitizer_lib_boilerplate("msan") {
2322
Ok(native) => native,
2423
_ => return,
2524
};

src/librustc_trans/back/link.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,19 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
11221122
cnum: CrateNum) {
11231123
let src = sess.cstore.used_crate_source(cnum);
11241124
let cratepath = &src.rlib.unwrap().0;
1125+
1126+
if sess.target.target.options.is_like_osx {
1127+
// On Apple platforms, the sanitizer is always built as a dylib, and
1128+
// LLVM will link to `@rpath/*.dylib`, so we need to specify an
1129+
// rpath to the library as well (the rpath should be absolute, see
1130+
// PR #41352 for details).
1131+
//
1132+
// FIXME: Remove this logic into librustc_*san once Cargo supports it
1133+
let rpath = cratepath.parent().unwrap();
1134+
let rpath = rpath.to_str().expect("non-utf8 component in path");
1135+
cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
1136+
}
1137+
11251138
let dst = tmpdir.join(cratepath.file_name().unwrap());
11261139
let cfg = archive_config(sess, &dst, Some(cratepath));
11271140
let mut archive = ArchiveBuilder::new(cfg);

src/librustc_tsan/build.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ extern crate build_helper;
1212
extern crate cmake;
1313

1414
use std::env;
15-
use build_helper::native_lib_boilerplate;
15+
use build_helper::sanitizer_lib_boilerplate;
1616

1717
use cmake::Config;
1818

1919
fn main() {
2020
if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
21-
let native = match native_lib_boilerplate("compiler-rt", "tsan", "clang_rt.tsan-x86_64",
22-
"build/lib/linux") {
21+
let native = match sanitizer_lib_boilerplate("tsan") {
2322
Ok(native) => native,
2423
_ => return,
2524
};

src/libstd/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ compiler_builtins = { path = "../libcompiler_builtins" }
2323
std_unicode = { path = "../libstd_unicode" }
2424
unwind = { path = "../libunwind" }
2525

26+
[target.x86_64-apple-darwin.dependencies]
27+
rustc_asan = { path = "../librustc_asan" }
28+
rustc_tsan = { path = "../librustc_tsan" }
29+
2630
[target.x86_64-unknown-linux-gnu.dependencies]
2731
rustc_asan = { path = "../librustc_asan" }
2832
rustc_lsan = { path = "../librustc_lsan" }
+14-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
-include ../tools.mk
22

3-
# NOTE the address sanitizer only supports x86_64 linux
4-
ifdef SANITIZER_SUPPORT
5-
all:
6-
$(RUSTC) -g -Z sanitizer=address -Z print-link-args overflow.rs | grep -q librustc_asan
7-
$(TMPDIR)/overflow 2>&1 | grep -q stack-buffer-overflow
3+
# NOTE the address sanitizer only supports x86_64 linux and macOS
4+
5+
ifeq ($(TARGET),x86_64-apple-darwin)
6+
ASAN_SUPPORT=$(SANITIZER_SUPPORT)
7+
EXTRA_RUSTFLAG=-C rpath
88
else
9-
all:
9+
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
10+
ASAN_SUPPORT=$(SANITIZER_SUPPORT)
11+
EXTRA_RUSTFLAG=
12+
endif
13+
endif
1014

15+
all:
16+
ifeq ($(ASAN_SUPPORT),1)
17+
$(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | grep -q librustc_asan
18+
$(TMPDIR)/overflow 2>&1 | grep -q stack-buffer-overflow
1119
endif
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
-include ../tools.mk
22

33
all:
4-
$(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'
4+
$(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'
+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
-include ../tools.mk
22

3-
ifdef SANITIZER_SUPPORT
43
all:
4+
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
5+
ifdef SANITIZER_SUPPORT
56
$(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | grep -q librustc_lsan
67
$(TMPDIR)/leak 2>&1 | grep -q 'detected memory leaks'
7-
else
8-
all:
9-
108
endif
9+
endif
10+
+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
-include ../tools.mk
22

3-
ifdef SANITIZER_SUPPORT
43
all:
4+
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
5+
ifdef SANITIZER_SUPPORT
56
$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | grep -q librustc_msan
67
$(TMPDIR)/uninit 2>&1 | grep -q use-of-uninitialized-value
7-
else
8-
all:
9-
108
endif
9+
endif
10+

src/test/run-make/sysroot-crates-are-unstable/Makefile

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
-include ../tools.mk
22

3-
# This is a whitelist of crates which are stable, we don't check for the
4-
# instability of these crates as they're all stable!
3+
# This is a whitelist of files which are stable crates or simply are not crates,
4+
# we don't check for the instability of these crates as they're all stable!
55
STABLE_CRATES := \
66
std \
77
core \
88
proc_macro \
99
rsbegin.o \
1010
rsend.o \
1111
dllcrt2.o \
12-
crt2.o
12+
crt2.o \
13+
clang_rt.%_dynamic.dylib
1314

1415
# Generate a list of all crates in the sysroot. To do this we list all files in
1516
# rustc's sysroot, look at the filename, strip everything after the `-`, and

0 commit comments

Comments
 (0)