diff --git a/Cargo.lock b/Cargo.lock
index d96f92505adec..8ae21c8663706 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1139,10 +1139,12 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.19"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72327b15c228bfe31f1390f93dd5e9279587f0463836393c9df719ce62a3e450"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
 dependencies = [
+ "rustc-std-workspace-core",
+ "rustc-std-workspace-std",
  "unicode-width",
 ]
 
@@ -2375,6 +2377,9 @@ dependencies = [
 [[package]]
 name = "proc_macro"
 version = "0.0.0"
+dependencies = [
+ "std",
+]
 
 [[package]]
 name = "profiler_builtins"
@@ -3063,6 +3068,13 @@ dependencies = [
  "core",
 ]
 
+[[package]]
+name = "rustc-std-workspace-std"
+version = "1.0.0"
+dependencies = [
+ "std",
+]
+
 [[package]]
 name = "rustc-workspace-hack"
 version = "1.0.0"
@@ -4068,6 +4080,10 @@ dependencies = [
 [[package]]
 name = "term"
 version = "0.0.0"
+dependencies = [
+ "core",
+ "std",
+]
 
 [[package]]
 name = "term"
@@ -4114,8 +4130,13 @@ dependencies = [
 name = "test"
 version = "0.0.0"
 dependencies = [
+ "core",
  "getopts",
+ "libc",
+ "panic_abort",
+ "panic_unwind",
  "proc_macro",
+ "std",
  "term 0.0.0",
 ]
 
@@ -4483,9 +4504,14 @@ checksum = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
+checksum = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
+dependencies = [
+ "compiler_builtins",
+ "rustc-std-workspace-core",
+ "rustc-std-workspace-std",
+]
 
 [[package]]
 name = "unicode-xid"
diff --git a/Cargo.toml b/Cargo.toml
index ccd7e8b7654a6..a242f090fbc07 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -68,6 +68,7 @@ rustc-workspace-hack = { path = 'src/tools/rustc-workspace-hack' }
 # here
 rustc-std-workspace-core = { path = 'src/tools/rustc-std-workspace-core' }
 rustc-std-workspace-alloc = { path = 'src/tools/rustc-std-workspace-alloc' }
+rustc-std-workspace-std = { path = 'src/tools/rustc-std-workspace-std' }
 
 [patch."https://github.com/rust-lang/rust-clippy"]
 clippy_lints = { path = "src/tools/clippy/clippy_lints" }
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 4e49aaa16eae1..4f5de1ecd2b44 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -337,7 +337,6 @@ impl<'a> Builder<'a> {
         match kind {
             Kind::Build => describe!(
                 compile::Std,
-                compile::Test,
                 compile::Rustc,
                 compile::CodegenBackend,
                 compile::StartupObjects,
@@ -363,7 +362,6 @@ impl<'a> Builder<'a> {
             ),
             Kind::Check | Kind::Clippy | Kind::Fix => describe!(
                 check::Std,
-                check::Test,
                 check::Rustc,
                 check::CodegenBackend,
                 check::Rustdoc
@@ -425,8 +423,6 @@ impl<'a> Builder<'a> {
                 doc::TheBook,
                 doc::Standalone,
                 doc::Std,
-                doc::Test,
-                doc::WhitelistedRustc,
                 doc::Rustc,
                 doc::Rustdoc,
                 doc::ErrorIndex,
@@ -801,7 +797,7 @@ impl<'a> Builder<'a> {
         }
 
         match mode {
-            Mode::Std | Mode::Test | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTest=> {},
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {},
             Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
                 // Build proc macros both for the host and the target
                 if target != compiler.host && cmd != "check" {
@@ -852,7 +848,6 @@ impl<'a> Builder<'a> {
         // things still build right, please do!
         match mode {
             Mode::Std => metadata.push_str("std"),
-            Mode::Test => metadata.push_str("test"),
             _ => {},
         }
         cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
@@ -948,9 +943,9 @@ impl<'a> Builder<'a> {
 
         let debuginfo_level = match mode {
             Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
-            Mode::Std | Mode::Test => self.config.rust_debuginfo_level_std,
+            Mode::Std => self.config.rust_debuginfo_level_std,
             Mode::ToolBootstrap | Mode::ToolStd |
-            Mode::ToolTest | Mode::ToolRustc => self.config.rust_debuginfo_level_tools,
+            Mode::ToolRustc => self.config.rust_debuginfo_level_tools,
         };
         cargo.env("RUSTC_DEBUGINFO_LEVEL", debuginfo_level.to_string());
 
@@ -1150,7 +1145,6 @@ impl<'a> Builder<'a> {
 
         match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
             (Mode::Std, Some(n), _) |
-            (Mode::Test, Some(n), _) |
             (_, _, Some(n)) => {
                 cargo.env("RUSTC_CODEGEN_UNITS", n.to_string());
             }
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index d1542b1fca6b7..2bb90fdb04edc 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -365,27 +365,6 @@ fn dist_with_same_targets_and_hosts() {
             },
         ]
     );
-    assert_eq!(
-        first(builder.cache.all::<compile::Test>()),
-        &[
-            compile::Test {
-                compiler: Compiler { host: a, stage: 0 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 1 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 2 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 1 },
-                target: b,
-            },
-        ]
-    );
     assert_eq!(
         first(builder.cache.all::<compile::Assemble>()),
         &[
@@ -415,7 +394,47 @@ fn build_default() {
     let b = INTERNER.intern_str("B");
     let c = INTERNER.intern_str("C");
 
-    assert!(!builder.cache.all::<compile::Std>().is_empty());
+    assert_eq!(
+        first(builder.cache.all::<compile::Std>()),
+        &[
+            compile::Std {
+                compiler: Compiler { host: a, stage: 0 },
+                target: a,
+            },
+            compile::Std {
+                compiler: Compiler { host: a, stage: 1 },
+                target: a,
+            },
+            compile::Std {
+                compiler: Compiler { host: a, stage: 2 },
+                target: a,
+            },
+            compile::Std {
+                compiler: Compiler { host: b, stage: 2 },
+                target: a,
+            },
+            compile::Std {
+                compiler: Compiler { host: a, stage: 1 },
+                target: b,
+            },
+            compile::Std {
+                compiler: Compiler { host: a, stage: 2 },
+                target: b,
+            },
+            compile::Std {
+                compiler: Compiler { host: b, stage: 2 },
+                target: b,
+            },
+            compile::Std {
+                compiler: Compiler { host: a, stage: 2 },
+                target: c,
+            },
+            compile::Std {
+                compiler: Compiler { host: b, stage: 2 },
+                target: c,
+            },
+        ]
+    );
     assert!(!builder.cache.all::<compile::Assemble>().is_empty());
     assert_eq!(
         first(builder.cache.all::<compile::Rustc>()),
@@ -450,63 +469,61 @@ fn build_default() {
             },
         ]
     );
+}
+
+#[test]
+fn build_with_target_flag() {
+    let mut config = configure(&["B"], &["C"]);
+    config.skip_only_host_steps = true;
+    let build = Build::new(config);
+    let mut builder = Builder::new(&build);
+    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+
+    let a = INTERNER.intern_str("A");
+    let b = INTERNER.intern_str("B");
+    let c = INTERNER.intern_str("C");
 
     assert_eq!(
-        first(builder.cache.all::<compile::Test>()),
+        first(builder.cache.all::<compile::Std>()),
         &[
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 0 },
                 target: a,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 1 },
                 target: a,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 2 },
                 target: a,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: b, stage: 2 },
                 target: a,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 1 },
                 target: b,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 2 },
                 target: b,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: b, stage: 2 },
                 target: b,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: a, stage: 2 },
                 target: c,
             },
-            compile::Test {
+            compile::Std {
                 compiler: Compiler { host: b, stage: 2 },
                 target: c,
             },
         ]
     );
-}
-
-#[test]
-fn build_with_target_flag() {
-    let mut config = configure(&["B"], &["C"]);
-    config.skip_only_host_steps = true;
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
-
-    let a = INTERNER.intern_str("A");
-    let b = INTERNER.intern_str("B");
-    let c = INTERNER.intern_str("C");
-
-    assert!(!builder.cache.all::<compile::Std>().is_empty());
     assert_eq!(
         first(builder.cache.all::<compile::Assemble>()),
         &[
@@ -541,48 +558,6 @@ fn build_with_target_flag() {
             },
         ]
     );
-
-    assert_eq!(
-        first(builder.cache.all::<compile::Test>()),
-        &[
-            compile::Test {
-                compiler: Compiler { host: a, stage: 0 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 1 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 2 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: b, stage: 2 },
-                target: a,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 1 },
-                target: b,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 2 },
-                target: b,
-            },
-            compile::Test {
-                compiler: Compiler { host: b, stage: 2 },
-                target: b,
-            },
-            compile::Test {
-                compiler: Compiler { host: a, stage: 2 },
-                target: c,
-            },
-            compile::Test {
-                compiler: Compiler { host: b, stage: 2 },
-                target: c,
-            },
-        ]
-    );
 }
 
 #[test]
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 6e6fea6b831a7..e9a9b7881a068 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -1,6 +1,6 @@
 //! Implementation of compiling the compiler and standard library, in "check"-based modes.
 
-use crate::compile::{run_cargo, std_cargo, test_cargo, rustc_cargo, rustc_cargo_env,
+use crate::compile::{run_cargo, std_cargo, rustc_cargo, rustc_cargo_env,
                      add_to_sysroot};
 use crate::builder::{RunConfig, Builder, Kind, ShouldRun, Step};
 use crate::tool::{prepare_tool_cargo, SourceType};
@@ -92,7 +92,7 @@ impl Step for Rustc {
         let compiler = builder.compiler(0, builder.config.build);
         let target = self.target;
 
-        builder.ensure(Test { target });
+        builder.ensure(Std { target });
 
         let mut cargo = builder.cargo(compiler, Mode::Rustc, target,
             cargo_subcommand(builder.kind));
@@ -159,47 +159,6 @@ impl Step for CodegenBackend {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Test {
-    pub target: Interned<String>,
-}
-
-impl Step for Test {
-    type Output = ();
-    const DEFAULT: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.all_krates("test")
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Test {
-            target: run.target,
-        });
-    }
-
-    fn run(self, builder: &Builder<'_>) {
-        let compiler = builder.compiler(0, builder.config.build);
-        let target = self.target;
-
-        builder.ensure(Std { target });
-
-        let mut cargo = builder.cargo(compiler, Mode::Test, target, cargo_subcommand(builder.kind));
-        test_cargo(builder, &compiler, target, &mut cargo);
-
-        builder.info(&format!("Checking test artifacts ({} -> {})", &compiler.host, target));
-        run_cargo(builder,
-                  &mut cargo,
-                  args(builder.kind),
-                  &libtest_stamp(builder, compiler, target),
-                  true);
-
-        let libdir = builder.sysroot_libdir(compiler, target);
-        let hostdir = builder.sysroot_libdir(compiler, compiler.host);
-        add_to_sysroot(builder, &libdir, &hostdir, &libtest_stamp(builder, compiler, target));
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Rustdoc {
     pub target: Interned<String>,
@@ -258,16 +217,6 @@ pub fn libstd_stamp(
     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
 }
 
-/// Cargo's output path for libtest in a given stage, compiled by a particular
-/// compiler for the specified target.
-pub fn libtest_stamp(
-    builder: &Builder<'_>,
-    compiler: Compiler,
-    target: Interned<String>,
-) -> PathBuf {
-    builder.cargo_out(compiler, Mode::Test, target).join(".libtest-check.stamp")
-}
-
 /// Cargo's output path for librustc in a given stage, compiled by a particular
 /// compiler for the specified target.
 pub fn librustc_stamp(
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 96987d081594f..7dad146b48d83 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -216,7 +216,7 @@ pub fn std_cargo(builder: &Builder<'_>,
 
         cargo.arg("--features").arg(features)
             .arg("--manifest-path")
-            .arg(builder.src.join("src/libstd/Cargo.toml"));
+            .arg(builder.src.join("src/libtest/Cargo.toml"));
 
         if target.contains("musl") {
             if let Some(p) = builder.musl_root(target) {
@@ -358,129 +358,6 @@ impl Step for StartupObjects {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Test {
-    pub target: Interned<String>,
-    pub compiler: Compiler,
-}
-
-impl Step for Test {
-    type Output = ();
-    const DEFAULT: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.all_krates("test")
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Test {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
-            target: run.target,
-        });
-    }
-
-    /// Builds libtest.
-    ///
-    /// This will build libtest and supporting libraries for a particular stage of
-    /// the build using the `compiler` targeting the `target` architecture. The
-    /// artifacts created will also be linked into the sysroot directory.
-    fn run(self, builder: &Builder<'_>) {
-        let target = self.target;
-        let compiler = self.compiler;
-
-        builder.ensure(Std { compiler, target });
-
-        if builder.config.keep_stage.contains(&compiler.stage) {
-            builder.info("Warning: Using a potentially old libtest. This may not behave well.");
-            builder.ensure(TestLink {
-                compiler,
-                target_compiler: compiler,
-                target,
-            });
-            return;
-        }
-
-        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
-        if compiler_to_use != compiler {
-            builder.ensure(Test {
-                compiler: compiler_to_use,
-                target,
-            });
-            builder.info(
-                &format!("Uplifting stage1 test ({} -> {})", builder.config.build, target));
-            builder.ensure(TestLink {
-                compiler: compiler_to_use,
-                target_compiler: compiler,
-                target,
-            });
-            return;
-        }
-
-        let mut cargo = builder.cargo(compiler, Mode::Test, target, "build");
-        test_cargo(builder, &compiler, target, &mut cargo);
-
-        builder.info(&format!("Building stage{} test artifacts ({} -> {})", compiler.stage,
-                &compiler.host, target));
-        run_cargo(builder,
-                  &mut cargo,
-                  vec![],
-                  &libtest_stamp(builder, compiler, target),
-                  false);
-
-        builder.ensure(TestLink {
-            compiler: builder.compiler(compiler.stage, builder.config.build),
-            target_compiler: compiler,
-            target,
-        });
-    }
-}
-
-/// Same as `std_cargo`, but for libtest
-pub fn test_cargo(builder: &Builder<'_>,
-                  _compiler: &Compiler,
-                  _target: Interned<String>,
-                  cargo: &mut Command) {
-    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
-        cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
-    }
-    cargo.arg("--manifest-path")
-        .arg(builder.src.join("src/libtest/Cargo.toml"));
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TestLink {
-    pub compiler: Compiler,
-    pub target_compiler: Compiler,
-    pub target: Interned<String>,
-}
-
-impl Step for TestLink {
-    type Output = ();
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.never()
-    }
-
-    /// Same as `std_link`, only for libtest
-    fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
-        let target_compiler = self.target_compiler;
-        let target = self.target;
-        builder.info(&format!("Copying stage{} test from stage{} ({} -> {} / {})",
-                target_compiler.stage,
-                compiler.stage,
-                &compiler.host,
-                target_compiler.host,
-                target));
-        add_to_sysroot(
-            builder,
-            &builder.sysroot_libdir(target_compiler, target),
-            &builder.sysroot_libdir(target_compiler, compiler.host),
-            &libtest_stamp(builder, compiler, target)
-        );
-    }
-}
-
 #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
     pub target: Interned<String>,
@@ -512,7 +389,7 @@ impl Step for Rustc {
         let compiler = self.compiler;
         let target = self.target;
 
-        builder.ensure(Test { compiler, target });
+        builder.ensure(Std { compiler, target });
 
         if builder.config.keep_stage.contains(&compiler.stage) {
             builder.info("Warning: Using a potentially old librustc. This may not behave well.");
@@ -541,7 +418,7 @@ impl Step for Rustc {
         }
 
         // Ensure that build scripts and proc macros have a std / libproc_macro to link against.
-        builder.ensure(Test {
+        builder.ensure(Std {
             compiler: builder.compiler(self.compiler.stage, builder.config.build),
             target: builder.config.build,
         });
@@ -872,16 +749,6 @@ pub fn libstd_stamp(
     builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp")
 }
 
-/// Cargo's output path for libtest in a given stage, compiled by a particular
-/// compiler for the specified target.
-pub fn libtest_stamp(
-    builder: &Builder<'_>,
-    compiler: Compiler,
-    target: Interned<String>,
-) -> PathBuf {
-    builder.cargo_out(compiler, Mode::Test, target).join(".libtest.stamp")
-}
-
 /// Cargo's output path for librustc in a given stage, compiled by a particular
 /// compiler for the specified target.
 pub fn librustc_stamp(
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 213ceb194a814..0f4ac63651ca9 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -678,12 +678,7 @@ impl Step for Std {
         if builder.hosts.iter().any(|t| t == target) {
             builder.ensure(compile::Rustc { compiler, target });
         } else {
-            if builder.no_std(target) == Some(true) {
-                // the `test` doesn't compile for no-std targets
-                builder.ensure(compile::Std { compiler, target });
-            } else {
-                builder.ensure(compile::Test { compiler, target });
-            }
+            builder.ensure(compile::Std { compiler, target });
         }
 
         let image = tmpdir(builder).join(format!("{}-{}-image", name, target));
@@ -912,6 +907,7 @@ impl Step for Src {
             "src/libproc_macro",
             "src/tools/rustc-std-workspace-core",
             "src/tools/rustc-std-workspace-alloc",
+            "src/tools/rustc-std-workspace-std",
             "src/librustc",
             "src/libsyntax",
         ];
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 4f96c12fc1ddd..6805474aa049f 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -478,138 +478,12 @@ impl Step for Std {
             builder.run(&mut cargo);
             builder.cp_r(&my_out, &out);
         };
-        for krate in &["alloc", "core", "std"] {
+        for krate in &["alloc", "core", "std", "proc_macro", "test"] {
             run_cargo_rustdoc_for(krate);
         }
     }
 }
 
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub struct Test {
-    stage: u32,
-    target: Interned<String>,
-}
-
-impl Step for Test {
-    type Output = ();
-    const DEFAULT: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        let builder = run.builder;
-        run.krate("test").default_condition(builder.config.docs)
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Test {
-            stage: run.builder.top_stage,
-            target: run.target,
-        });
-    }
-
-    /// Compile all libtest documentation.
-    ///
-    /// This will generate all documentation for libtest and its dependencies. This
-    /// is largely just a wrapper around `cargo doc`.
-    fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
-        let target = self.target;
-        builder.info(&format!("Documenting stage{} test ({})", stage, target));
-        let out = builder.doc_out(target);
-        t!(fs::create_dir_all(&out));
-        let compiler = builder.compiler_for(stage, builder.config.build, target);
-
-        // Build libstd docs so that we generate relative links
-        builder.ensure(Std { stage, target });
-
-        builder.ensure(compile::Test { compiler, target });
-        let out_dir = builder.stage_out(compiler, Mode::Test)
-                           .join(target).join("doc");
-
-        // See docs in std above for why we symlink
-        let my_out = builder.crate_doc_out(target);
-        t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
-
-        let mut cargo = builder.cargo(compiler, Mode::Test, target, "doc");
-        compile::test_cargo(builder, &compiler, target, &mut cargo);
-
-        cargo.arg("--no-deps")
-             .arg("-p").arg("test")
-             .env("RUSTDOC_RESOURCE_SUFFIX", crate::channel::CFG_RELEASE_NUM)
-             .env("RUSTDOC_GENERATE_REDIRECT_PAGES", "1");
-
-        builder.run(&mut cargo);
-        builder.cp_r(&my_out, &out);
-    }
-}
-
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub struct WhitelistedRustc {
-    stage: u32,
-    target: Interned<String>,
-}
-
-impl Step for WhitelistedRustc {
-    type Output = ();
-    const DEFAULT: bool = true;
-    const ONLY_HOSTS: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        let builder = run.builder;
-        run.krate("rustc-main").default_condition(builder.config.docs)
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(WhitelistedRustc {
-            stage: run.builder.top_stage,
-            target: run.target,
-        });
-    }
-
-    /// Generates whitelisted compiler crate documentation.
-    ///
-    /// This will generate all documentation for crates that are whitelisted
-    /// to be included in the standard documentation. This documentation is
-    /// included in the standard Rust documentation, so we should always
-    /// document it and symlink to merge with the rest of the std and test
-    /// documentation. We don't build other compiler documentation
-    /// here as we want to be able to keep it separate from the standard
-    /// documentation. This is largely just a wrapper around `cargo doc`.
-    fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
-        let target = self.target;
-        builder.info(&format!("Documenting stage{} whitelisted compiler ({})", stage, target));
-        let out = builder.doc_out(target);
-        t!(fs::create_dir_all(&out));
-        let compiler = builder.compiler_for(stage, builder.config.build, target);
-
-        // Build libstd docs so that we generate relative links
-        builder.ensure(Std { stage, target });
-
-        builder.ensure(compile::Rustc { compiler, target });
-        let out_dir = builder.stage_out(compiler, Mode::Rustc)
-                           .join(target).join("doc");
-
-        // See docs in std above for why we symlink
-        let my_out = builder.crate_doc_out(target);
-        t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
-
-        let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
-        compile::rustc_cargo(builder, &mut cargo);
-
-        // We don't want to build docs for internal compiler dependencies in this
-        // step (there is another step for that). Therefore, we whitelist the crates
-        // for which docs must be built.
-        for krate in &["proc_macro"] {
-            cargo.arg("-p").arg(krate)
-                 .env("RUSTDOC_RESOURCE_SUFFIX", crate::channel::CFG_RELEASE_NUM)
-                 .env("RUSTDOC_GENERATE_REDIRECT_PAGES", "1");
-        }
-
-        builder.run(&mut cargo);
-        builder.cp_r(&my_out, &out);
-    }
-}
-
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Rustc {
     stage: u32,
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index b72aa78f3de19..c0e0ad1a857b9 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -297,9 +297,6 @@ pub enum Mode {
     /// Build the standard library, placing output in the "stageN-std" directory.
     Std,
 
-    /// Build libtest, placing output in the "stageN-test" directory.
-    Test,
-
     /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.
     Rustc,
 
@@ -315,7 +312,6 @@ pub enum Mode {
     /// Compile a tool which uses all libraries we compile (up to rustc).
     /// Doesn't use the stage0 compiler libraries like "other", and includes
     /// tools like rustdoc, cargo, rls, etc.
-    ToolTest,
     ToolStd,
     ToolRustc,
 }
@@ -536,11 +532,10 @@ impl Build {
     fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
         let suffix = match mode {
             Mode::Std => "-std",
-            Mode::Test => "-test",
             Mode::Rustc => "-rustc",
             Mode::Codegen => "-codegen",
             Mode::ToolBootstrap => "-bootstrap-tools",
-            Mode::ToolStd | Mode::ToolTest | Mode::ToolRustc => "-tools",
+            Mode::ToolStd | Mode::ToolRustc => "-tools",
         };
         self.out.join(&*compiler.host)
                 .join(format!("stage{}{}", compiler.stage, suffix))
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 87bd5cbacfffa..2bb053cc2b002 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1040,21 +1040,10 @@ impl Step for Compiletest {
             builder.ensure(compile::Rustc { compiler, target });
         }
 
-        if builder.no_std(target) == Some(true) {
-            // the `test` doesn't compile for no-std targets
-            builder.ensure(compile::Std { compiler, target });
-        } else {
-            builder.ensure(compile::Test { compiler, target });
-        }
-
-        if builder.no_std(target) == Some(true) {
-            // for no_std run-make (e.g., thumb*),
-            // we need a host compiler which is called by cargo.
-            builder.ensure(compile::Std { compiler, target: compiler.host });
-        }
+        builder.ensure(compile::Std { compiler, target });
+        // ensure that `libproc_macro` is available on the host.
+        builder.ensure(compile::Std { compiler, target: compiler.host });
 
-        // HACK(eddyb) ensure that `libproc_macro` is available on the host.
-        builder.ensure(compile::Test { compiler, target: compiler.host });
         // Also provide `rust_test_helpers` for the host.
         builder.ensure(native::TestHelpers { target: compiler.host });
 
@@ -1399,7 +1388,7 @@ impl Step for DocTest {
     fn run(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
 
-        builder.ensure(compile::Test {
+        builder.ensure(compile::Std {
             compiler,
             target: compiler.host,
         });
@@ -1709,8 +1698,7 @@ impl Step for Crate {
 
     fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run = run.krate("test");
-        for krate in run.builder.in_tree_crates("std") {
+        for krate in run.builder.in_tree_crates("test") {
             if !(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) {
                 run = run.path(krate.local_path(&builder).to_str().unwrap());
             }
@@ -1734,14 +1722,9 @@ impl Step for Crate {
             });
         };
 
-        for krate in builder.in_tree_crates("std") {
-            if run.path.ends_with(&krate.local_path(&builder)) {
-                make(Mode::Std, krate);
-            }
-        }
         for krate in builder.in_tree_crates("test") {
             if run.path.ends_with(&krate.local_path(&builder)) {
-                make(Mode::Test, krate);
+                make(Mode::Std, krate);
             }
         }
     }
@@ -1761,7 +1744,7 @@ impl Step for Crate {
         let test_kind = self.test_kind;
         let krate = self.krate;
 
-        builder.ensure(compile::Test { compiler, target });
+        builder.ensure(compile::Std { compiler, target });
         builder.ensure(RemoteCopyLibs { compiler, target });
 
         // If we're not doing a full bootstrap but we're testing a stage2
@@ -1775,9 +1758,6 @@ impl Step for Crate {
             Mode::Std => {
                 compile::std_cargo(builder, &compiler, target, &mut cargo);
             }
-            Mode::Test => {
-                compile::test_cargo(builder, &compiler, target, &mut cargo);
-            }
             Mode::Rustc => {
                 builder.ensure(compile::Rustc { compiler, target });
                 compile::rustc_cargo(builder, &mut cargo);
@@ -1979,7 +1959,7 @@ impl Step for RemoteCopyLibs {
             return;
         }
 
-        builder.ensure(compile::Test { compiler, target });
+        builder.ensure(compile::Std { compiler, target });
 
         builder.info(&format!("REMOTE copy libs to emulator ({})", target));
         t!(fs::create_dir_all(builder.out.join("tmp")));
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index df7eb7c455d02..54fe26f18e741 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -577,12 +577,6 @@ impl Step for Cargo {
     }
 
     fn run(self, builder: &Builder<'_>) -> PathBuf {
-        // Cargo depends on procedural macros, so make sure the host
-        // libstd/libproc_macro is available.
-        builder.ensure(compile::Test {
-            compiler: self.compiler,
-            target: builder.config.build,
-        });
         builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
@@ -650,31 +644,10 @@ macro_rules! tool_extended {
 
 tool_extended!((self, builder),
     Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {};
-    CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", {
-        // Clippy depends on procedural macros, so make sure that's built for
-        // the compiler itself.
-        builder.ensure(compile::Test {
-            compiler: self.compiler,
-            target: builder.config.build,
-        });
-    };
-    Clippy, clippy, "src/tools/clippy", "clippy-driver", {
-        // Clippy depends on procedural macros, so make sure that's built for
-        // the compiler itself.
-        builder.ensure(compile::Test {
-            compiler: self.compiler,
-            target: builder.config.build,
-        });
-    };
+    CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", {};
+    Clippy, clippy, "src/tools/clippy", "clippy-driver", {};
     Miri, miri, "src/tools/miri", "miri", {};
-    CargoMiri, miri, "src/tools/miri", "cargo-miri", {
-        // Miri depends on procedural macros, so make sure that's built for
-        // the compiler itself.
-        builder.ensure(compile::Test {
-            compiler: self.compiler,
-            target: builder.config.build,
-        });
-    };
+    CargoMiri, miri, "src/tools/miri", "cargo-miri", {};
     Rls, rls, "src/tools/rls", "rls", {
         let clippy = builder.ensure(Clippy {
             compiler: self.compiler,
@@ -684,12 +657,6 @@ tool_extended!((self, builder),
         if clippy.is_some() {
             self.extra_features.push("clippy".to_owned());
         }
-        // RLS depends on procedural macros, so make sure that's built for
-        // the compiler itself.
-        builder.ensure(compile::Test {
-            compiler: self.compiler,
-            target: builder.config.build,
-        });
     };
     Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {};
 );
diff --git a/src/libproc_macro/Cargo.toml b/src/libproc_macro/Cargo.toml
index b3d0ee94f0e12..187bdac80019d 100644
--- a/src/libproc_macro/Cargo.toml
+++ b/src/libproc_macro/Cargo.toml
@@ -6,3 +6,6 @@ edition = "2018"
 
 [lib]
 path = "lib.rs"
+
+[dependencies]
+std = { path = "../libstd" }
diff --git a/src/libterm/Cargo.toml b/src/libterm/Cargo.toml
index 4eba9a9d79cc4..2931e0bda9518 100644
--- a/src/libterm/Cargo.toml
+++ b/src/libterm/Cargo.toml
@@ -5,6 +5,8 @@ version = "0.0.0"
 edition = "2018"
 
 [lib]
-name = "term"
 path = "lib.rs"
-crate-type = ["dylib", "rlib"]
+
+[dependencies]
+core = { path = "../libcore" }
+std = { path = "../libstd" }
diff --git a/src/libtest/Cargo.toml b/src/libtest/Cargo.toml
index a72e4c7050289..170fbb984cf9b 100644
--- a/src/libtest/Cargo.toml
+++ b/src/libtest/Cargo.toml
@@ -10,8 +10,22 @@ path = "lib.rs"
 crate-type = ["dylib", "rlib"]
 
 [dependencies]
-getopts = "0.2.19"
+getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] }
 term = { path = "../libterm" }
+std = { path = "../libstd" }
+core = { path = "../libcore" }
+libc = { version = "0.2", default-features = false }
+panic_unwind = { path = "../libpanic_unwind" }
+panic_abort = { path = "../libpanic_abort" }
 
 # not actually used but needed to always have proc_macro in the sysroot
 proc_macro = { path = "../libproc_macro" }
+
+# Forward features to the `std` crate as necessary
+[features]
+backtrace = ["std/backtrace"]
+compiler-builtins-c = ["std/compiler-builtins-c"]
+llvm-libunwind = ["std/llvm-libunwind"]
+panic-unwind = ["std/panic_unwind"]
+panic_immediate_abort = ["std/panic_immediate_abort"]
+profiler = ["std/profiler"]
diff --git a/src/tools/rustc-std-workspace-std/Cargo.toml b/src/tools/rustc-std-workspace-std/Cargo.toml
new file mode 100644
index 0000000000000..ce1644809dbe6
--- /dev/null
+++ b/src/tools/rustc-std-workspace-std/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "rustc-std-workspace-std"
+version = "1.0.0"
+authors = ["Alex Crichton <alex@alexcrichton.com>"]
+license = 'MIT OR Apache-2.0'
+description = """
+Hack for the compiler's own build system
+"""
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+std = { path = "../../libstd" }
diff --git a/src/tools/rustc-std-workspace-std/README.md b/src/tools/rustc-std-workspace-std/README.md
new file mode 100644
index 0000000000000..2228907f304c4
--- /dev/null
+++ b/src/tools/rustc-std-workspace-std/README.md
@@ -0,0 +1,3 @@
+# The `rustc-std-workspace-std` crate
+
+See documentation for the `rustc-std-workspace-core` crate.
diff --git a/src/tools/rustc-std-workspace-std/lib.rs b/src/tools/rustc-std-workspace-std/lib.rs
new file mode 100644
index 0000000000000..f40d09cafbb47
--- /dev/null
+++ b/src/tools/rustc-std-workspace-std/lib.rs
@@ -0,0 +1 @@
+pub use std::*;
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index de54eb8f57312..e07a07234c71e 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -90,15 +90,18 @@ const WHITELIST: &[Crate<'_>] = &[
     Crate("crossbeam-epoch"),
     Crate("crossbeam-utils"),
     Crate("datafrog"),
+    Crate("dlmalloc"),
     Crate("either"),
     Crate("ena"),
     Crate("env_logger"),
     Crate("filetime"),
     Crate("flate2"),
+    Crate("fortanix-sgx-abi"),
     Crate("fuchsia-zircon"),
     Crate("fuchsia-zircon-sys"),
     Crate("getopts"),
     Crate("getrandom"),
+    Crate("hashbrown"),
     Crate("humantime"),
     Crate("indexmap"),
     Crate("itertools"),