From 9af6aa38895d3c1d263c52984666893b9ca22fe1 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Thu, 29 Dec 2016 23:28:11 -0500
Subject: [PATCH 1/8] sanitizer support

---
 src/Cargo.lock                                | 40 ++++++++++++
 src/bootstrap/check.rs                        |  5 +-
 src/bootstrap/compile.rs                      | 12 ++++
 src/bootstrap/config.rs                       |  3 +
 src/bootstrap/lib.rs                          |  7 +-
 src/compiler-rt                               |  2 +-
 src/librustc/middle/cstore.rs                 |  2 +
 src/librustc/session/config.rs                | 25 +++++++-
 src/librustc_asan/Cargo.toml                  | 16 +++++
 src/librustc_asan/build.rs                    | 50 +++++++++++++++
 src/librustc_asan/lib.rs                      | 20 ++++++
 src/librustc_llvm/ffi.rs                      |  3 +
 src/librustc_lsan/Cargo.toml                  | 16 +++++
 src/librustc_lsan/build.rs                    | 50 +++++++++++++++
 src/librustc_lsan/lib.rs                      | 20 ++++++
 src/librustc_metadata/creader.rs              | 64 ++++++++++++++++++-
 src/librustc_metadata/cstore.rs               |  5 ++
 src/librustc_metadata/cstore_impl.rs          |  4 ++
 src/librustc_msan/Cargo.toml                  | 16 +++++
 src/librustc_msan/build.rs                    | 50 +++++++++++++++
 src/librustc_msan/lib.rs                      | 20 ++++++
 src/librustc_trans/back/link.rs               | 33 ++++++++++
 src/librustc_trans/back/write.rs              | 18 +++++-
 src/librustc_trans/declare.rs                 | 16 +++++
 src/librustc_tsan/Cargo.toml                  | 16 +++++
 src/librustc_tsan/build.rs                    | 50 +++++++++++++++
 src/librustc_tsan/lib.rs                      | 20 ++++++
 src/libstd/Cargo.toml                         | 10 +++
 src/libsyntax/feature_gate.rs                 | 10 +++
 src/rustc/std_shim/Cargo.toml                 |  4 ++
 src/rustllvm/RustWrapper.cpp                  |  6 ++
 src/rustllvm/rustllvm.h                       |  3 +
 .../feature-gate-sanitizer-runtime.rs         | 13 ++++
 src/test/run-make/sanitizer-address/Makefile  | 21 ++++++
 .../run-make/sanitizer-address/overflow.rs    | 14 ++++
 src/test/run-make/sanitizer-dylib/Makefile    |  4 ++
 src/test/run-make/sanitizer-dylib/hello.rs    | 13 ++++
 .../sanitizer-invalid-target/Makefile         |  4 ++
 .../sanitizer-invalid-target/hello.rs         | 13 ++++
 src/test/run-make/sanitizer-leak/Makefile     | 23 +++++++
 src/test/run-make/sanitizer-leak/leak.rs      | 16 +++++
 src/test/run-make/sanitizer-memory/Makefile   | 21 ++++++
 src/test/run-make/sanitizer-memory/uninit.rs  | 16 +++++
 src/test/run-make/sanitizer-thread/Makefile   | 21 ++++++
 src/test/run-make/sanitizer-thread/racy.rs    | 21 ++++++
 45 files changed, 810 insertions(+), 6 deletions(-)
 create mode 100644 src/librustc_asan/Cargo.toml
 create mode 100644 src/librustc_asan/build.rs
 create mode 100644 src/librustc_asan/lib.rs
 create mode 100644 src/librustc_lsan/Cargo.toml
 create mode 100644 src/librustc_lsan/build.rs
 create mode 100644 src/librustc_lsan/lib.rs
 create mode 100644 src/librustc_msan/Cargo.toml
 create mode 100644 src/librustc_msan/build.rs
 create mode 100644 src/librustc_msan/lib.rs
 create mode 100644 src/librustc_tsan/Cargo.toml
 create mode 100644 src/librustc_tsan/build.rs
 create mode 100644 src/librustc_tsan/lib.rs
 create mode 100644 src/test/compile-fail/feature-gate-sanitizer-runtime.rs
 create mode 100644 src/test/run-make/sanitizer-address/Makefile
 create mode 100644 src/test/run-make/sanitizer-address/overflow.rs
 create mode 100644 src/test/run-make/sanitizer-dylib/Makefile
 create mode 100644 src/test/run-make/sanitizer-dylib/hello.rs
 create mode 100644 src/test/run-make/sanitizer-invalid-target/Makefile
 create mode 100644 src/test/run-make/sanitizer-invalid-target/hello.rs
 create mode 100644 src/test/run-make/sanitizer-leak/Makefile
 create mode 100644 src/test/run-make/sanitizer-leak/leak.rs
 create mode 100644 src/test/run-make/sanitizer-memory/Makefile
 create mode 100644 src/test/run-make/sanitizer-memory/uninit.rs
 create mode 100644 src/test/run-make/sanitizer-thread/Makefile
 create mode 100644 src/test/run-make/sanitizer-thread/racy.rs

diff --git a/src/Cargo.lock b/src/Cargo.lock
index 06cf32ad0f6b5..8e987ba3b7f15 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -268,6 +268,15 @@ name = "rustc-serialize"
 version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "rustc_asan"
+version = "0.0.0"
+dependencies = [
+ "alloc_system 0.0.0",
+ "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core 0.0.0",
+]
+
 [[package]]
 name = "rustc_back"
 version = "0.0.0"
@@ -401,6 +410,15 @@ dependencies = [
  "rustc_bitflags 0.0.0",
 ]
 
+[[package]]
+name = "rustc_lsan"
+version = "0.0.0"
+dependencies = [
+ "alloc_system 0.0.0",
+ "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core 0.0.0",
+]
+
 [[package]]
 name = "rustc_metadata"
 version = "0.0.0"
@@ -435,6 +453,15 @@ dependencies = [
  "syntax_pos 0.0.0",
 ]
 
+[[package]]
+name = "rustc_msan"
+version = "0.0.0"
+dependencies = [
+ "alloc_system 0.0.0",
+ "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core 0.0.0",
+]
+
 [[package]]
 name = "rustc_passes"
 version = "0.0.0"
@@ -516,6 +543,15 @@ dependencies = [
  "syntax_pos 0.0.0",
 ]
 
+[[package]]
+name = "rustc_tsan"
+version = "0.0.0"
+dependencies = [
+ "alloc_system 0.0.0",
+ "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core 0.0.0",
+]
+
 [[package]]
 name = "rustc_typeck"
 version = "0.0.0"
