diff --git a/config.toml.example b/config.toml.example
index a2f64e6b70588..f50543e18a764 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -300,7 +300,7 @@
 # =============================================================================
 [target.x86_64-unknown-linux-gnu]
 
-# C compiler to be used to compiler C code and link Rust code. Note that the
+# C compiler to be used to compiler C code. Note that the
 # default value is platform specific, and if not specified it may also depend on
 # what platform is crossing to what platform.
 #cc = "cc"
@@ -309,6 +309,15 @@
 # This is only used for host targets.
 #cxx = "c++"
 
+# Archiver to be used to assemble static libraries compiled from C/C++ code.
+# Note: an absolute path should be used, otherwise LLVM build will break.
+#ar = "ar"
+
+# Linker to be used to link Rust code. Note that the
+# default value is platform specific, and if not specified it may also depend on
+# what platform is crossing to what platform.
+#linker = "cc"
+
 # Path to the `llvm-config` binary of the installation of a custom LLVM to link
 # against. Note that if this is specifed we don't compile LLVM at all for this
 # target.
diff --git a/src/Cargo.lock b/src/Cargo.lock
index aa5e262e85ff1..2b95be27ffea7 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1994,7 +1994,6 @@ dependencies = [
  "alloc_jemalloc 0.0.0",
  "alloc_system 0.0.0",
  "build_helper 0.1.0",
- "cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "collections 0.0.0",
  "compiler_builtins 0.0.0",
  "core 0.0.0",
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index e91acb21dac99..aeeda85e924ef 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -120,10 +120,9 @@ fn main() {
             cmd.arg("-L").arg(&root);
         }
 
-        // Pass down extra flags, commonly used to configure `-Clinker` when
-        // cross compiling.
-        if let Ok(s) = env::var("RUSTC_FLAGS") {
-            cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
+        // Override linker if necessary.
+        if let Ok(target_linker) = env::var("RUSTC_TARGET_LINKER") {
+            cmd.arg(format!("-Clinker={}", target_linker));
         }
 
         // Pass down incremental directory, if any.
@@ -252,6 +251,11 @@ fn main() {
         if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
             cmd.arg("-Z").arg("force-unstable-if-unmarked");
         }
+    } else {
+        // Override linker if necessary.
+        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
+            cmd.arg(format!("-Clinker={}", host_linker));
+        }
     }
 
     let color = match env::var("RUSTC_COLOR") {
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index d18512b257d6a..4e975adc9721c 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -47,6 +47,9 @@ fn main() {
     if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
         cmd.arg("-Z").arg("force-unstable-if-unmarked");
     }
+    if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") {
+        cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
+    }
 
     // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
     // it up so we can make rustdoc print this into the docs
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 1d63e112ca6fb..6480b5a619c03 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -413,13 +413,15 @@ impl<'a> Builder<'a> {
     pub fn rustdoc_cmd(&self, host: Interned<String>) -> Command {
         let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc"));
         let compiler = self.compiler(self.top_stage, host);
-        cmd
-            .env("RUSTC_STAGE", compiler.stage.to_string())
-            .env("RUSTC_SYSROOT", self.sysroot(compiler))
-            .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
-            .env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
-            .env("RUSTDOC_REAL", self.rustdoc(host))
-            .env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
+        cmd.env("RUSTC_STAGE", compiler.stage.to_string())
+           .env("RUSTC_SYSROOT", self.sysroot(compiler))
+           .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
+           .env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
+           .env("RUSTDOC_REAL", self.rustdoc(host))
+           .env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
+        if let Some(linker) = self.build.linker(host) {
+            cmd.env("RUSTC_TARGET_LINKER", linker);
+        }
         cmd
     }
 
@@ -482,8 +484,14 @@ impl<'a> Builder<'a> {
              } else {
                  PathBuf::from("/path/to/nowhere/rustdoc/not/required")
              })
