diff --git a/Cargo.toml b/Cargo.toml
index acaab28..c328c46 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,12 +1,22 @@
+# We set up our own workspace that contains the library crates.
+# `./run-test.sh` makes the `library` folder a symlink to the right place.
+# (Somehow, directly building things from `rustlib/src` does not work...)
 [workspace]
 members = [
-  "core_miri_test",
-  "alloc_miri_test",
-  "std_miri_test",
+  "library/std",
+  "library/sysroot",
 ]
 
 exclude = [
   # stdarch has its own Cargo workspace
   "library/stdarch",
-  "rust-src-patched/library/stdarch",
+  # this is just a staging ground for CI
+  "rust-src-patched",
 ]
+
+[patch.crates-io]
+# See comments in `library/rustc-std-workspace-core/README.md` for what's going on
+# here
+rustc-std-workspace-core = { path = 'library/rustc-std-workspace-core' }
+rustc-std-workspace-alloc = { path = 'library/rustc-std-workspace-alloc' }
+rustc-std-workspace-std = { path = 'library/rustc-std-workspace-std' }
diff --git a/alloc_miri_test/Cargo.toml b/alloc_miri_test/Cargo.toml
deleted file mode 100644
index ac870d2..0000000
--- a/alloc_miri_test/Cargo.toml
+++ /dev/null
@@ -1,29 +0,0 @@
-[package]
-name = "alloc_miri_test"
-version = "0.0.0"
-autotests = false
-autobenches = false
-edition = "2021"
-
-[lib]
-name = "alloc_miri_test"
-path = "../library/alloc/src/lib.rs"
-
-[features]
-# Empty this crate to avoid two copies of liballoc.
-# See https://github.com/rust-lang/miri-test-libstd/issues/4.
-default = ["miri-test-libstd"]
-miri-test-libstd = []
-
-[dependencies]
-# This lets the crate access the `core` and `alloc` crates.
-core = { path = "../fake/core" }
-alloc = { path = "../fake/alloc" }
-
-[dev-dependencies]
-rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
-rand_xorshift = "0.3.0"
-
-[[test]]
-name = "collectionstests"
-path = "../library/alloc/tests/lib.rs"
diff --git a/ci-test.sh b/ci-test.sh
index eef415b..9bb1243 100644
--- a/ci-test.sh
+++ b/ci-test.sh
@@ -85,9 +85,10 @@ std)
     echo "::endgroup::"
     ;;
 simd)
-    cd $MIRI_LIB_SRC/portable-simd
+    export CARGO_TARGET_DIR=$(pwd)/target
     export RUSTFLAGS="-Ainternal_features ${RUSTFLAGS:-}"
     export RUSTDOCFLAGS="-Ainternal_features ${RUSTDOCFLAGS:-}"
+    cd $MIRI_LIB_SRC/portable-simd
 
     echo "::group::Testing portable-simd"
     MIRIFLAGS="$DEFAULTFLAGS" \