@@ -577,6 +613,10 @@ dependencies = [
  "panic_abort 0.0.0",
  "panic_unwind 0.0.0",
  "rand 0.0.0",
+ "rustc_asan 0.0.0",
+ "rustc_lsan 0.0.0",
+ "rustc_msan 0.0.0",
+ "rustc_tsan 0.0.0",
  "std_unicode 0.0.0",
  "unwind 0.0.0",
 ]
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 19aac0f36bb27..573d0df0cee20 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -332,7 +332,10 @@ pub fn krate(build: &Build,
              krate: Option<&str>) {
     let (name, path, features, root) = match mode {
         Mode::Libstd => {
-            ("libstd", "src/rustc/std_shim", build.std_features(), "std_shim")
+            ("libstd",
+             "src/rustc/std_shim",
+             build.std_features(),
+             "std_shim")
         }
         Mode::Libtest => {
             ("libtest", "src/rustc/test_shim", String::new(), "test_shim")
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 776b91028a1a3..3e29339a75b47 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -51,6 +51,18 @@ pub fn std(build: &Build, target: &str, compiler: &Compiler) {
     if compiler.stage == 0 && build.local_rebuild && !build.config.use_jemalloc {
         features.push_str(" force_alloc_system");
     }
+
+    if compiler.stage != 0 && !build.system_llvm(target) {
+        // 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 don't build the runtimes when compiling against system llvm
+        // because some distributions ship llvm packages that have a directory
+        // layout different from the one that the runtime's build system expects
+        cargo.env("LLVM_CONFIG", build.llvm_config(target));
+    }
     cargo.arg("--features").arg(features)
          .arg("--manifest-path")
          .arg(build.src.join("src/rustc/std_shim/Cargo.toml"));
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 6e077691b3a05..b171c89c20ad3 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -108,6 +108,8 @@ pub struct Config {
 /// Per-target configuration stored in the global configuration structure.
 #[derive(Default)]
 pub struct Target {
+    // `true` if compiling against system LLVM or a pre-built LLVM
+    pub system_llvm: bool,
     pub llvm_config: Option<PathBuf>,
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
@@ -512,6 +514,7 @@ impl Config {
                                      .or_insert(Target::default());
                     let root = parse_configure_path(value);
                     target.llvm_config = Some(push_exe_path(root, &["bin", "llvm-config"]));
+                    target.system_llvm = true;
                 }
                 "CFG_JEMALLOC_ROOT" if value.len() > 0 => {
                     let target = self.target_config.entry(self.build.clone())
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index df1218752d1c9..21dd4b1520a88 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -599,7 +599,8 @@ impl Build {
     /// Get the space-separated set of activated features for the standard
     /// library.
     fn std_features(&self) -> String {
-        let mut features = "panic-unwind".to_string();
+        let mut features = "panic-unwind asan lsan msan tsan".to_string();
+
         if self.config.debug_jemalloc {
             features.push_str(" debug-jemalloc");
         }
@@ -716,6 +717,10 @@ impl Build {
         }
     }
 
+    fn system_llvm(&self, target: &str) -> bool {
+        self.config.target_config.get(target).map(|t| t.system_llvm).unwrap_or(false)
+    }
+
     /// Returns the path to `FileCheck` binary for the specified target
     fn llvm_filecheck(&self, target: &str) -> PathBuf {
         let target_config = self.config.target_config.get(target);
diff --git a/src/compiler-rt b/src/compiler-rt
index a8fc4c169fac4..d30da544a8afc 160000
--- a/src/compiler-rt
+++ b/src/compiler-rt
@@ -1 +1 @@
-Subproject commit a8fc4c169fac43a5dc204d4fd56ddb1739f8c178
+Subproject commit d30da544a8afc5d78391dee270bdf40e74a215d3
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 6537cc1adce00..2d80fc32c469d 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -229,6 +229,7 @@ pub trait CrateStore<'tcx> {
     fn is_allocator(&self, cnum: CrateNum) -> bool;
     fn is_panic_runtime(&self, cnum: CrateNum) -> bool;
     fn is_compiler_builtins(&self, cnum: CrateNum) -> bool;
+    fn is_sanitizer_runtime(&self, cnum: CrateNum) -> bool;
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy;
     fn extern_crate(&self, cnum: CrateNum) -> Option<ExternCrate>;
     /// The name of the crate as it is referred to in source code of the current
@@ -390,6 +391,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn is_allocator(&self, cnum: CrateNum) -> bool { bug!("is_allocator") }
     fn is_panic_runtime(&self, cnum: CrateNum) -> bool { bug!("is_panic_runtime") }
     fn is_compiler_builtins(&self, cnum: CrateNum) -> bool { bug!("is_compiler_builtins") }
+    fn is_sanitizer_runtime(&self, cnum: CrateNum) -> bool { bug!("is_sanitizer_runtime") }
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy {
         bug!("panic_strategy")
     }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index d41c2ba93b935..24615f2fa6992 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -51,6 +51,14 @@ pub struct Config {
     pub uint_type: UintTy,
 }
 
+#[derive(Clone)]
+pub enum Sanitizer {
+    Address,
+    Leak,
+    Memory,
+    Thread,
+}
+
 #[derive(Clone, Copy, PartialEq, Hash)]
 pub enum OptLevel {
     No, // -O0
@@ -626,11 +634,13 @@ macro_rules! options {
             Some("a number");
         pub const parse_panic_strategy: Option<&'static str> =
             Some("either `panic` or `abort`");
+        pub const parse_sanitizer: Option<&'static str> =
+            Some("one of: `address`, `leak`, `memory` or `thread`");
     }
 
     #[allow(dead_code)]
     mod $mod_set {
-        use super::{$struct_name, Passes, SomePasses, AllPasses};
+        use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer};
         use rustc_back::PanicStrategy;
 
         $(
@@ -751,6 +761,17 @@ macro_rules! options {
             }
             true
         }
+
+        fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
+            match v {
+                Some("address") => *slote = Some(Sanitizer::Address),
+                Some("leak") => *slote = Some(Sanitizer::Leak),
+                Some("memory") => *slote = Some(Sanitizer::Memory),
+                Some("thread") => *slote = Some(Sanitizer::Thread),
+                _ => return false,
+            }
+            true
+        }
     }
 ) }
 
@@ -949,6 +970,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "encode MIR of all functions into the crate metadata"),
     osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
           "pass `-install_name @rpath/...` to the OSX linker"),
+    sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [UNTRACKED],
+                                   "Use a sanitizer"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc_asan/Cargo.toml b/src/librustc_asan/Cargo.toml
new file mode 100644
index 0000000000000..abbd7cc0966ea
--- /dev/null
+++ b/src/librustc_asan/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+name = "rustc_asan"
+version = "0.0.0"
+
+[lib]
+name = "rustc_asan"
+path = "lib.rs"
+
+[build-dependencies]
+cmake = "0.1.18"
+
+[dependencies]
+alloc_system = { path = "../liballoc_system" }
+core = { path = "../libcore" }
diff --git a/src/librustc_asan/build.rs b/src/librustc_asan/build.rs
new file mode 100644
index 0000000000000..3e33efcadb807
--- /dev/null
+++ b/src/librustc_asan/build.rs
@@ -0,0 +1,50 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate cmake;
+
+use std::path::PathBuf;
+use std::env;
+
+use cmake::Config;
+
+fn main() {
+    if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
+        let dst = Config::new("../compiler-rt")
+            .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
+            .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
+            .define("COMPILER_RT_BUILD_XRAY", "OFF")
+            .define("LLVM_CONFIG_PATH", llvm_config)
+            .build_target("asan")
+            .build();
+
+        println!("cargo:rustc-link-search=native={}",
+                 dst.join("build/lib/linux").display());
+        println!("cargo:rustc-link-lib=static=clang_rt.asan-x86_64");
+
+        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
+        let mut stack = src_dir.join("../compiler-rt")
+            .read_dir()
+            .unwrap()
+            .map(|e| e.unwrap())
+            .filter(|e| &*e.file_name() != ".git")
+            .collect::<Vec<_>>();
+        while let Some(entry) = stack.pop() {
+            let path = entry.path();
+            if entry.file_type().unwrap().is_dir() {
+                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
+            } else {
+                println!("cargo:rerun-if-changed={}", path.display());
+            }
+        }
+    }
+
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/librustc_asan/lib.rs b/src/librustc_asan/lib.rs
new file mode 100644
index 0000000000000..71a166b91ebcb
--- /dev/null
+++ b/src/librustc_asan/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(not(stage0), feature(sanitizer_runtime))]
+#![cfg_attr(not(stage0), sanitizer_runtime)]
+#![feature(alloc_system)]
+#![feature(staged_api)]
+#![no_std]
+#![unstable(feature = "sanitizer_runtime_lib",
+            reason = "internal implementation detail of sanitizers",
+            issue = "0")]
+
+extern crate alloc_system;
diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs
index 8510b9f523cb5..6c9976ca3f8e4 100644
--- a/src/librustc_llvm/ffi.rs
+++ b/src/librustc_llvm/ffi.rs
@@ -126,6 +126,9 @@ pub enum Attribute {
     UWTable         = 17,
     ZExt            = 18,
     InReg           = 19,
+    SanitizeThread  = 20,
+    SanitizeAddress  = 21,
+    SanitizeMemory  = 22,
 }
 
 /// LLVMIntPredicate
diff --git a/src/librustc_lsan/Cargo.toml b/src/librustc_lsan/Cargo.toml
new file mode 100644
index 0000000000000..ac53f3fe73a75
--- /dev/null
+++ b/src/librustc_lsan/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+name = "rustc_lsan"
+version = "0.0.0"
+
+[lib]
+name = "rustc_lsan"
+path = "lib.rs"
+
+[build-dependencies]
+cmake = "0.1.18"
+
+[dependencies]
+alloc_system = { path = "../liballoc_system" }
+core = { path = "../libcore" }
diff --git a/src/librustc_lsan/build.rs b/src/librustc_lsan/build.rs
new file mode 100644
index 0000000000000..f13928d2bd457
--- /dev/null
+++ b/src/librustc_lsan/build.rs
@@ -0,0 +1,50 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate cmake;
+
+use std::path::PathBuf;
+use std::env;
+
+use cmake::Config;
+
+fn main() {
+    if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
+        let dst = Config::new("../compiler-rt")
+            .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
+            .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
+            .define("COMPILER_RT_BUILD_XRAY", "OFF")
+            .define("LLVM_CONFIG_PATH", llvm_config)
+            .build_target("lsan")
+            .build();
+
+        println!("cargo:rustc-link-search=native={}",
+                 dst.join("build/lib/linux").display());
+        println!("cargo:rustc-link-lib=static=clang_rt.lsan-x86_64");
+
+        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
+        let mut stack = src_dir.join("../compiler-rt")
+            .read_dir()
+            .unwrap()
+            .map(|e| e.unwrap())
+            .filter(|e| &*e.file_name() != ".git")
+            .collect::<Vec<_>>();
+        while let Some(entry) = stack.pop() {
+            let path = entry.path();
+            if entry.file_type().unwrap().is_dir() {
+                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
+            } else {
+                println!("cargo:rerun-if-changed={}", path.display());
+            }
+        }
+    }
+
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/librustc_lsan/lib.rs b/src/librustc_lsan/lib.rs
new file mode 100644
index 0000000000000..71a166b91ebcb
--- /dev/null
+++ b/src/librustc_lsan/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(not(stage0), feature(sanitizer_runtime))]
+#![cfg_attr(not(stage0), sanitizer_runtime)]
+#![feature(alloc_system)]
+#![feature(staged_api)]
+#![no_std]
+#![unstable(feature = "sanitizer_runtime_lib",
+            reason = "internal implementation detail of sanitizers",
+            issue = "0")]
+
+extern crate alloc_system;
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 81a4f7c93b6e9..55dc5aa2876f6 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -17,7 +17,8 @@ use schema::CrateRoot;
 use rustc::hir::def_id::{CrateNum, DefIndex};
 use rustc::hir::svh::Svh;
 use rustc::middle::cstore::DepKind;
