diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index c8cb2595d32..e58cf0d1641 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -1035,12 +1035,8 @@ def bootstrap(help_triggered):
     env["BOOTSTRAP_PYTHON"] = sys.executable
     env["BUILD_DIR"] = build.build_dir
     env["RUSTC_BOOTSTRAP"] = '1'
-    env["CARGO"] = build.cargo()
-    env["RUSTC"] = build.rustc()
     if toml_path:
         env["BOOTSTRAP_CONFIG"] = toml_path
-    if build.rustfmt():
-        env["RUSTFMT"] = build.rustfmt()
     run(args, env=env, verbose=build.verbose)
 
 
diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs
index 5e5c31de5b6..d40b924e0ff 100644
--- a/src/bootstrap/build.rs
+++ b/src/bootstrap/build.rs
@@ -1,3 +1,26 @@
+use std::env;
+use std::path::PathBuf;
+
 fn main() {
-    println!("cargo:rustc-env=BUILD_TRIPLE={}", std::env::var("HOST").unwrap());
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rustc-env=BUILD_TRIPLE={}", env::var("HOST").unwrap());
+
+    // This may not be a canonicalized path.
+    let mut rustc = PathBuf::from(env::var_os("RUSTC").unwrap());
+
+    if rustc.is_relative() {
+        for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
+            let absolute = dir.join(&rustc);
+            if absolute.exists() {
+                rustc = absolute;
+                break;
+            }
+        }
+    }
+    assert!(rustc.is_absolute());
+
+    // FIXME: if the path is not utf-8, this is going to break. Unfortunately
+    // Cargo doesn't have a way for us to specify non-utf-8 paths easily, so
+    // we'll need to invent some encoding scheme if this becomes a problem.
+    println!("cargo:rustc-env=RUSTC={}", rustc.to_str().unwrap());
 }
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 3ecfac83564..d4f6ce64dec 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -291,7 +291,7 @@ struct Build {
     build_dir: Option<String>,
     cargo: Option<String>,
     rustc: Option<String>,
-    rustfmt: Option<String>, /* allow bootstrap.py to use rustfmt key */
+    rustfmt: Option<PathBuf>,
     docs: Option<bool>,
     compiler_docs: Option<bool>,
     submodules: Option<bool>,
@@ -493,9 +493,8 @@ impl Config {
         config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned();
         config.out = Config::path_from_python("BUILD_DIR");
 
-        config.initial_rustc = Config::path_from_python("RUSTC");
-        config.initial_cargo = Config::path_from_python("CARGO");
-        config.initial_rustfmt = env::var_os("RUSTFMT").map(Config::normalize_python_path);
+        config.initial_cargo = PathBuf::from(env!("CARGO"));
+        config.initial_rustc = PathBuf::from(env!("RUSTC"));
 
         config
     }
@@ -584,6 +583,9 @@ impl Config {
         set(&mut config.full_bootstrap, build.full_bootstrap);
         set(&mut config.extended, build.extended);
         config.tools = build.tools;
+        if build.rustfmt.is_some() {
+            config.initial_rustfmt = build.rustfmt;
+        }
         set(&mut config.verbose, build.verbose);
         set(&mut config.sanitizers, build.sanitizers);
         set(&mut config.profiler, build.profiler);
@@ -838,12 +840,15 @@ impl Config {
             set(&mut config.missing_tools, t.missing_tools);
         }
 
+        // Cargo does not provide a RUSTFMT environment variable, so we
+        // synthesize it manually. Note that we also later check the config.toml
+        // and set this to that path if necessary.
+        let rustfmt = config.initial_rustc.with_file_name(exe("rustfmt", config.build));
+        config.initial_rustfmt = if rustfmt.exists() { Some(rustfmt) } else { None };
+
         // Now that we've reached the end of our configuration, infer the
         // default values for all options that we haven't otherwise stored yet.
 
-        set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
-        set(&mut config.initial_cargo, build.cargo.map(PathBuf::from));
-
         config.llvm_skip_rebuild = llvm_skip_rebuild.unwrap_or(false);
 
         let default = false;