-             .env("TEST_MIRI", self.config.test_miri.to_string())
-             .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
+             .env("TEST_MIRI", self.config.test_miri.to_string());
+
+        if let Some(host_linker) = self.build.linker(compiler.host) {
+            cargo.env("RUSTC_HOST_LINKER", host_linker);
+        }
+        if let Some(target_linker) = self.build.linker(target) {
+            cargo.env("RUSTC_TARGET_LINKER", target_linker);
+        }
 
         if mode != Mode::Tool {
             // Tools don't get debuginfo right now, e.g. cargo and rls don't
@@ -557,17 +565,35 @@ impl<'a> Builder<'a> {
 
         cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
 
-        // Specify some various options for build scripts used throughout
-        // the build.
+        // Throughout the build Cargo can execute a number of build scripts
+        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
+        // obtained previously to those build scripts.
+        // Build scripts use either the `cc` crate or `configure/make` so we pass
+        // the options through environment variables that are fetched and understood by both.
         //
         // FIXME: the guard against msvc shouldn't need to be here
         if !target.contains("msvc") {
-            cargo.env(format!("CC_{}", target), self.cc(target))
-                 .env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None
-                 .env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
+            let cc = self.cc(target);
+            cargo.env(format!("CC_{}", target), cc)
+                 .env("CC", cc);
+
+            let cflags = self.cflags(target).join(" ");
+            cargo.env(format!("CFLAGS_{}", target), cflags.clone())
+                 .env("CFLAGS", cflags.clone());
+
+            if let Some(ar) = self.ar(target) {
+                let ranlib = format!("{} s", ar.display());
+                cargo.env(format!("AR_{}", target), ar)
+                     .env("AR", ar)
+                     .env(format!("RANLIB_{}", target), ranlib.clone())
+                     .env("RANLIB", ranlib);
+            }
 
             if let Ok(cxx) = self.cxx(target) {
-                 cargo.env(format!("CXX_{}", target), cxx);
+                cargo.env(format!("CXX_{}", target), cxx)
+                     .env("CXX", cxx)
+                     .env(format!("CXXFLAGS_{}", target), cflags.clone())
+                     .env("CXXFLAGS", cflags);
             }
         }
 
diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs
index 08df65c761182..6e3e3c920291d 100644
--- a/src/bootstrap/cc_detect.rs
+++ b/src/bootstrap/cc_detect.rs
@@ -31,20 +31,51 @@
 //! ever be probed for. Instead the compilers found here will be used for
 //! everything.
 
+use std::collections::HashSet;
+use std::{env, iter};
+use std::path::{Path, PathBuf};
 use std::process::Command;
-use std::iter;
 
-use build_helper::{cc2ar, output};
+use build_helper::output;
 use cc;
 
 use Build;
 use config::Target;
 use cache::Interned;
 
+// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
+// so use some simplified logic here. First we respect the environment variable `AR`, then
+// try to infer the archiver path from the C compiler path.
+// In the future this logic should be replaced by calling into the `cc` crate.
+fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
+    if let Some(ar) = env::var_os("AR") {
+        Some(PathBuf::from(ar))
+    } else if target.contains("msvc") {
+        None
+    } else if target.contains("musl") {
+        Some(PathBuf::from("ar"))
+    } else if target.contains("openbsd") {
+        Some(PathBuf::from("ar"))
+    } else {
+        let parent = cc.parent().unwrap();
+        let file = cc.file_name().unwrap().to_str().unwrap();
+        for suffix in &["gcc", "cc", "clang"] {
+            if let Some(idx) = file.rfind(suffix) {
+                let mut file = file[..idx].to_owned();
+                file.push_str("ar");
+                return Some(parent.join(&file));
+            }
+        }
+        Some(parent.join(file))
+    }
+}
+
 pub fn find(build: &mut Build) {
     // For all targets we're going to need a C compiler for building some shims
     // and such as well as for being a linker for Rust code.
-    for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
+    let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
+                               .collect::<HashSet<_>>();
+    for target in targets.into_iter() {
         let mut cfg = cc::Build::new();
         cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
            .target(&target).host(&build.build);
@@ -57,16 +88,23 @@ pub fn find(build: &mut Build) {
         }
 
         let compiler = cfg.get_compiler();
-        let ar = cc2ar(compiler.path(), &target);
+        let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
+            ar
+        } else {
+            cc2ar(compiler.path(), &target)
+        };
+
         build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
-        if let Some(ref ar) = ar {
+        build.cc.insert(target, compiler);
+        if let Some(ar) = ar {
             build.verbose(&format!("AR_{} = {:?}", &target, ar));
+            build.ar.insert(target, ar);
         }
-        build.cc.insert(target, (compiler, ar));
     }
 
     // For all host triples we need to find a C++ compiler as well
-    for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
+    let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
+    for host in hosts.into_iter() {
         let mut cfg = cc::Build::new();
         cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
            .target(&host).host(&build.build);
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index fb32ab0cb862d..d9ee63eef8cdd 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -747,12 +747,14 @@ impl Step for Compiletest {
             flags.push("-g".to_string());
         }
 
-        let mut hostflags = build.rustc_flags(compiler.host);
-        hostflags.extend(flags.clone());
+        if let Some(linker) = build.linker(target) {
+            cmd.arg("--linker").arg(linker);
+        }
+
+        let hostflags = flags.clone();
         cmd.arg("--host-rustcflags").arg(hostflags.join(" "));
 
-        let mut targetflags = build.rustc_flags(target);
-        targetflags.extend(flags);
+        let mut targetflags = flags.clone();
         targetflags.push(format!("-Lnative={}",
                                  build.test_helpers_out(target).display()));
         cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
@@ -806,6 +808,9 @@ impl Step for Compiletest {
                 .arg("--cflags").arg(build.cflags(target).join(" "))
                 .arg("--llvm-components").arg(llvm_components.trim())
                 .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim());
+                if let Some(ar) = build.ar(target) {
+                    cmd.arg("--ar").arg(ar);
+                }
             }
         }
         if suite == "run-make" && !build.config.llvm_enabled {
@@ -831,7 +836,7 @@ impl Step for Compiletest {
         // Note that if we encounter `PATH` we make sure to append to our own `PATH`
         // rather than stomp over it.
         if target.contains("msvc") {
-            for &(ref k, ref v) in build.cc[&target].0.env() {
+            for &(ref k, ref v) in build.cc[&target].env() {
                 if k != "PATH" {
                     cmd.env(k, v);
                 }
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 5fb5eb3b7f1a5..69e0f58f1cd06 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -143,6 +143,8 @@ pub struct Target {
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
     pub cxx: Option<PathBuf>,
+    pub ar: Option<PathBuf>,
+    pub linker: Option<PathBuf>,
     pub ndk: Option<PathBuf>,
     pub crt_static: Option<bool>,
     pub musl_root: Option<PathBuf>,
@@ -282,6 +284,8 @@ struct TomlTarget {
     jemalloc: Option<String>,
     cc: Option<String>,
     cxx: Option<String>,
+    ar: Option<String>,
+    linker: Option<String>,
     android_ndk: Option<String>,
     crt_static: Option<bool>,
     musl_root: Option<String>,
@@ -484,8 +488,10 @@ impl Config {
                 if let Some(ref s) = cfg.android_ndk {
                     target.ndk = Some(env::current_dir().unwrap().join(s));
                 }
-                target.cxx = cfg.cxx.clone().map(PathBuf::from);
                 target.cc = cfg.cc.clone().map(PathBuf::from);
+                target.cxx = cfg.cxx.clone().map(PathBuf::from);
+                target.ar = cfg.ar.clone().map(PathBuf::from);
+                target.linker = cfg.linker.clone().map(PathBuf::from);
                 target.crt_static = cfg.crt_static.clone();
                 target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
                 target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 2d721f455785a..6ac919d3fbdda 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -240,10 +240,11 @@ pub struct Build {
     lldb_python_dir: Option<String>,
 
     // Runtime state filled in later on
-    // target -> (cc, ar)
-    cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>,
-    // host -> (cc, ar)
+    // C/C++ compilers and archiver for all targets
+    cc: HashMap<Interned<String>, cc::Tool>,
     cxx: HashMap<Interned<String>, cc::Tool>,
+    ar: HashMap<Interned<String>, PathBuf>,
+    // Misc
     crates: HashMap<Interned<String>, Crate>,
     is_sudo: bool,
     ci_env: CiEnv,
@@ -324,6 +325,7 @@ impl Build {
             rls_info,
             cc: HashMap::new(),
             cxx: HashMap::new(),
+            ar: HashMap::new(),
             crates: HashMap::new(),
             lldb_version: None,
             lldb_python_dir: None,
@@ -612,7 +614,7 @@ impl Build {
 
     /// Returns the path to the C compiler for the target specified.
     fn cc(&self, target: Interned<String>) -> &Path {
-        self.cc[&target].0.path()
+        self.cc[&target].path()
     }
 
     /// Returns a list of flags to pass to the C compiler for the target
@@ -620,7 +622,7 @@ impl Build {
     fn cflags(&self, target: Interned<String>) -> Vec<String> {
         // Filter out -O and /O (the optimization flags) that we picked up from
         // cc-rs because the build scripts will determine that for themselves.
-        let mut base = self.cc[&target].0.args().iter()
+        let mut base = self.cc[&target].args().iter()
                            .map(|s| s.to_string_lossy().into_owned())
                            .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
                            .collect::<Vec<_>>();
@@ -644,7 +646,7 @@ impl Build {
 
     /// Returns the path to the `ar` archive utility for the target specified.
     fn ar(&self, target: Interned<String>) -> Option<&Path> {
-        self.cc[&target].1.as_ref().map(|p| &**p)
+        self.ar.get(&target).map(|p| &**p)
     }
 
     /// Returns the path to the C++ compiler for the target specified.
@@ -657,21 +659,17 @@ impl Build {
         }
     }
 
-    /// Returns flags to pass to the compiler to generate code for `target`.
-    fn rustc_flags(&self, target: Interned<String>) -> Vec<String> {
-        // New flags should be added here with great caution!
-        //
-        // It's quite unfortunate to **require** flags to generate code for a
-        // target, so it should only be passed here if absolutely necessary!
-        // Most default configuration should be done through target specs rather
-        // than an entry here.
-
-        let mut base = Vec::new();
-        if target != self.config.build && !target.contains("msvc") &&
-            !target.contains("emscripten") {
-            base.push(format!("-Clinker={}", self.cc(target).display()));
+    /// Returns the path to the linker for the given target if it needs to be overriden.
+    fn linker(&self, target: Interned<String>) -> Option<&Path> {
+        if let Some(linker) = self.config.target_config.get(&target)
+                                                       .and_then(|c| c.linker.as_ref()) {
+            Some(linker)
+        } else if target != self.config.build &&
+                  !target.contains("msvc") && !target.contains("emscripten") {
+            Some(self.cc(target))
+        } else {
+            None
         }
-        base
     }
 
     /// Returns if this target should statically link the C runtime, if specified
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 2c8e5004041da..941ea96bbec23 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -227,6 +227,13 @@ impl Step for Llvm {
             cfg.build_arg("-j").build_arg(build.jobs().to_string());
             cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
             cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
+            if let Some(ar) = build.ar(target) {
+                if ar.is_absolute() {
+                    // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
+                    // tries to resolve this path in the LLVM build directory.
+                    cfg.define("CMAKE_AR", sanitize_cc(ar));
+                }
+            }
         };
 
         configure_compilers(&mut cfg);
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index e95a5e0743637..662c56d728dfe 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -561,7 +561,7 @@ impl<'a> Builder<'a> {
         if compiler.host.contains("msvc") {
             let curpaths = env::var_os("PATH").unwrap_or_default();
             let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
-            for &(ref k, ref v) in self.cc[&compiler.host].0.env() {
+            for &(ref k, ref v) in self.cc[&compiler.host].env() {
                 if k != "PATH" {
                     continue
                 }
diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs
index e81dab70b43e7..97723e260f6cb 100644
--- a/src/build_helper/lib.rs
+++ b/src/build_helper/lib.rs
@@ -138,27 +138,6 @@ pub fn gnu_target(target: &str) -> String {
     }
 }
 
-pub fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
-    if target.contains("msvc") {
-        None
-    } else if target.contains("musl") {
-        Some(PathBuf::from("ar"))
-    } else if target.contains("openbsd") {
-        Some(PathBuf::from("ar"))
-    } else {
-        let parent = cc.parent().unwrap();
-        let file = cc.file_name().unwrap().to_str().unwrap();
-        for suffix in &["gcc", "cc", "clang"] {
-            if let Some(idx) = file.rfind(suffix) {
-                let mut file = file[..idx].to_owned();
-                file.push_str("ar");
-                return Some(parent.join(&file));
-            }
-        }
-        Some(parent.join(file))
-    }
-}
-
 pub fn make(host: &str) -> PathBuf {
     if host.contains("bitrig") || host.contains("dragonfly") ||
         host.contains("freebsd") || host.contains("netbsd") ||
diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs
index 7dd85ddcc7965..65e035d4ffdef 100644
--- a/src/liballoc_jemalloc/build.rs
+++ b/src/liballoc_jemalloc/build.rs
@@ -63,15 +63,6 @@ fn main() {
         _ => return,
     };
 
-    let compiler = cc::Build::new().get_compiler();
-    // only msvc returns None for ar so unwrap is okay
-    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
-    let cflags = compiler.args()
-        .iter()
-        .map(|s| s.to_str().unwrap())
-        .collect::<Vec<_>>()
-        .join(" ");
-
     let mut cmd = Command::new("sh");
     cmd.arg(native.src_dir.join("configure")
                           .to_str()
@@ -79,8 +70,6 @@ fn main() {
                           .replace("C:\\", "/c/")
                           .replace("\\", "/"))
        .current_dir(&native.out_dir)
-       .env("CC", compiler.path())
-       .env("EXTRA_CFLAGS", cflags.clone())
        // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
        // that GCC will run the preprocessor, and only the preprocessor, over
        // jemalloc's source files. If we don't specify CPPFLAGS, then at least
@@ -89,9 +78,7 @@ fn main() {
        // passed to GCC, and then GCC won't define the
        // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
        // select an atomic operation implementation.
-       .env("CPPFLAGS", cflags.clone())
-       .env("AR", &ar)
-       .env("RANLIB", format!("{} s", ar.display()));
+       .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default());
 
     if target.contains("ios") {
         cmd.arg("--disable-tls");
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index f8bf00ad73fcc..890e1169c0591 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -246,6 +246,9 @@ pub fn opts() -> Vec<RustcOptGroup> {
         unstable("crate-version", |o| {
             o.optopt("", "crate-version", "crate version to print into documentation", "VERSION")
         }),
+        unstable("linker", |o| {
+            o.optopt("", "linker", "linker used for building executable test code", "PATH")
+        }),
     ]
 }
 
@@ -357,15 +360,16 @@ pub fn main_args(args: &[String]) -> isize {
     let playground_url = matches.opt_str("playground-url");
     let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
     let display_warnings = matches.opt_present("display-warnings");
+    let linker = matches.opt_str("linker");
 
     match (should_test, markdown_input) {
         (true, true) => {
             return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, render_type,
-                                  display_warnings)
+                                  display_warnings, linker)
         }
         (true, false) => {
             return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot,
-                             render_type, display_warnings)
+                             render_type, display_warnings, linker)
         }
         (false, true) => return markdown::render(input,
                                                  output.unwrap_or(PathBuf::from("doc")),
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 484285e91f6e9..fe6bd985bb618 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -142,7 +142,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
 /// Run any tests/code examples in the markdown file `input`.
 pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
             mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>,
-            render_type: RenderType, display_warnings: bool) -> isize {
+            render_type: RenderType, display_warnings: bool, linker: Option<String>) -> isize {
     let input_str = match load_string(input) {
         Ok(s) => s,
         Err(LoadStringError::ReadFail) => return 1,
@@ -154,7 +154,7 @@ pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
     let mut collector = Collector::new(input.to_string(), cfgs, libs, externs,
                                        true, opts, maybe_sysroot, None,
                                        Some(input.to_owned()),
-                                       render_type);
+                                       render_type, linker);
     if render_type == RenderType::Pulldown {
         old_find_testable_code(&input_str, &mut collector, DUMMY_SP);
         find_testable_code(&input_str, &mut collector, DUMMY_SP);
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 4b2980bc3a695..8b2c8d2da395a 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -61,7 +61,8 @@ pub fn run(input: &str,
            crate_name: Option<String>,
            maybe_sysroot: Option<PathBuf>,
            render_type: RenderType,
-           display_warnings: bool)
+           display_warnings: bool,
+           linker: Option<String>)
            -> isize {
     let input_path = PathBuf::from(input);
     let input = config::Input::File(input_path.clone());
@@ -121,7 +122,8 @@ pub fn run(input: &str,
                                        maybe_sysroot,
                                        Some(codemap),
                                        None,
-                                       render_type);
+                                       render_type,
+                                       linker);
 
     {
         let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
@@ -180,7 +182,8 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs
             externs: Externs,
             should_panic: bool, no_run: bool, as_test_harness: bool,
             compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
-            maybe_sysroot: Option<PathBuf>) {
+            maybe_sysroot: Option<PathBuf>,
+            linker: Option<String>) {
     // the test harness wants its own `main` & top level functions, so
     // never wrap the test in `fn main() { ... }`
     let test = make_test(test, Some(cratename), as_test_harness, opts);
@@ -201,6 +204,7 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs
         externs,
         cg: config::CodegenOptions {
             prefer_dynamic: true,
+            linker,
             .. config::basic_codegen_options()
         },
         test: as_test_harness,
@@ -442,13 +446,14 @@ pub struct Collector {
     filename: Option<String>,
     // to be removed when hoedown will be removed as well
     pub render_type: RenderType,
+    linker: Option<String>,
 }
 
 impl Collector {
     pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
                use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>,
                codemap: Option<Rc<CodeMap>>, filename: Option<String>,
-               render_type: RenderType) -> Collector {
+               render_type: RenderType, linker: Option<String>) -> Collector {
         Collector {
             tests: Vec::new(),
             old_tests: HashMap::new(),
@@ -464,6 +469,7 @@ impl Collector {
             codemap,
             filename,
             render_type,
+            linker,
         }
     }
 
@@ -510,6 +516,7 @@ impl Collector {
         let cratename = self.cratename.to_string();
         let opts = self.opts.clone();
         let maybe_sysroot = self.maybe_sysroot.clone();
+        let linker = self.linker.clone();
         debug!("Creating test {}: {}", name, test);
         self.tests.push(testing::TestDescAndFn {
             desc: testing::TestDesc {
@@ -538,7 +545,8 @@ impl Collector {
                                  compile_fail,
                                  error_codes,
                                  &opts,
-                                 maybe_sysroot)
+                                 maybe_sysroot,
+                                 linker)
                     })
                 } {
                     Ok(()) => (),
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index c95287390b4f0..866c0038a7fd9 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -36,7 +36,6 @@ rustc_tsan = { path = "../librustc_tsan" }
 
 [build-dependencies]
 build_helper = { path = "../build_helper" }
-cc = "1.0.1"
 
 [features]
 backtrace = []
diff --git a/src/libstd/build.rs b/src/libstd/build.rs
index 7ca762c801a81..0e6214ea04fc0 100644
--- a/src/libstd/build.rs
+++ b/src/libstd/build.rs
@@ -11,7 +11,6 @@
 #![deny(warnings)]
 
 extern crate build_helper;
-extern crate cc;
 
 use std::env;
 use std::process::Command;
@@ -77,12 +76,6 @@ fn main() {
 fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
     let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?;
 
-    let compiler = cc::Build::new().get_compiler();
-    // only msvc returns None for ar so unwrap is okay
-    let ar = build_helper::cc2ar(compiler.path(), target).unwrap();
-    let mut cflags = compiler.args().iter().map(|s| s.to_str().unwrap())
-                             .collect::<Vec<_>>().join(" ");
-    cflags.push_str(" -fvisibility=hidden");
     run(Command::new("sh")
                 .current_dir(&native.out_dir)
                 .arg(native.src_dir.join("configure").to_str().unwrap()
@@ -94,10 +87,7 @@ fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
                 .arg("--disable-host-shared")
                 .arg(format!("--host={}", build_helper::gnu_target(target)))
                 .arg(format!("--build={}", build_helper::gnu_target(host)))
-                .env("CC", compiler.path())
-                .env("AR", &ar)
-                .env("RANLIB", format!("{} s", ar.display()))
-                .env("CFLAGS", cflags),
+                .env("CFLAGS", env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden"),
         BuildExpectation::None);
 
     run(Command::new(build_helper::make(host))
diff --git a/src/test/run-make/archive-duplicate-names/Makefile b/src/test/run-make/archive-duplicate-names/Makefile
index 5202e6dea541e..93711c41d79f8 100644
--- a/src/test/run-make/archive-duplicate-names/Makefile
+++ b/src/test/run-make/archive-duplicate-names/Makefile
@@ -5,7 +5,7 @@ all:
 	mkdir $(TMPDIR)/b
 	$(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c)
 	$(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c)
-	ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o
+	$(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o
 	$(RUSTC) foo.rs
 	$(RUSTC) bar.rs
 	$(call RUN,bar)
diff --git a/src/test/run-make/compiler-rt-works-on-mingw/Makefile b/src/test/run-make/compiler-rt-works-on-mingw/Makefile
index 4ec54f73e67a5..06d1bb6698ece 100644
--- a/src/test/run-make/compiler-rt-works-on-mingw/Makefile
+++ b/src/test/run-make/compiler-rt-works-on-mingw/Makefile
@@ -3,8 +3,8 @@
 ifneq (,$(findstring MINGW,$(UNAME)))
 ifndef IS_MSVC
 all:
-	g++ foo.cpp -c -o $(TMPDIR)/foo.o
-	ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o
+	$(CXX) foo.cpp -c -o $(TMPDIR)/foo.o
+	$(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o
 	$(RUSTC) foo.rs -lfoo -lstdc++
 	$(call RUN,foo)
 else
diff --git a/src/test/run-make/invalid-library/Makefile b/src/test/run-make/invalid-library/Makefile
index 0dbe655b77dfd..5c9cc9935099f 100644
--- a/src/test/run-make/invalid-library/Makefile
+++ b/src/test/run-make/invalid-library/Makefile
@@ -2,5 +2,5 @@
 
 all:
 	touch $(TMPDIR)/rust.metadata.bin
-	ar crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin
+	$(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin
 	$(RUSTC) foo.rs 2>&1 | grep "can't find crate for"
diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs
index 461df49b468f9..4db027aaeef71 100644
--- a/src/test/run-make/issue-19371/foo.rs
+++ b/src/test/run-make/issue-19371/foo.rs
@@ -18,7 +18,6 @@ extern crate rustc_errors;
 extern crate rustc_trans;
 extern crate syntax;
 
-use rustc::dep_graph::DepGraph;
 use rustc::session::{build_session, Session};
 use rustc::session::config::{basic_options, build_configuration, Input,
                              OutputType, OutputTypes};
@@ -56,6 +55,9 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
     let mut opts = basic_options();
     opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
     opts.maybe_sysroot = Some(sysroot);
+    if let Ok(linker) = std::env::var("RUSTC_LINKER") {
+        opts.cg.linker = Some(linker);
+    }
 
     let descriptions = Registry::new(&rustc::DIAGNOSTICS);
     let cstore = Rc::new(CStore::new(Box::new(rustc_trans::LlvmMetadataLoader)));
@@ -67,8 +69,7 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
 
 fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
     let (sess, cstore) = basic_sess(sysroot);
-    let cfg = build_configuration(&sess, HashSet::new());
     let control = CompileController::basic();
     let input = Input::Str { name: anon_src(), input: code };
-    compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control);
+    let _ = compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control);
 }
diff --git a/src/test/run-make/issue-22131/Makefile b/src/test/run-make/issue-22131/Makefile
index 64af91b487b32..cb4f146273348 100644
--- a/src/test/run-make/issue-22131/Makefile
+++ b/src/test/run-make/issue-22131/Makefile
@@ -2,6 +2,6 @@
 
 all: foo.rs
 	$(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs
-	$(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \
+	$(RUSTDOC) --test --cfg 'feature="bar"' \
 		-L $(TMPDIR) foo.rs |\
 		grep -q 'foo.rs - foo (line 11) ... ok'
diff --git a/src/test/run-make/rustdoc-output-path/Makefile b/src/test/run-make/rustdoc-output-path/Makefile
index 4e570718a62f9..8ce1c699526cd 100644
--- a/src/test/run-make/rustdoc-output-path/Makefile
+++ b/src/test/run-make/rustdoc-output-path/Makefile
@@ -1,4 +1,4 @@
 -include ../tools.mk
 
 all:
-	$(HOST_RPATH_ENV) '$(RUSTDOC)' -o "$(TMPDIR)/foo/bar/doc" foo.rs
+	$(RUSTDOC) -o "$(TMPDIR)/foo/bar/doc" foo.rs
diff --git a/src/test/run-make/staticlib-blank-lib/Makefile b/src/test/run-make/staticlib-blank-lib/Makefile
index 5878eec66bad4..92a278825c242 100644
--- a/src/test/run-make/staticlib-blank-lib/Makefile
+++ b/src/test/run-make/staticlib-blank-lib/Makefile
@@ -1,6 +1,6 @@
 -include ../tools.mk
 
 all:
-	ar crus $(TMPDIR)/libfoo.a foo.rs
-	ar d $(TMPDIR)/libfoo.a foo.rs
+	$(AR) crus $(TMPDIR)/libfoo.a foo.rs
+	$(AR) d $(TMPDIR)/libfoo.a foo.rs
 	$(RUSTC) foo.rs
diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk
index 27f235d54d46b..c5d5626bf7269 100644
--- a/src/test/run-make/tools.mk
+++ b/src/test/run-make/tools.mk
@@ -7,7 +7,13 @@ TARGET_RPATH_ENV = \
 
 RUSTC_ORIGINAL := $(RUSTC)
 BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)'
+BARE_RUSTDOC := $(HOST_RPATH_ENV) '$(RUSTDOC)'
 RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS)
+RUSTDOC := $(BARE_RUSTDOC)
+ifdef RUSTC_LINKER
+RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER)
+RUSTDOC := $(RUSTDOC) --linker $(RUSTC_LINKER) -Z unstable-options
+endif
 #CC := $(CC) -L $(TMPDIR)
 HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py
 
@@ -102,13 +108,13 @@ REMOVE_DYLIBS     = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1))
 REMOVE_RLIBS      = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))
 
 %.a: %.o
-	ar crus $@ $<
+	$(AR) crus $@ $<
 ifdef IS_MSVC
 %.lib: lib%.o
 	$(MSVC_LIB) -out:`cygpath -w $@` $<
 else
 %.lib: lib%.o
-	ar crus $@ $<
+	$(AR) crus $@ $<
 endif
 %.dylib: %.o
 	$(CC) -dynamiclib -Wl,-dylib -o $@ $<
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index cee7e52c7f3c6..aa98f818f4040 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -201,6 +201,8 @@ pub struct Config {
     pub cc: String,
     pub cxx: String,
     pub cflags: String,
+    pub ar: String,
+    pub linker: Option<String>,
     pub llvm_components: String,
     pub llvm_cxxflags: String,
     pub nodejs: Option<String>,
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 306497da9e3f9..1701c8a3e43ee 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -102,6 +102,8 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         .reqopt("", "cc", "path to a C compiler", "PATH")
         .reqopt("", "cxx", "path to a C++ compiler", "PATH")
         .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
+        .optopt("", "ar", "path to an archiver", "PATH")
+        .optopt("", "linker", "path to a linker", "PATH")
         .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
         .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
         .optopt("", "nodejs", "the name of nodejs", "PATH")
@@ -198,6 +200,8 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         cc: matches.opt_str("cc").unwrap(),
         cxx: matches.opt_str("cxx").unwrap(),
         cflags: matches.opt_str("cflags").unwrap(),
+        ar: matches.opt_str("ar").unwrap_or("ar".into()),
+        linker: matches.opt_str("linker"),
         llvm_components: matches.opt_str("llvm-components").unwrap(),
         llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
         nodejs: matches.opt_str("nodejs"),
@@ -234,6 +238,8 @@ pub fn log_config(config: &Config) {
     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
     logv(c, format!("adb_device_status: {}",
                     config.adb_device_status));
+    logv(c, format!("ar: {}", config.ar));
+    logv(c, format!("linker: {:?}", config.linker));
     logv(c, format!("verbose: {}", config.verbose));
     logv(c, format!("quiet: {}", config.quiet));
     logv(c, "\n".to_string());
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 60acc20e692a2..d61077643f1fe 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1155,6 +1155,9 @@ actual:\n\
             .arg("-o").arg(out_dir)
             .arg(&self.testpaths.file)
             .args(&self.props.compile_flags);
+        if let Some(ref linker) = self.config.linker {
+            rustdoc.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
+        }
 
         self.compose_and_run_compiler(rustdoc, None)
     }
@@ -1441,6 +1444,9 @@ actual:\n\
         } else {
             rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
         }
+        if let Some(ref linker) = self.config.linker {
+            rustc.arg(format!("-Clinker={}", linker));
+        }
 
         rustc.args(&self.props.compile_flags);
 
@@ -2101,6 +2107,10 @@ actual:\n\
            .env("LLVM_COMPONENTS", &self.config.llvm_components)
            .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags);
 
+        if let Some(ref linker) = self.config.linker {
+            cmd.env("RUSTC_LINKER", linker);
+        }
+
         // We don't want RUSTFLAGS set from the outside to interfere with
         // compiler flags set in the test cases:
         cmd.env_remove("RUSTFLAGS");
@@ -2123,7 +2133,8 @@ actual:\n\
                .env("CXX", &self.config.cxx);
         } else {
             cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
-               .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags));
+               .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
+               .env("AR", &self.config.ar);
 
             if self.config.target.contains("windows") {
                 cmd.env("IS_WINDOWS", "1");