-use rustc::session::{config, Session};
+use rustc::session::Session;
+use rustc::session::config::{Sanitizer, self};
 use rustc_back::PanicStrategy;
 use rustc::session::search_paths::PathKind;
 use rustc::middle;
@@ -786,6 +787,64 @@ impl<'a> CrateLoader<'a> {
                                   &|data| data.needs_panic_runtime());
     }
 
+    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."));
+                return
+            }
+
+            if !self.sess.crate_types.borrow().iter().all(|ct| {
+                match *ct {
+                    // Link the runtime
+                    config::CrateTypeExecutable => true,
+                    // This crate will be compiled with the required
+                    // instrumentation pass
+                    config::CrateTypeRlib => false,
+                    _ => {
+                        self.sess.err(&format!("Only executables and rlibs can be \
+                                                compiled with `-Z sanitizer`"));
+                        false
+                    }
+                }
+            }) {
+                return
+            }
+
+            let mut uses_std = false;
+            self.cstore.iter_crate_data(|_, data| {
+                if data.name == "std" {
+                    uses_std = true;
+                }
+            });
+
+            if uses_std {
+                let name = match *sanitizer {
+                    Sanitizer::Address => "rustc_asan",
+                    Sanitizer::Leak => "rustc_lsan",
+                    Sanitizer::Memory => "rustc_msan",
+                    Sanitizer::Thread => "rustc_tsan",
+                };
+                info!("loading sanitizer: {}", name);
+
+                let symbol = Symbol::intern(name);
+                let dep_kind = DepKind::Implicit;
+                let (_, data) =
+                    self.resolve_crate(&None, symbol, symbol, None, DUMMY_SP,
+                                       PathKind::Crate, dep_kind);
+
+                // Sanity check the loaded crate to ensure it is indeed a sanitizer runtime
+                if !data.is_sanitizer_runtime() {
+                    self.sess.err(&format!("the crate `{}` is not a sanitizer runtime",
+                                           name));
+                }
+            }
+        }
+    }
+
     fn inject_allocator_crate(&mut self) {
         // Make sure that we actually need an allocator, if none of our
         // dependencies need one then we definitely don't!
@@ -982,6 +1041,9 @@ impl<'a> CrateLoader<'a> {
 
 impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> {
     fn postprocess(&mut self, krate: &ast::Crate) {
+        // inject the sanitizer runtime before the allocator runtime because all
+        // sanitizers force the use of the `alloc_system` allocator
+        self.inject_sanitizer_runtime();
         self.inject_allocator_crate();
         self.inject_panic_runtime(krate);
 
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index beba5faf3d034..4709ca6101c79 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -297,6 +297,11 @@ impl CrateMetadata {
         attr::contains_name(&attrs, "compiler_builtins")
     }
 
+    pub fn is_sanitizer_runtime(&self) -> bool {
+        let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
+        attr::contains_name(&attrs, "sanitizer_runtime")
+    }
+
     pub fn is_no_builtins(&self) -> bool {
         let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
         attr::contains_name(&attrs, "no_builtins")
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index 39581a4696088..7b0177bfd23ed 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -297,6 +297,10 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         self.get_crate_data(cnum).is_compiler_builtins()
     }
 
+    fn is_sanitizer_runtime(&self, cnum: CrateNum) -> bool {
+        self.get_crate_data(cnum).is_sanitizer_runtime()
+    }
+
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy {
         self.get_crate_data(cnum).panic_strategy()
     }
diff --git a/src/librustc_msan/Cargo.toml b/src/librustc_msan/Cargo.toml
new file mode 100644
index 0000000000000..628746ac232df
--- /dev/null
+++ b/src/librustc_msan/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+name = "rustc_msan"
+version = "0.0.0"
+
+[lib]
+name = "rustc_msan"
+path = "lib.rs"
+
+[build-dependencies]
+cmake = "0.1.18"
+
+[dependencies]
+alloc_system = { path = "../liballoc_system" }
+core = { path = "../libcore" }
diff --git a/src/librustc_msan/build.rs b/src/librustc_msan/build.rs
new file mode 100644
index 0000000000000..bf630c7844a22
--- /dev/null
+++ b/src/librustc_msan/build.rs
@@ -0,0 +1,50 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate cmake;
+
+use std::path::PathBuf;
+use std::env;
+
+use cmake::Config;
+
+fn main() {
+    if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
+        let dst = Config::new("../compiler-rt")
+            .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
+            .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
+            .define("COMPILER_RT_BUILD_XRAY", "OFF")
+            .define("LLVM_CONFIG_PATH", llvm_config)
+            .build_target("msan")
+            .build();
+
+        println!("cargo:rustc-link-search=native={}",
+                 dst.join("build/lib/linux").display());
+        println!("cargo:rustc-link-lib=static=clang_rt.msan-x86_64");
+
+        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
+        let mut stack = src_dir.join("../compiler-rt")
+            .read_dir()
+            .unwrap()
+            .map(|e| e.unwrap())
+            .filter(|e| &*e.file_name() != ".git")
+            .collect::<Vec<_>>();
+        while let Some(entry) = stack.pop() {
+            let path = entry.path();
+            if entry.file_type().unwrap().is_dir() {
+                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
+            } else {
+                println!("cargo:rerun-if-changed={}", path.display());
+            }
+        }
+    }
+
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/librustc_msan/lib.rs b/src/librustc_msan/lib.rs
new file mode 100644
index 0000000000000..71a166b91ebcb
--- /dev/null
+++ b/src/librustc_msan/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(not(stage0), feature(sanitizer_runtime))]
+#![cfg_attr(not(stage0), sanitizer_runtime)]
+#![feature(alloc_system)]
+#![feature(staged_api)]
+#![no_std]
+#![unstable(feature = "sanitizer_runtime_lib",
+            reason = "internal implementation detail of sanitizers",
+            issue = "0")]
+
+extern crate alloc_system;
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 4ddf8a883bc48..1cbfa26b705ac 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -1031,6 +1031,9 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
         // symbols from the dylib.
         let src = sess.cstore.used_crate_source(cnum);
         match data[cnum.as_usize() - 1] {
+            _ if sess.cstore.is_sanitizer_runtime(cnum) => {
+                link_sanitizer_runtime(cmd, sess, tmpdir, cnum);
+            }
             // compiler-builtins are always placed last to ensure that they're
             // linked correctly.
             _ if sess.cstore.is_compiler_builtins(cnum) => {
@@ -1048,6 +1051,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
         }
     }
 
+    // compiler-builtins are always placed last to ensure that they're
+    // linked correctly.
     // We must always link the `compiler_builtins` crate statically. Even if it
     // was already "included" in a dylib (e.g. `libstd` when `-C prefer-dynamic`
     // is used)
@@ -1064,6 +1069,34 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
         }
     }
 