diff --git a/core_miri_test/Cargo.toml b/core_miri_test/Cargo.toml
deleted file mode 100644
index 3188dea..0000000
--- a/core_miri_test/Cargo.toml
+++ /dev/null
@@ -1,31 +0,0 @@
-[package]
-authors = ["The Rust Project Developers"]
-name = "core_miri_test"
-version = "0.0.0"
-autotests = false
-autobenches = false
-edition = "2021"
-
-[lib]
-name = "core_miri_test"
-path = "../library/core/src/lib.rs"
-test = false
-bench = false
-
-[features]
-# Empty this crate to avoid two copies of libcore.
-# See https://github.com/rust-lang/miri-test-libstd/issues/4.
-default = ["miri-test-libstd"]
-miri-test-libstd = []
-
-[[test]]
-name = "coretests"
-path = "../library/core/tests/lib.rs"
-
-[dependencies]
-# This lets the crate access the `core` crate.
-core = { path = "../fake/core" }
-
-[dev-dependencies]
-rand = { version = "0.8.5", default-features = false }
-rand_xorshift = { version = "0.3.0", default-features = false }
diff --git a/fake/README.md b/fake/README.md
deleted file mode 100644
index 376d73d..0000000
--- a/fake/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-These crates all just forward the contents of a sysroot crate.
-This is so that we can add them as dependencies to `std`, so the imports there work out.
diff --git a/fake/alloc/Cargo.toml b/fake/alloc/Cargo.toml
deleted file mode 100644
index e5a5167..0000000
--- a/fake/alloc/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "alloc"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/alloc/lib.rs b/fake/alloc/lib.rs
deleted file mode 100644
index c38a8d2..0000000
--- a/fake/alloc/lib.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![feature(no_core)]
-#![no_core]
-
-// See rustc-std-workspace-core for why this crate is needed.
-
-// Rename the crate to avoid conflicting with the alloc module in liballoc.
-extern crate alloc as foo;
-
-pub use foo::*;
diff --git a/fake/cfg-if/Cargo.toml b/fake/cfg-if/Cargo.toml
deleted file mode 100644
index c671815..0000000
--- a/fake/cfg-if/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "cfg-if"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/cfg-if/lib.rs b/fake/cfg-if/lib.rs
deleted file mode 100644
index b7f64f7..0000000
--- a/fake/cfg-if/lib.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#![feature(rustc_private)]
-extern crate cfg_if;
-pub use cfg_if::*;
diff --git a/fake/core/Cargo.toml b/fake/core/Cargo.toml
deleted file mode 100644
index 0848f01..0000000
--- a/fake/core/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "core"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/core/lib.rs b/fake/core/lib.rs
deleted file mode 100644
index c6e2665..0000000
--- a/fake/core/lib.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#![feature(no_core)]
-#![no_core]
-
-extern crate core;
-pub use core::*;
diff --git a/fake/hashbrown/Cargo.toml b/fake/hashbrown/Cargo.toml
deleted file mode 100644
index 44fcc42..0000000
--- a/fake/hashbrown/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "hashbrown"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/hashbrown/lib.rs b/fake/hashbrown/lib.rs
deleted file mode 100644
index ffff3da..0000000
--- a/fake/hashbrown/lib.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#![feature(rustc_private)]
-extern crate hashbrown;
-pub use hashbrown::*;
diff --git a/fake/libc/Cargo.toml b/fake/libc/Cargo.toml
deleted file mode 100644
index 1ae1236..0000000
--- a/fake/libc/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "libc"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/libc/lib.rs b/fake/libc/lib.rs
deleted file mode 100644
index 8418f20..0000000
--- a/fake/libc/lib.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#![feature(rustc_private)]
-extern crate libc;
-pub use libc::*;
diff --git a/fake/rustc-demangle/Cargo.toml b/fake/rustc-demangle/Cargo.toml
deleted file mode 100644
index 0162036..0000000
--- a/fake/rustc-demangle/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "rustc-demangle"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/rustc-demangle/lib.rs b/fake/rustc-demangle/lib.rs
deleted file mode 100644
index ae71bb2..0000000
--- a/fake/rustc-demangle/lib.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#![feature(rustc_private)]
-extern crate rustc_demangle;
-pub use rustc_demangle::*;
diff --git a/fake/std_detect/Cargo.toml b/fake/std_detect/Cargo.toml
deleted file mode 100644
index 3682a18..0000000
--- a/fake/std_detect/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "std_detect"
-version = "1.99.0"
-license = 'MIT OR Apache-2.0'
-
-[lib]
-path = "lib.rs"
diff --git a/fake/std_detect/lib.rs b/fake/std_detect/lib.rs
deleted file mode 100644
index 2e33251..0000000
--- a/fake/std_detect/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![feature(rustc_private, stdarch_internal)]
-#![allow(internal_features)] // yes we are doing very internal stuff here
-extern crate std_detect;
-pub use std_detect::*;
diff --git a/run-stdarch-test.sh b/run-stdarch-test.sh
index 66745bb..7be4d35 100755
--- a/run-stdarch-test.sh
+++ b/run-stdarch-test.sh
@@ -6,7 +6,7 @@ set -euo pipefail
 ## Usage:
 ##   ./run-test.sh TARGET
 ## Environment variables:
-##   MIRI_LIB_SRC: The path to the Rust library directory (`library`).
+##   MIRI_LIB_SRC: The path to the Rust `library` directory (optional).
 ##   RUSTFLAGS: rustc flags (optional)
 ##   MIRIFLAGS: Miri flags (optional)
 
@@ -40,8 +40,11 @@ export STDARCH_TEST_EVERYTHING=1
 # Needed to pass the STDARCH_TEST_EVERYTHING environment variable
 export MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-disable-isolation"
 
-cd $MIRI_LIB_SRC/stdarch
+# Set library source dir
+export MIRI_LIB_SRC=${MIRI_LIB_SRC:-$(rustc --print sysroot)/lib/rustlib/src/rust/library}
+
+export CARGO_TARGET_DIR=$(pwd)/target
 cargo miri test \
+    --manifest-path=$MIRI_LIB_SRC/stdarch/crates/core_arch/Cargo.toml \
     --target "$TARGET" \
-    --manifest-path=crates/core_arch/Cargo.toml \
     -- "${TEST_ARGS[@]}"
diff --git a/run-test.sh b/run-test.sh
index 4b9f39f..4f5ba0e 100755
--- a/run-test.sh
+++ b/run-test.sh
@@ -35,6 +35,10 @@ ln -s "$MIRI_LIB_SRC" library
 # use the rust-src lockfile
 cp "$MIRI_LIB_SRC/../Cargo.lock" Cargo.lock
 
