Add bootstrap step for building sanitizer runtimes

This commit is contained in:
Tomasz Miąsko 2019-11-07 00:00:00 +00:00
parent 0c6b1a7e3c
commit ae57259403
6 changed files with 159 additions and 43 deletions

View File

@ -343,6 +343,7 @@ impl<'a> Builder<'a> {
tool::Rustdoc,
tool::Clippy,
native::Llvm,
native::Sanitizers,
tool::Rustfmt,
tool::Miri,
native::Lld

View File

@ -45,7 +45,7 @@ impl Step for Std {
let compiler = builder.compiler(0, builder.config.build);
let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind));
std_cargo(builder, &compiler, target, &mut cargo);
std_cargo(builder, target, &mut cargo);
builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target));
run_cargo(

View File

@ -87,7 +87,7 @@ impl Step for Std {
target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter());
let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
std_cargo(builder, &compiler, target, &mut cargo);
std_cargo(builder, target, &mut cargo);
builder.info(&format!(
"Building stage{} std artifacts ({} -> {})",
@ -153,17 +153,18 @@ fn copy_third_party_objects(
copy_and_stamp(Path::new(&src), "libunwind.a");
}
if builder.config.sanitizers && compiler.stage != 0 {
// The sanitizers are only copied in stage1 or above,
// to avoid creating dependency on LLVM.
target_deps.extend(copy_sanitizers(builder, &compiler, target));
}
target_deps
}
/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
pub fn std_cargo(
builder: &Builder<'_>,
compiler: &Compiler,
target: Interned<String>,
cargo: &mut Cargo,
) {
pub fn std_cargo(builder: &Builder<'_>, target: Interned<String>, cargo: &mut Cargo) {
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
}
@ -206,19 +207,6 @@ pub fn std_cargo(
let mut features = builder.std_features();
features.push_str(&compiler_builtins_c_feature);
if compiler.stage != 0 && builder.config.sanitizers {
// This variable is used by the sanitizer runtime crates, e.g.
// rustc_lsan, to build the sanitizer runtime from C code
// When this variable is missing, those crates won't compile the C code,
// so we don't set this variable during stage0 where llvm-config is
// missing
// We also only build the runtimes when --enable-sanitizers (or its
// config.toml equivalent) is used
let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
cargo.env("LLVM_CONFIG", llvm_config);
cargo.env("RUSTC_BUILD_SANITIZERS", "1");
}
cargo
.arg("--features")
.arg(features)
@ -276,31 +264,43 @@ impl Step for StdLink {
let libdir = builder.sysroot_libdir(target_compiler, target);
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
if builder.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(builder, &builder.native_dir(target), "osx", &libdir);
}
}
}
fn copy_apple_sanitizer_dylibs(
/// Copies sanitizer runtime libraries into target libdir.
fn copy_sanitizers(
builder: &Builder<'_>,
native_dir: &Path,
platform: &str,
into: &Path,
) {
for &sanitizer in &["asan", "tsan"] {
let filename = format!("lib__rustc__clang_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);
builder.copy(&src_path, &into.join(filename));
compiler: &Compiler,
target: Interned<String>,
) -> Vec<PathBuf> {
let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target });
if builder.config.dry_run {
return Vec::new();
}
let mut target_deps = Vec::new();
let libdir = builder.sysroot_libdir(*compiler, target);
for runtime in &runtimes {
let dst = libdir.join(&runtime.name);
builder.copy(&runtime.path, &dst);
if target == "x86_64-apple-darwin" {
// Update the library install name reflect the fact it has been renamed.
let status = Command::new("install_name_tool")
.arg("-id")
.arg(format!("@rpath/{}", runtime.name))
.arg(&dst)
.status()
.expect("failed to execute `install_name_tool`");
assert!(status.success());
}
target_deps.push(dst);
}
target_deps
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

View File

@ -449,7 +449,7 @@ impl Step for Std {
let run_cargo_rustdoc_for = |package: &str| {
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
compile::std_cargo(builder, &compiler, target, &mut cargo);
compile::std_cargo(builder, target, &mut cargo);
// Keep a whitelist so we do not build internal stdlib crates, these will be
// build by the rustc step later if enabled.

View File

@ -546,3 +546,118 @@ impl Step for TestHelpers {
.compile("rust_test_helpers");
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Sanitizers {
pub target: Interned<String>,
}
impl Step for Sanitizers {
type Output = Vec<SanitizerRuntime>;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/llvm-project/compiler-rt").path("src/sanitizers")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Sanitizers { target: run.target });
}
/// Builds sanitizer runtime libraries.
fn run(self, builder: &Builder<'_>) -> Self::Output {
let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
if !compiler_rt_dir.exists() {
return Vec::new();
}
let out_dir = builder.native_dir(self.target).join("sanitizers");
let runtimes = supported_sanitizers(&out_dir, self.target);
if runtimes.is_empty() {
return runtimes;
}
let llvm_config = builder.ensure(Llvm { target: builder.config.build });
if builder.config.dry_run {
return runtimes;
}
let done_stamp = out_dir.join("sanitizers-finished-building");
if done_stamp.exists() {
builder.info(&format!(
"Assuming that sanitizers rebuild is not necessary. \
To force a rebuild, remove the file `{}`",
done_stamp.display()
));
return runtimes;
}
builder.info(&format!("Building sanitizers for {}", self.target));
let _time = util::timeit(&builder);
let mut cfg = cmake::Config::new(&compiler_rt_dir);
cfg.target(&self.target);
cfg.host(&builder.config.build);
cfg.profile("Release");
cfg.define("CMAKE_C_COMPILER_TARGET", self.target);
cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
cfg.define("LLVM_CONFIG_PATH", &llvm_config);
t!(fs::create_dir_all(&out_dir));
cfg.out_dir(out_dir);
for runtime in &runtimes {
cfg.build_target(&runtime.cmake_target);
cfg.build();
}
t!(fs::write(&done_stamp, b""));
runtimes
}
}
#[derive(Clone, Debug)]
pub struct SanitizerRuntime {
/// CMake target used to build the runtime.
pub cmake_target: String,
/// Path to the built runtime library.
pub path: PathBuf,
/// Library filename that will be used rustc.
pub name: String,
}
/// Returns sanitizers available on a given target.
fn supported_sanitizers(out_dir: &Path, target: Interned<String>) -> Vec<SanitizerRuntime> {
let mut result = Vec::new();
match &*target {
"x86_64-apple-darwin" => {
for s in &["asan", "lsan", "tsan"] {
result.push(SanitizerRuntime {
cmake_target: format!("clang_rt.{}_osx_dynamic", s),
path: out_dir
.join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)),
name: format!("librustc_rt.{}.dylib", s),
});
}
}
"x86_64-unknown-linux-gnu" => {
for s in &["asan", "lsan", "msan", "tsan"] {
result.push(SanitizerRuntime {
cmake_target: format!("clang_rt.{}-x86_64", s),
path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)),
name: format!("librustc_rt.{}.a", s),
});
}
}
_ => {}
}
result
}

View File

@ -1659,7 +1659,7 @@ impl Step for Crate {
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
match mode {
Mode::Std => {
compile::std_cargo(builder, &compiler, target, &mut cargo);
compile::std_cargo(builder, target, &mut cargo);
}
Mode::Rustc => {
builder.ensure(compile::Rustc { compiler, target });