+    // 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(cmd: &mut Linker,
+                              sess: &Session,
+                              tmpdir: &Path,
+                              cnum: CrateNum) {
+        let src = sess.cstore.used_crate_source(cnum);
+        let cratepath = &src.rlib.unwrap().0;
+        let dst = tmpdir.join(cratepath.file_name().unwrap());
+        let cfg = archive_config(sess, &dst, Some(cratepath));
+        let mut archive = ArchiveBuilder::new(cfg);
+        archive.update_symbols();
+
+        for f in archive.src_files() {
+            if f.ends_with("bytecode.deflate") ||
+                f == sess.cstore.metadata_filename() {
+                    archive.remove_file(&f);
+                    continue
+                }
+        }
+
+        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:
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index b3a2d66a07c11..8e71c57109519 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -12,7 +12,7 @@ use back::lto;
 use back::link::{get_linker, remove};
 use back::symbol_export::ExportedSymbols;
 use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
-use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses};
+use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses, Sanitizer};
 use session::Session;
 use session::config::{self, OutputType};
 use llvm;
@@ -678,6 +678,22 @@ pub fn run_passes(sess: &Session,
     let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone());
     let mut metadata_config = ModuleConfig::new(tm, vec![]);
 
+    if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
+        match *sanitizer {
+            Sanitizer::Address => {
+                modules_config.passes.push("asan".to_owned());
+                modules_config.passes.push("asan-module".to_owned());
+            }
+            Sanitizer::Memory => {
+                modules_config.passes.push("msan".to_owned())
+            }
+            Sanitizer::Thread => {
+                modules_config.passes.push("tsan".to_owned())
+            }
+            _ => {}
+        }
+    }
+
     modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
     modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize));
 
diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs
index bf7a02eb0f196..7ac482459ee39 100644
--- a/src/librustc_trans/declare.rs
+++ b/src/librustc_trans/declare.rs
@@ -23,6 +23,7 @@
 use llvm::{self, ValueRef};
 use llvm::AttributePlace::Function;
 use rustc::ty;
+use rustc::session::config::Sanitizer;
 use abi::{Abi, FnType};
 use attributes;
 use context::CrateContext;
@@ -72,6 +73,21 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty:
         llvm::Attribute::NoRedZone.apply_llfn(Function, llfn);
     }
 
+    if let Some(ref sanitizer) = ccx.tcx().sess.opts.debugging_opts.sanitizer {
+        match *sanitizer {
+            Sanitizer::Address => {
+                llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
+            },
+            Sanitizer::Memory => {
+                llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
+            },
+            Sanitizer::Thread => {
+                llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
+            },
+            _ => {}
+        }
+    }
+
     // If we're compiling the compiler-builtins crate, e.g. the equivalent of
     // compiler-rt, then we want to implicitly compile everything with hidden
     // visibility as we're going to link this object all over the place but
diff --git a/src/librustc_tsan/Cargo.toml b/src/librustc_tsan/Cargo.toml
new file mode 100644
index 0000000000000..2af0ae3f73411
--- /dev/null
+++ b/src/librustc_tsan/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+name = "rustc_tsan"
+version = "0.0.0"
+
+[lib]
+name = "rustc_tsan"
+path = "lib.rs"
+
+[build-dependencies]
+cmake = "0.1.18"
+
+[dependencies]
+alloc_system = { path = "../liballoc_system" }
+core = { path = "../libcore" }
diff --git a/src/librustc_tsan/build.rs b/src/librustc_tsan/build.rs
new file mode 100644
index 0000000000000..2ba5866ab9d44
--- /dev/null
+++ b/src/librustc_tsan/build.rs
@@ -0,0 +1,50 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate cmake;
+
+use std::path::PathBuf;
+use std::env;
+
+use cmake::Config;
+
+fn main() {
+    if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
+        let dst = Config::new("../compiler-rt")
+            .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
+            .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
+            .define("COMPILER_RT_BUILD_XRAY", "OFF")
+            .define("LLVM_CONFIG_PATH", llvm_config)
+            .build_target("tsan")
+            .build();
+
+        println!("cargo:rustc-link-search=native={}",
+                 dst.join("build/lib/linux").display());
+        println!("cargo:rustc-link-lib=static=clang_rt.tsan-x86_64");
+
+        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
+        let mut stack = src_dir.join("../compiler-rt")
+            .read_dir()
+            .unwrap()
+            .map(|e| e.unwrap())
+            .filter(|e| &*e.file_name() != ".git")
+            .collect::<Vec<_>>();
+        while let Some(entry) = stack.pop() {
+            let path = entry.path();
+            if entry.file_type().unwrap().is_dir() {
+                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
+            } else {
+                println!("cargo:rerun-if-changed={}", path.display());
+            }
+        }
+    }
+
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/librustc_tsan/lib.rs b/src/librustc_tsan/lib.rs
new file mode 100644
index 0000000000000..71a166b91ebcb
--- /dev/null
+++ b/src/librustc_tsan/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(not(stage0), feature(sanitizer_runtime))]
+#![cfg_attr(not(stage0), sanitizer_runtime)]
+#![feature(alloc_system)]
+#![feature(staged_api)]
+#![no_std]
+#![unstable(feature = "sanitizer_runtime_lib",
+            reason = "internal implementation detail of sanitizers",
+            issue = "0")]
+
+extern crate alloc_system;
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index 8146e7fb1edaf..2ba7517d3d202 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -23,13 +23,23 @@ compiler_builtins = { path = "../libcompiler_builtins" }
 std_unicode = { path = "../libstd_unicode" }
 unwind = { path = "../libunwind" }
 
+[target.x86_64-unknown-linux-gnu.dependencies]
+rustc_asan = { path = "../librustc_asan", optional = true }
+rustc_lsan = { path = "../librustc_lsan", optional = true }
+rustc_msan = { path = "../librustc_msan", optional = true }
+rustc_tsan = { path = "../librustc_tsan", optional = true }
+
 [build-dependencies]
 build_helper = { path = "../build_helper" }
 gcc = "0.3.27"
 
 [features]
+asan = ["rustc_asan"]
 backtrace = []
 debug-jemalloc = ["alloc_jemalloc/debug"]
 jemalloc = ["alloc_jemalloc"]
 force_alloc_system = []
+lsan = ["rustc_lsan"]
+msan = ["rustc_msan"]
 panic-unwind = ["panic_unwind"]
+tsan = ["rustc_tsan"]
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 52ef2a05fcf19..6a16a0ef353a4 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -328,6 +328,10 @@ declare_features! (
 
     // `extern "msp430-interrupt" fn()`
     (active, abi_msp430_interrupt, "1.16.0", Some(38487)),
+
+    // Used to identify crates that contain sanitizer runtimes
+    // rustc internal
+    (active, sanitizer_runtime, "1.17.0", None),
 );
 
 declare_features! (
@@ -647,6 +651,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                               contains compiler-rt intrinsics and will never be \
                                               stable",
                                           cfg_fn!(compiler_builtins))),