+# This ensures that the "core" crate being built as part of `cargo miri test`
+# is just a re-export of the sysroot crate, so we don't get duplicate lang items.
+export MIRI_REPLACE_LIBRS_IF_NOT_TEST=1
+
 # run test
-cd ./${CRATE}_miri_test
-cargo miri test "$@"
+export CARGO_TARGET_DIR=$(pwd)/target
+cargo miri test --manifest-path "library/$CRATE/Cargo.toml" "$@"
diff --git a/rust-src.diff b/rust-src.diff
index e69de29..66b0aa1 100644
--- a/rust-src.diff
+++ b/rust-src.diff
@@ -0,0 +1,57 @@
+diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs
+index 638f343fb24..0561f49c967 100644
+--- a/library/alloc/benches/lib.rs
++++ b/library/alloc/benches/lib.rs
+@@ -1,6 +1,8 @@
+ // Disabling on android for the time being
+ // See https://github.com/rust-lang/rust/issues/73535#event-3477699747
+ #![cfg(not(target_os = "android"))]
++// Disabling in Miri as these would take too long.
++#![cfg(not(miri))]
+ #![feature(btree_extract_if)]
+ #![feature(iter_next_chunk)]
+ #![feature(repr_simd)]
+diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs
+index 4d14b930e41..32d15c386cb 100644
+--- a/library/core/benches/lib.rs
++++ b/library/core/benches/lib.rs
+@@ -1,5 +1,7 @@
+ // wasm32 does not support benches (no time).
+ #![cfg(not(target_arch = "wasm32"))]
++// Disabling in Miri as these would take too long.
++#![cfg(not(miri))]
+ #![feature(flt2dec)]
+ #![feature(test)]
+ #![feature(trusted_random_access)]
+diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs
+index 4d1cf7fab7b..1b21c230a0b 100644
+--- a/library/std/benches/lib.rs
++++ b/library/std/benches/lib.rs
+@@ -1,3 +1,5 @@
++// Disabling in Miri as these would take too long.
++#![cfg(not(miri))]
+ #![feature(test)]
+ 
+ extern crate test;
+diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs
+index 59f67f9901f..2b7997299c5 100644
+--- a/library/std/tests/process_spawning.rs
++++ b/library/std/tests/process_spawning.rs
+@@ -1,4 +1,6 @@
+ #![cfg(not(target_env = "sgx"))]
++// Process spawning does not work in Miri.
++#![cfg(not(miri))]
+ 
+ use std::env;
+ use std::fs;
+diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs
+index 27f3e8a9b96..a80f24fcb19 100644
+--- a/library/std/tests/switch-stdout.rs
++++ b/library/std/tests/switch-stdout.rs
+@@ -1,4 +1,6 @@
+ #![cfg(any(target_family = "unix", target_family = "windows"))]
++// Calls functions that are not supported by Miri.
++#![cfg(not(miri))]
+ 
+ use std::fs::File;
+ use std::io::{Read, Write};
diff --git a/rust-version b/rust-version
index 913163d..33cec4b 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-nightly-2024-04-01
+nightly-2024-04-06
diff --git a/std_miri_test/Cargo.toml b/std_miri_test/Cargo.toml
deleted file mode 100644
index f231dcc..0000000
--- a/std_miri_test/Cargo.toml
+++ /dev/null
@@ -1,51 +0,0 @@
-[package]
-name = "std_miri_test"
-version = "0.0.0"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/rust-lang/rust.git"
-description = "The Rust Standard Library"
-autotests = false
-autobenches = false
-edition = "2021"
-build = "../library/std/build.rs"
-
-[lib]
-name = "std_miri_test"
-path = "../library/std/src/lib.rs"
-
-[dependencies]
-# just a bunch of fake crates that reeexport sysroot crates, so that std's imports work out
-# (this works because we only build std as a test; the regular crate build is
-# completely empty thanks to the `miri-test-libstd` feature below)
-core = { path = "../fake/core" }
-alloc = { path = "../fake/alloc" }
-cfg-if = { path = "../fake/cfg-if" }
-libc = { path = "../fake/libc" }
-hashbrown = { path = "../fake/hashbrown" }
-std_detect = { path = "../fake/std_detect" }
-rustc-demangle = { path = "../fake/rustc-demangle" }
-
-[dev-dependencies]
-rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
-rand_xorshift = "0.3.0"
-
-[build-dependencies]
-# Dependency of the `backtrace` crate's build script
-cc = "1.0.67"
-
-[features]
-# Empty this crate
-default = ["miri-test-libstd"]
-miri-test-libstd = []
-
-[[test]]
-name = "env"
-path = "../library/std/tests/env.rs"
-
-[[test]]
-name = "thread"
-path = "../library/std/tests/thread.rs"
-
-[[test]]
-name = "run-time-detect"
-path = "../library/std/tests/run-time-detect.rs"