+    ("sanitizer_runtime", Whitelisted, Gated(Stability::Unstable,
+                                             "sanitizer_runtime",
+                                             "the `#[sanitizer_runtime]` attribute is used to \
+                                              identify crates that contain the runtime of a \
+                                              sanitizer and will never be stable",
+                                             cfg_fn!(sanitizer_runtime))),
 
     ("allow_internal_unstable", Normal, Gated(Stability::Unstable,
                                               "allow_internal_unstable",
diff --git a/src/rustc/std_shim/Cargo.toml b/src/rustc/std_shim/Cargo.toml
index 14c9c5544b188..db96079d3e916 100644
--- a/src/rustc/std_shim/Cargo.toml
+++ b/src/rustc/std_shim/Cargo.toml
@@ -35,8 +35,12 @@ core = { path = "../../libcore" }
 
 # Reexport features from std
 [features]
+asan = ["std/asan"]
 backtrace = ["std/backtrace"]
 debug-jemalloc = ["std/debug-jemalloc"]
 jemalloc = ["std/jemalloc"]
 force_alloc_system = ["std/force_alloc_system"]
+lsan = ["std/lsan"]
+msan = ["std/msan"]
 panic-unwind = ["std/panic-unwind"]
+tsan = ["std/tsan"]
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index c7bcd2558186e..58dfe0a3d38f3 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -148,6 +148,12 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::ZExt;
   case InReg:
     return Attribute::InReg;
+  case SanitizeThread:
+    return Attribute::SanitizeThread;
+  case SanitizeAddress:
+    return Attribute::SanitizeAddress;
+  case SanitizeMemory:
+    return Attribute::SanitizeMemory;
   }
   llvm_unreachable("bad AttributeKind");
 }
diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h
index a30fa3133e282..0baf5528e9356 100644
--- a/src/rustllvm/rustllvm.h
+++ b/src/rustllvm/rustllvm.h
@@ -98,6 +98,9 @@ enum LLVMRustAttribute {
   UWTable = 17,
   ZExt = 18,
   InReg = 19,
+  SanitizeThread = 20,
+  SanitizeAddress = 21,
+  SanitizeMemory = 22,
 };
 
 typedef struct OpaqueRustString *RustStringRef;
diff --git a/src/test/compile-fail/feature-gate-sanitizer-runtime.rs b/src/test/compile-fail/feature-gate-sanitizer-runtime.rs
new file mode 100644
index 0000000000000..a18641d824691
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-sanitizer-runtime.rs
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![sanitizer_runtime] //~ ERROR the `#[sanitizer_runtime]` attribute is
+
+fn main() {}
diff --git a/src/test/run-make/sanitizer-address/Makefile b/src/test/run-make/sanitizer-address/Makefile
new file mode 100644
index 0000000000000..c490f490cdf7f
--- /dev/null
+++ b/src/test/run-make/sanitizer-address/Makefile
@@ -0,0 +1,21 @@
+-include ../tools.mk
+
+# NOTE the address sanitizer only supports x86_64 linux
+ifndef IS_WINDOWS
+ifeq ($(shell uname),Linux)
+ifeq ($(shell uname -m),x86_64)
+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:
+
+endif
+else
+all:
+
+endif
+else
+all:
+
+endif
diff --git a/src/test/run-make/sanitizer-address/overflow.rs b/src/test/run-make/sanitizer-address/overflow.rs
new file mode 100644
index 0000000000000..e35c3873f7eb5
--- /dev/null
+++ b/src/test/run-make/sanitizer-address/overflow.rs
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let xs = [0, 1, 2, 3];
+    let y = unsafe { *xs.as_ptr().offset(4) };
+}
diff --git a/src/test/run-make/sanitizer-dylib/Makefile b/src/test/run-make/sanitizer-dylib/Makefile
new file mode 100644
index 0000000000000..70a8254a6a655
--- /dev/null
+++ b/src/test/run-make/sanitizer-dylib/Makefile
@@ -0,0 +1,4 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) -Z sanitizer=leak --crate-type dylib hello.rs 2>&1 | grep -q 'Only executables and rlibs can be compiled with `-Z sanitizer`'
diff --git a/src/test/run-make/sanitizer-dylib/hello.rs b/src/test/run-make/sanitizer-dylib/hello.rs
new file mode 100644
index 0000000000000..41782851a1a6d
--- /dev/null
+++ b/src/test/run-make/sanitizer-dylib/hello.rs
@@ -0,0 +1,13 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    println!("Hello, world!");
+}
diff --git a/src/test/run-make/sanitizer-invalid-target/Makefile b/src/test/run-make/sanitizer-invalid-target/Makefile
new file mode 100644
index 0000000000000..6a1ce8bab2fb6
--- /dev/null
+++ b/src/test/run-make/sanitizer-invalid-target/Makefile
@@ -0,0 +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'
diff --git a/src/test/run-make/sanitizer-invalid-target/hello.rs b/src/test/run-make/sanitizer-invalid-target/hello.rs
new file mode 100644
index 0000000000000..e9e46b7702a80
--- /dev/null
+++ b/src/test/run-make/sanitizer-invalid-target/hello.rs
@@ -0,0 +1,13 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(no_core)]
+#![no_core]
+#![no_main]
diff --git a/src/test/run-make/sanitizer-leak/Makefile b/src/test/run-make/sanitizer-leak/Makefile
new file mode 100644
index 0000000000000..492e328fab239
--- /dev/null
+++ b/src/test/run-make/sanitizer-leak/Makefile
@@ -0,0 +1,23 @@
+-include ../tools.mk
+
+# NOTE the leak sanitizer only supports x86_64 linux
+# Also, this particular sanitizer sometimes doesn't work so we are not going to
+# run the binary
+ifndef IS_WINDOWS
+ifeq ($(shell uname),Linux)
+ifeq ($(shell uname -m),x86_64)
+all:
+	$(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
+else
+all:
+
+endif
+else
+all:
+
+endif
diff --git a/src/test/run-make/sanitizer-leak/leak.rs b/src/test/run-make/sanitizer-leak/leak.rs
new file mode 100644
index 0000000000000..279da6aaae707
--- /dev/null
+++ b/src/test/run-make/sanitizer-leak/leak.rs
@@ -0,0 +1,16 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::mem;
+
+fn main() {
+    let xs = vec![1, 2, 3, 4];
+    mem::forget(xs);
+}
diff --git a/src/test/run-make/sanitizer-memory/Makefile b/src/test/run-make/sanitizer-memory/Makefile
new file mode 100644
index 0000000000000..f8960992a0df0
--- /dev/null
+++ b/src/test/run-make/sanitizer-memory/Makefile
@@ -0,0 +1,21 @@
+-include ../tools.mk
+
+# NOTE the memory sanitizer only supports x86_64 linux
+ifndef IS_WINDOWS
+ifeq ($(shell uname),Linux)
+ifeq ($(shell uname -m),x86_64)
+all:
+	$(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
+else
+all:
+
+endif
+else
+all:
+
+endif
diff --git a/src/test/run-make/sanitizer-memory/uninit.rs b/src/test/run-make/sanitizer-memory/uninit.rs
new file mode 100644
index 0000000000000..8350c7de3acab
--- /dev/null
+++ b/src/test/run-make/sanitizer-memory/uninit.rs
@@ -0,0 +1,16 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::mem;
+
+fn main() {
+    let xs: [u8; 4] = unsafe { mem::uninitialized() };
+    let y = xs[0] + xs[1];
+}
diff --git a/src/test/run-make/sanitizer-thread/Makefile b/src/test/run-make/sanitizer-thread/Makefile
new file mode 100644
index 0000000000000..e32247c4a9b24
--- /dev/null
+++ b/src/test/run-make/sanitizer-thread/Makefile
@@ -0,0 +1,21 @@
+-include ../tools.mk
+
+# NOTE the leak sanitizer only supports x86_64 linux
+ifndef IS_WINDOWS
+ifeq ($(shell uname),Linux)
+ifeq ($(shell uname -m),x86_64)
+all:
+	$(RUSTC) -g -Z sanitizer=thread -Z print-link-args racy.rs | grep -q librustc_tsan
+	$(TMPDIR)/racy 2>&1 | grep -q 'data race'
+else
+all:
+
+endif
+else
+all:
+
+endif
+else
+all:
+
+endif
diff --git a/src/test/run-make/sanitizer-thread/racy.rs b/src/test/run-make/sanitizer-thread/racy.rs
new file mode 100644
index 0000000000000..dc929e004a479
--- /dev/null
+++ b/src/test/run-make/sanitizer-thread/racy.rs
@@ -0,0 +1,21 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::thread;
+
+static mut ANSWER: i32 = 0;
+
+fn main() {
+    let t1 = thread::spawn(|| unsafe { ANSWER = 42 });
+    unsafe {
+        ANSWER = 24;
+    }
+    t1.join().ok();
+}

From 775a93646cb6db5affff24f2260fb60a7723deba Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Fri, 3 Feb 2017 18:58:47 -0500
Subject: [PATCH 2/8] build/test the sanitizers only when --enable-sanitizers
 is used

---
 configure                                    |  1 +
 src/bootstrap/check.rs                       |  9 +++++----
 src/bootstrap/compile.rs                     |  7 +++----
 src/bootstrap/config.rs                      |  7 ++++---
 src/bootstrap/config.toml.example            |  3 +++
 src/bootstrap/lib.rs                         |  4 ----
 src/librustc_llvm/ffi.rs                     |  2 +-
 src/test/run-make/sanitizer-address/Makefile | 12 +-----------
 src/test/run-make/sanitizer-leak/Makefile    | 15 +--------------
 src/test/run-make/sanitizer-memory/Makefile  | 13 +------------
 src/test/run-make/sanitizer-thread/Makefile  | 13 +------------
 11 files changed, 21 insertions(+), 65 deletions(-)

diff --git a/configure b/configure
index 4ce80a5e84919..0904143a7b5f6 100755
--- a/configure
+++ b/configure
@@ -649,6 +649,7 @@ opt codegen-tests 1 "run the src/test/codegen tests"
 opt option-checking 1 "complain about unrecognized options in this configure script"
 opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)"
 opt vendor 0 "enable usage of vendored Rust crates"
+opt sanitizers 0 "build the sanitizer runtimes (asan, lsan, msan, tsan)"
 
 # Optimization and debugging options. These may be overridden by the release channel, etc.
 opt_nosave optimize 1 "build optimized rust code"
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 573d0df0cee20..32cce45e067ad 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -236,6 +236,10 @@ pub fn compiletest(build: &Build,
     cmd.env("RUSTC_BOOTSTRAP", "1");
     build.add_rust_test_threads(&mut cmd);
 
+    if build.config.sanitizers {
+        cmd.env("SANITIZER_SUPPORT", "1");
+    }
+
     cmd.arg("--adb-path").arg("adb");
     cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
     if target.contains("android") {
@@ -332,10 +336,7 @@ pub fn krate(build: &Build,
              krate: Option<&str>) {
     let (name, path, features, root) = match mode {
         Mode::Libstd => {
-            ("libstd",
-             "src/rustc/std_shim",
-             build.std_features(),
-             "std_shim")
+            ("libstd", "src/rustc/std_shim", build.std_features(), "std_shim")
         }
         Mode::Libtest => {
             ("libtest", "src/rustc/test_shim", String::new(), "test_shim")
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 3e29339a75b47..d329f9c069043 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -52,15 +52,14 @@ pub fn std(build: &Build, target: &str, compiler: &Compiler) {
         features.push_str(" force_alloc_system");
     }
 
-    if compiler.stage != 0 && !build.system_llvm(target) {
+    if compiler.stage != 0 && build.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 don't build the runtimes when compiling against system llvm
-        // because some distributions ship llvm packages that have a directory
-        // layout different from the one that the runtime's build system expects
+        // We also only build the runtimes when --enable-sanitizers (or its
+        // config.toml equivalent) is used
         cargo.env("LLVM_CONFIG", build.llvm_config(target));
     }
     cargo.arg("--features").arg(features)
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index b171c89c20ad3..a31b202a0ae7b 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -48,6 +48,7 @@ pub struct Config {
     pub target_config: HashMap<String, Target>,
     pub full_bootstrap: bool,
     pub extended: bool,
+    pub sanitizers: bool,
 
     // llvm codegen options
     pub llvm_assertions: bool,
@@ -108,8 +109,6 @@ pub struct Config {
 /// Per-target configuration stored in the global configuration structure.
 #[derive(Default)]
 pub struct Target {
-    // `true` if compiling against system LLVM or a pre-built LLVM
-    pub system_llvm: bool,
     pub llvm_config: Option<PathBuf>,
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
@@ -150,6 +149,7 @@ struct Build {
     python: Option<String>,
     full_bootstrap: Option<bool>,
     extended: Option<bool>,
+    sanitizers: Option<bool>,
 }
 
 /// TOML representation of various global install decisions.
@@ -294,6 +294,7 @@ impl Config {
         set(&mut config.vendor, build.vendor);
         set(&mut config.full_bootstrap, build.full_bootstrap);
         set(&mut config.extended, build.extended);
+        set(&mut config.sanitizers, build.sanitizers);
 
         if let Some(ref install) = toml.install {
             config.prefix = install.prefix.clone().map(PathBuf::from);
@@ -437,6 +438,7 @@ impl Config {
                 ("VENDOR", self.vendor),
                 ("FULL_BOOTSTRAP", self.full_bootstrap),
                 ("EXTENDED", self.extended),
+                ("SANITIZERS", self.sanitizers),
             }
 
             match key {
@@ -514,7 +516,6 @@ impl Config {
                                      .or_insert(Target::default());
                     let root = parse_configure_path(value);
                     target.llvm_config = Some(push_exe_path(root, &["bin", "llvm-config"]));
-                    target.system_llvm = true;
                 }
                 "CFG_JEMALLOC_ROOT" if value.len() > 0 => {
                     let target = self.target_config.entry(self.build.clone())
diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example
index a53419ad7fd78..025fe990f91da 100644
--- a/src/bootstrap/config.toml.example
+++ b/src/bootstrap/config.toml.example
@@ -124,6 +124,9 @@
 # disabled by default.
 #extended = false
 
+# Build the sanitizer runtimes
+#sanitizers = false
+
 # =============================================================================
 # General install configuration options
 # =============================================================================
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 21dd4b1520a88..1d01b8773cec8 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -717,10 +717,6 @@ impl Build {
         }
     }
 
-    fn system_llvm(&self, target: &str) -> bool {
-        self.config.target_config.get(target).map(|t| t.system_llvm).unwrap_or(false)
-    }
-
     /// Returns the path to `FileCheck` binary for the specified target
     fn llvm_filecheck(&self, target: &str) -> PathBuf {
         let target_config = self.config.target_config.get(target);
diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs
index 6c9976ca3f8e4..bd24f7657e761 100644
--- a/src/librustc_llvm/ffi.rs
+++ b/src/librustc_llvm/ffi.rs
@@ -127,7 +127,7 @@ pub enum Attribute {
     ZExt            = 18,
     InReg           = 19,
     SanitizeThread  = 20,
-    SanitizeAddress  = 21,
+    SanitizeAddress = 21,
     SanitizeMemory  = 22,
 }
 
diff --git a/src/test/run-make/sanitizer-address/Makefile b/src/test/run-make/sanitizer-address/Makefile
index c490f490cdf7f..5931145f3a47d 100644
--- a/src/test/run-make/sanitizer-address/Makefile
+++ b/src/test/run-make/sanitizer-address/Makefile
@@ -1,9 +1,7 @@
 -include ../tools.mk
 
 # NOTE the address sanitizer only supports x86_64 linux
-ifndef IS_WINDOWS
-ifeq ($(shell uname),Linux)
-ifeq ($(shell uname -m),x86_64)
+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
@@ -11,11 +9,3 @@ else
 all:
 
 endif
-else
-all:
-
-endif
-else
-all:
-
-endif
diff --git a/src/test/run-make/sanitizer-leak/Makefile b/src/test/run-make/sanitizer-leak/Makefile
index 492e328fab239..f02d948fdc84f 100644
--- a/src/test/run-make/sanitizer-leak/Makefile
+++ b/src/test/run-make/sanitizer-leak/Makefile
@@ -1,11 +1,6 @@
 -include ../tools.mk
 
-# NOTE the leak sanitizer only supports x86_64 linux
-# Also, this particular sanitizer sometimes doesn't work so we are not going to
-# run the binary
-ifndef IS_WINDOWS
-ifeq ($(shell uname),Linux)
-ifeq ($(shell uname -m),x86_64)
+ifdef SANITIZER_SUPPORT
 all:
 	$(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'
@@ -13,11 +8,3 @@ else
 all:
 
 endif
-else
-all:
-
-endif
-else
-all:
-
-endif
diff --git a/src/test/run-make/sanitizer-memory/Makefile b/src/test/run-make/sanitizer-memory/Makefile
index f8960992a0df0..08682e5975e51 100644
--- a/src/test/run-make/sanitizer-memory/Makefile
+++ b/src/test/run-make/sanitizer-memory/Makefile
@@ -1,9 +1,6 @@
 -include ../tools.mk
 
-# NOTE the memory sanitizer only supports x86_64 linux
-ifndef IS_WINDOWS
-ifeq ($(shell uname),Linux)
-ifeq ($(shell uname -m),x86_64)
+ifdef SANITIZER_SUPPORT
 all:
 	$(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
@@ -11,11 +8,3 @@ else
 all:
 
 endif
-else
-all:
-
-endif
-else
-all:
-
-endif
diff --git a/src/test/run-make/sanitizer-thread/Makefile b/src/test/run-make/sanitizer-thread/Makefile
index e32247c4a9b24..8bb89a241cb05 100644
--- a/src/test/run-make/sanitizer-thread/Makefile
+++ b/src/test/run-make/sanitizer-thread/Makefile
@@ -1,9 +1,6 @@
 -include ../tools.mk
 
-# NOTE the leak sanitizer only supports x86_64 linux
-ifndef IS_WINDOWS
-ifeq ($(shell uname),Linux)
-ifeq ($(shell uname -m),x86_64)
+ifdef SANITIZER_SUPPORT
 all:
 	$(RUSTC) -g -Z sanitizer=thread -Z print-link-args racy.rs | grep -q librustc_tsan
 	$(TMPDIR)/racy 2>&1 | grep -q 'data race'
@@ -11,11 +8,3 @@ else
 all:
 
 endif
-else
-all:
-
-endif
-else
-all:
-
-endif

From 22097e6827b726f517e94c31df0728b481f57245 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Sat, 4 Feb 2017 20:10:29 -0500
Subject: [PATCH 3/8] use helper function in the rebuild logic of the
 rustc_*san crates

---
 src/Cargo.lock               |  4 ++++
 src/librustc_asan/Cargo.toml |  1 +
 src/librustc_asan/build.rs   | 19 ++++---------------
 src/librustc_lsan/Cargo.toml |  1 +
 src/librustc_lsan/build.rs   | 19 ++++---------------
 src/librustc_msan/Cargo.toml |  1 +
 src/librustc_msan/build.rs   | 19 ++++---------------
 src/librustc_tsan/Cargo.toml |  1 +
 src/librustc_tsan/build.rs   | 19 ++++---------------
 9 files changed, 24 insertions(+), 60 deletions(-)

diff --git a/src/Cargo.lock b/src/Cargo.lock
index 8e987ba3b7f15..c1222dc444a22 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -273,6 +273,7 @@ name = "rustc_asan"
 version = "0.0.0"
 dependencies = [
  "alloc_system 0.0.0",
+ "build_helper 0.1.0",
  "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "core 0.0.0",
 ]
@@ -415,6 +416,7 @@ name = "rustc_lsan"
 version = "0.0.0"
 dependencies = [
  "alloc_system 0.0.0",
+ "build_helper 0.1.0",
  "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "core 0.0.0",
 ]
@@ -458,6 +460,7 @@ name = "rustc_msan"
 version = "0.0.0"
 dependencies = [
  "alloc_system 0.0.0",
+ "build_helper 0.1.0",
  "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "core 0.0.0",
 ]
@@ -548,6 +551,7 @@ name = "rustc_tsan"
 version = "0.0.0"
 dependencies = [
  "alloc_system 0.0.0",
+ "build_helper 0.1.0",
  "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "core 0.0.0",
 ]
diff --git a/src/librustc_asan/Cargo.toml b/src/librustc_asan/Cargo.toml
index abbd7cc0966ea..2d4872b1fc942 100644
--- a/src/librustc_asan/Cargo.toml
+++ b/src/librustc_asan/Cargo.toml
@@ -9,6 +9,7 @@ name = "rustc_asan"
 path = "lib.rs"
 
 [build-dependencies]
+build_helper = { path = "../build_helper" }
 cmake = "0.1.18"
 
 [dependencies]
diff --git a/src/librustc_asan/build.rs b/src/librustc_asan/build.rs
index 3e33efcadb807..015be14bd495a 100644
--- a/src/librustc_asan/build.rs
+++ b/src/librustc_asan/build.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+extern crate build_helper;
 extern crate cmake;
 
 use std::path::PathBuf;
@@ -29,21 +30,9 @@ fn main() {
                  dst.join("build/lib/linux").display());
         println!("cargo:rustc-link-lib=static=clang_rt.asan-x86_64");
 
-        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
-        let mut stack = src_dir.join("../compiler-rt")
-            .read_dir()
-            .unwrap()
-            .map(|e| e.unwrap())
-            .filter(|e| &*e.file_name() != ".git")
-            .collect::<Vec<_>>();
-        while let Some(entry) = stack.pop() {
-            let path = entry.path();
-            if entry.file_type().unwrap().is_dir() {
-                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
-            } else {
-                println!("cargo:rerun-if-changed={}", path.display());
-            }
-        }
+        build_helper::rerun_if_changed_anything_in_dir(&PathBuf::from(env::var("CARGO_MANIFEST_DIR")
+                .unwrap())
+            .join("../compiler-rt"));
     }
 
     println!("cargo:rerun-if-changed=build.rs");
diff --git a/src/librustc_lsan/Cargo.toml b/src/librustc_lsan/Cargo.toml
index ac53f3fe73a75..bc1f2ead76884 100644
--- a/src/librustc_lsan/Cargo.toml
+++ b/src/librustc_lsan/Cargo.toml
@@ -9,6 +9,7 @@ name = "rustc_lsan"
 path = "lib.rs"
 
 [build-dependencies]
+build_helper = { path = "../build_helper" }
 cmake = "0.1.18"
 
 [dependencies]
diff --git a/src/librustc_lsan/build.rs b/src/librustc_lsan/build.rs
index f13928d2bd457..5773777d1f81b 100644
--- a/src/librustc_lsan/build.rs
+++ b/src/librustc_lsan/build.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+extern crate build_helper;
 extern crate cmake;
 
 use std::path::PathBuf;
@@ -29,21 +30,9 @@ fn main() {
                  dst.join("build/lib/linux").display());
         println!("cargo:rustc-link-lib=static=clang_rt.lsan-x86_64");
 
-        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
-        let mut stack = src_dir.join("../compiler-rt")
-            .read_dir()
-            .unwrap()
-            .map(|e| e.unwrap())
-            .filter(|e| &*e.file_name() != ".git")
-            .collect::<Vec<_>>();
-        while let Some(entry) = stack.pop() {
-            let path = entry.path();
-            if entry.file_type().unwrap().is_dir() {
-                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
-            } else {
-                println!("cargo:rerun-if-changed={}", path.display());
-            }
-        }
+        build_helper::rerun_if_changed_anything_in_dir(&PathBuf::from(env::var("CARGO_MANIFEST_DIR")
+                .unwrap())
+            .join("../compiler-rt"));
     }
 
     println!("cargo:rerun-if-changed=build.rs");
diff --git a/src/librustc_msan/Cargo.toml b/src/librustc_msan/Cargo.toml
index 628746ac232df..45cc6b9839fb8 100644
--- a/src/librustc_msan/Cargo.toml
+++ b/src/librustc_msan/Cargo.toml
@@ -9,6 +9,7 @@ name = "rustc_msan"
 path = "lib.rs"
 
 [build-dependencies]
+build_helper = { path = "../build_helper" }
 cmake = "0.1.18"
 
 [dependencies]
diff --git a/src/librustc_msan/build.rs b/src/librustc_msan/build.rs
index bf630c7844a22..7a4c8f7073933 100644
--- a/src/librustc_msan/build.rs
+++ b/src/librustc_msan/build.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+extern crate build_helper;
 extern crate cmake;
 
 use std::path::PathBuf;
@@ -29,21 +30,9 @@ fn main() {
                  dst.join("build/lib/linux").display());
         println!("cargo:rustc-link-lib=static=clang_rt.msan-x86_64");
 
-        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
-        let mut stack = src_dir.join("../compiler-rt")
-            .read_dir()
-            .unwrap()
-            .map(|e| e.unwrap())
-            .filter(|e| &*e.file_name() != ".git")
-            .collect::<Vec<_>>();
-        while let Some(entry) = stack.pop() {
-            let path = entry.path();
-            if entry.file_type().unwrap().is_dir() {
-                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
-            } else {
-                println!("cargo:rerun-if-changed={}", path.display());
-            }
-        }
+        build_helper::rerun_if_changed_anything_in_dir(&PathBuf::from(env::var("CARGO_MANIFEST_DIR")
+                .unwrap())
+            .join("../compiler-rt"));
     }
 
     println!("cargo:rerun-if-changed=build.rs");
diff --git a/src/librustc_tsan/Cargo.toml b/src/librustc_tsan/Cargo.toml
index 2af0ae3f73411..66d6236361ea2 100644
--- a/src/librustc_tsan/Cargo.toml
+++ b/src/librustc_tsan/Cargo.toml
@@ -9,6 +9,7 @@ name = "rustc_tsan"
 path = "lib.rs"
 
 [build-dependencies]
+build_helper = { path = "../build_helper" }
 cmake = "0.1.18"
 
 [dependencies]
diff --git a/src/librustc_tsan/build.rs b/src/librustc_tsan/build.rs
index 2ba5866ab9d44..84326ae8a7106 100644
--- a/src/librustc_tsan/build.rs
+++ b/src/librustc_tsan/build.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+extern crate build_helper;
 extern crate cmake;
 
 use std::path::PathBuf;
@@ -29,21 +30,9 @@ fn main() {
                  dst.join("build/lib/linux").display());
         println!("cargo:rustc-link-lib=static=clang_rt.tsan-x86_64");
 
-        let src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
-        let mut stack = src_dir.join("../compiler-rt")
-            .read_dir()
-            .unwrap()
-            .map(|e| e.unwrap())
-            .filter(|e| &*e.file_name() != ".git")
-            .collect::<Vec<_>>();
-        while let Some(entry) = stack.pop() {
-            let path = entry.path();
-            if entry.file_type().unwrap().is_dir() {
-                stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
-            } else {
-                println!("cargo:rerun-if-changed={}", path.display());
-            }
-        }
+        build_helper::rerun_if_changed_anything_in_dir(&PathBuf::from(env::var("CARGO_MANIFEST_DIR")
+                .unwrap())
+            .join("../compiler-rt"));
     }
 
     println!("cargo:rerun-if-changed=build.rs");

From 72058e4f4cff46daf5c60cbd7c7978734ecb13cc Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Sat, 4 Feb 2017 20:15:20 -0500
Subject: [PATCH 4/8] enable sanitizers on x86_64-linux releases

---
 src/ci/docker/dist-x86-linux/Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ci/docker/dist-x86-linux/Dockerfile b/src/ci/docker/dist-x86-linux/Dockerfile
index 4e4f5dd6f1e53..7238888a4af4f 100644
--- a/src/ci/docker/dist-x86-linux/Dockerfile
+++ b/src/ci/docker/dist-x86-linux/Dockerfile
@@ -76,5 +76,5 @@ RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST |
 ENV HOSTS=i686-unknown-linux-gnu
 ENV HOSTS=$HOSTS,x86_64-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended --enable-sanitizers
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

From 47ae2393e63e0d78118262b70245d34b8c8ba929 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Sun, 5 Feb 2017 19:09:32 -0500
Subject: [PATCH 5/8] enable sanitizers on build job that tests x86_64 linux

---
 src/ci/docker/x86_64-gnu/Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ci/docker/x86_64-gnu/Dockerfile b/src/ci/docker/x86_64-gnu/Dockerfile
index 6919487e17c3e..e903b6ddc64cd 100644
--- a/src/ci/docker/x86_64-gnu/Dockerfile
+++ b/src/ci/docker/x86_64-gnu/Dockerfile
@@ -22,5 +22,5 @@ RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-ini
     rm dumb-init_*.deb
 ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-sanitizers
 ENV SCRIPT python2.7 ../x.py test && python2.7 ../x.py dist

From 1914c8e0aca19b844b14a8b8032bc9376c6d37f0 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Mon, 6 Feb 2017 14:12:56 -0500
Subject: [PATCH 6/8] dist-x86-linux: install newer kernel headers

---
 src/ci/docker/dist-x86-linux/Dockerfile       |  5 ++++
 src/ci/docker/dist-x86-linux/build-headers.sh | 25 +++++++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100755 src/ci/docker/dist-x86-linux/build-headers.sh

diff --git a/src/ci/docker/dist-x86-linux/Dockerfile b/src/ci/docker/dist-x86-linux/Dockerfile
index 7238888a4af4f..a06e47c3bc922 100644
--- a/src/ci/docker/dist-x86-linux/Dockerfile
+++ b/src/ci/docker/dist-x86-linux/Dockerfile
@@ -63,6 +63,11 @@ RUN ./build-git.sh
 COPY build-cmake.sh /tmp/
 RUN ./build-cmake.sh
 
+# for sanitizers, we need kernel headers files newer than the ones CentOS ships
+# with so we install newer ones here
+COPY build-headers.sh /tmp/
+RUN ./build-headers.sh
+
 RUN curl -Lo /rustroot/dumb-init \
       https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 && \
       chmod +x /rustroot/dumb-init
diff --git a/src/ci/docker/dist-x86-linux/build-headers.sh b/src/ci/docker/dist-x86-linux/build-headers.sh
new file mode 100755
index 0000000000000..4ce38fd9205e2
--- /dev/null
+++ b/src/ci/docker/dist-x86-linux/build-headers.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+set -ex
+source shared.sh
+
+curl https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.2.84.tar.xz | unxz | tar x
+
+cd linux-3.2.84
+hide_output make mrproper
+hide_output make INSTALL_HDR_PATH=dest headers_install
+
+find dest/include \( -name .install -o -name ..install.cmd \) -delete
+yes | cp -fr dest/include/* /usr/include
+
+cd ..
+rm -rf linux-3.2.84

From 78a11f1b97f3ab3fa8c9e225f800489051777bc4 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Tue, 7 Feb 2017 22:47:03 -0500
Subject: [PATCH 7/8] fix the sanitizer-dylib test on non x86_64 linux hosts

---
 src/test/run-make/sanitizer-dylib/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/run-make/sanitizer-dylib/Makefile b/src/test/run-make/sanitizer-dylib/Makefile
index 70a8254a6a655..34fdf3b9709d7 100644
--- a/src/test/run-make/sanitizer-dylib/Makefile
+++ b/src/test/run-make/sanitizer-dylib/Makefile
@@ -1,4 +1,4 @@
 -include ../tools.mk
 
 all:
-	$(RUSTC) -Z sanitizer=leak --crate-type dylib hello.rs 2>&1 | grep -q 'Only executables and rlibs can be compiled with `-Z sanitizer`'
+	$(RUSTC) -Z sanitizer=leak --crate-type dylib --target x86_64-unknown-linux-gnu hello.rs 2>&1 | grep -q 'Only executables and rlibs can be compiled with `-Z sanitizer`'

From e180dd541a8ae48e4aaf8934765f67955932252f Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Wed, 8 Feb 2017 22:58:53 -0500
Subject: [PATCH 8/8] sanitizer-dylib: only run where std for x86_64-linux is
 available

---
 src/test/run-make/sanitizer-dylib/Makefile | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/test/run-make/sanitizer-dylib/Makefile b/src/test/run-make/sanitizer-dylib/Makefile
index 34fdf3b9709d7..835d5b0d9d8cd 100644
--- a/src/test/run-make/sanitizer-dylib/Makefile
+++ b/src/test/run-make/sanitizer-dylib/Makefile
@@ -1,4 +1,8 @@
 -include ../tools.mk
 
+ifeq ($(TARGET),x86_64-unknown-linux-gnu)
 all:
-	$(RUSTC) -Z sanitizer=leak --crate-type dylib --target x86_64-unknown-linux-gnu hello.rs 2>&1 | grep -q 'Only executables and rlibs can be compiled with `-Z sanitizer`'
+	$(RUSTC) -Z sanitizer=leak --crate-type dylib --target $(TARGET) hello.rs 2>&1 | grep -q 'Only executables and rlibs can be compiled with `-Z sanitizer`'
+else
+all:
+endif