Rollup merge of #125008 - Dirbaio:test-issue-122775, r=compiler-errors

Add test for #122775

Closes #122775
diff --git a/Cargo.lock b/Cargo.lock
index a562bdb..f49bcf7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -68,9 +68,9 @@
 
 [[package]]
 name = "allocator-api2"
-version = "0.2.16"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
 
 [[package]]
 name = "ammonia"
@@ -141,56 +141,57 @@
 
 [[package]]
 name = "anstream"
-version = "0.6.13"
+version = "0.6.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
  "anstyle-wincon",
  "colorchoice",
+ "is_terminal_polyfill",
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle"
-version = "1.0.6"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
 
 [[package]]
 name = "anstyle-lossy"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a0444767dbd4aea9355cb47a370eb184dbfe918875e127eff52cb9d1638181"
+checksum = "6fcff6599f06e21b0165c85052ccd6e67dc388ddd1c516a9dc5f55dc8cacf004"
 dependencies = [
  "anstyle",
 ]
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
 dependencies = [
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-svg"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b6ddad447b448d6d5db36b31cbd3ff27c7af071619501998eeceab01968287a"
+checksum = "bbbf0bf947d663010f0b4132f28ca08da9151f3b9035fa7578a38de521c1d1aa"
 dependencies = [
  "anstream",
  "anstyle",
@@ -201,9 +202,9 @@
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.2"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
 dependencies = [
  "anstyle",
  "windows-sys 0.52.0",
@@ -211,9 +212,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.81"
+version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
 dependencies = [
  "backtrace",
 ]
@@ -256,7 +257,7 @@
  "proc-macro2",
  "quote",
  "serde",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -276,9 +277,9 @@
 
 [[package]]
 name = "autocfg"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
 name = "backtrace"
@@ -389,18 +390,15 @@
 
 [[package]]
 name = "bumpalo"
-version = "3.15.4"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
 name = "bytecount"
-version = "0.6.7"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
-dependencies = [
- "packed_simd",
-]
+checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
 
 [[package]]
 name = "byteorder"
@@ -509,7 +507,7 @@
  "iana-time-zone",
  "num-traits",
  "serde",
- "windows-targets 0.52.4",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -579,9 +577,9 @@
 
 [[package]]
 name = "clap_complete"
-version = "4.5.1"
+version = "4.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c"
+checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e"
 dependencies = [
  "clap",
 ]
@@ -595,7 +593,7 @@
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -622,7 +620,7 @@
  "regex",
  "rustc_tools_util",
  "serde",
- "syn 2.0.58",
+ "syn 2.0.62",
  "tempfile",
  "termize",
  "tokio",
@@ -716,23 +714,23 @@
 
 [[package]]
 name = "color-print"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d"
+checksum = "1ee543c60ff3888934877a5671f45494dd27ed4ba25c6670b9a7576b7ed7a8c0"
 dependencies = [
  "color-print-proc-macro",
 ]
 
 [[package]]
 name = "color-print-proc-macro"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f"
+checksum = "77ff1a80c5f3cb1ca7c06ffdd71b6a6dd6d8f896c42141fbd43f50ed28dcdb93"
 dependencies = [
  "nom",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -749,9 +747,9 @@
 
 [[package]]
 name = "colorchoice"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
 
 [[package]]
 name = "colored"
@@ -978,7 +976,7 @@
  "proc-macro2",
  "quote",
  "strsim 0.10.0",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -989,7 +987,7 @@
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1004,7 +1002,7 @@
 dependencies = [
  "itertools 0.12.1",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1045,7 +1043,7 @@
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1055,7 +1053,7 @@
 checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
 dependencies = [
  "derive_builder_core",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1078,7 +1076,7 @@
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1167,14 +1165,14 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
 name = "dissimilar"
-version = "1.0.7"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
+checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
 
 [[package]]
 name = "dlmalloc"
@@ -1191,9 +1189,9 @@
 
 [[package]]
 name = "either"
-version = "1.10.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
 
 [[package]]
 name = "elasticlunr-rs"
@@ -1233,9 +1231,9 @@
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.33"
+version = "0.8.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
 dependencies = [
  "cfg-if",
 ]
@@ -1271,9 +1269,9 @@
 
 [[package]]
 name = "errno"
-version = "0.3.8"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
@@ -1314,9 +1312,9 @@
 
 [[package]]
 name = "fastrand"
-version = "2.0.2"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
 
 [[package]]
 name = "field-offset"
@@ -1336,15 +1334,15 @@
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.4.1",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "flate2"
-version = "1.0.28"
+version = "1.0.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -1352,9 +1350,9 @@
 
 [[package]]
 name = "fluent-bundle"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
+checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493"
 dependencies = [
  "fluent-langneg",
  "fluent-syntax",
@@ -1377,9 +1375,9 @@
 
 [[package]]
 name = "fluent-syntax"
-version = "0.11.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
+checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
 dependencies = [
  "thiserror",
 ]
@@ -1505,7 +1503,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -1577,9 +1575,9 @@
 
 [[package]]
 name = "getrandom"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1663,9 +1661,9 @@
 
 [[package]]
 name = "hashbrown"
-version = "0.14.3"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 dependencies = [
  "ahash",
  "allocator-api2",
@@ -1943,7 +1941,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -2055,9 +2053,9 @@
 
 [[package]]
 name = "intl-memoizer"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
+checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda"
 dependencies = [
  "type-map",
  "unic-langid",
@@ -2079,6 +2077,12 @@
 checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
 
 [[package]]
+name = "is_terminal_polyfill"
+version = "1.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+
+[[package]]
 name = "itertools"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2114,9 +2118,9 @@
 
 [[package]]
 name = "jobserver"
-version = "0.1.28"
+version = "0.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
 dependencies = [
  "libc",
 ]
@@ -2219,7 +2223,7 @@
 checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
 dependencies = [
  "cfg-if",
- "windows-targets 0.52.4",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -2296,9 +2300,9 @@
 
 [[package]]
 name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -2384,7 +2388,7 @@
  "memchr",
  "once_cell",
  "opener",
- "pulldown-cmark 0.10.2",
+ "pulldown-cmark 0.10.3",
  "regex",
  "serde",
  "serde_json",
@@ -2615,12 +2619,11 @@
 
 [[package]]
 name = "num-traits"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
- "libm",
 ]
 
 [[package]]
@@ -2717,7 +2720,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -2783,16 +2786,6 @@
 checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
 
 [[package]]
-name = "packed_simd"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d"
-dependencies = [
- "cfg-if",
- "num-traits",
-]
-
-[[package]]
 name = "pad"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2837,9 +2830,9 @@
 
 [[package]]
 name = "parking_lot"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -2847,15 +2840,15 @@
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.5.1",
  "smallvec",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -2890,9 +2883,9 @@
 
 [[package]]
 name = "pest"
-version = "2.7.9"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95"
+checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8"
 dependencies = [
  "memchr",
  "thiserror",
@@ -2901,9 +2894,9 @@
 
 [[package]]
 name = "pest_derive"
-version = "2.7.9"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c"
+checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459"
 dependencies = [
  "pest",
  "pest_generator",
@@ -2911,22 +2904,22 @@
 
 [[package]]
 name = "pest_generator"
-version = "2.7.9"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd"
+checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.7.9"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca"
+checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd"
 dependencies = [
  "once_cell",
  "pest",
@@ -3080,9 +3073,9 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.79"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
 dependencies = [
  "unicode-ident",
 ]
@@ -3126,9 +3119,9 @@
 
 [[package]]
 name = "pulldown-cmark"
-version = "0.10.2"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c"
+checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
 dependencies = [
  "bitflags 2.5.0",
  "memchr",
@@ -3138,9 +3131,9 @@
 
 [[package]]
 name = "pulldown-cmark-escape"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b"
+checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3"
 
 [[package]]
 name = "punycode"
@@ -3156,9 +3149,9 @@
 
 [[package]]
 name = "quote"
-version = "1.0.35"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -3262,6 +3255,15 @@
 ]
 
 [[package]]
+name = "redox_syscall"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
 name = "redox_users"
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3436,9 +3438,9 @@
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.23"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-core",
@@ -3999,7 +4001,7 @@
  "fluent-syntax",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "unic-langid",
 ]
 
@@ -4133,7 +4135,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "synstructure",
 ]
 
@@ -4280,7 +4282,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "synstructure",
 ]
 
@@ -4932,7 +4934,7 @@
  "proc-macro2",
  "quote",
  "serde",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -4967,9 +4969,9 @@
 
 [[package]]
 name = "rustix"
-version = "0.38.32"
+version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
  "bitflags 2.5.0",
  "errno",
@@ -4989,9 +4991,9 @@
 
 [[package]]
 name = "rustversion"
-version = "1.0.15"
+version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
+checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
 
 [[package]]
 name = "ruzstd"
@@ -5017,9 +5019,9 @@
 
 [[package]]
 name = "ryu"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "same-file"
@@ -5053,11 +5055,11 @@
 
 [[package]]
 name = "security-framework"
-version = "2.10.0"
+version = "2.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
+checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.5.0",
  "core-foundation",
  "core-foundation-sys",
  "libc",
@@ -5066,9 +5068,9 @@
 
 [[package]]
 name = "security-framework-sys"
-version = "2.10.0"
+version = "2.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
+checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -5080,49 +5082,49 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
 dependencies = [
- "self_cell 1.0.3",
+ "self_cell 1.0.4",
 ]
 
 [[package]]
 name = "self_cell"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
+checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
 
 [[package]]
 name = "semver"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.197"
+version = "1.0.201"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.197"
+version = "1.0.201"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.115"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
 dependencies = [
  "indexmap",
  "itoa",
@@ -5229,9 +5231,9 @@
 
 [[package]]
 name = "socket2"
-version = "0.5.6"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
@@ -5427,9 +5429,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.58"
+version = "2.0.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
+checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5450,14 +5452,14 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
 name = "sysinfo"
-version = "0.30.8"
+version = "0.30.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b1a378e48fb3ce3a5cf04359c456c9c98ff689bcf1c1bc6e6a31f247686f275"
+checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
 dependencies = [
  "cfg-if",
  "core-foundation-sys",
@@ -5601,22 +5603,22 @@
 
 [[package]]
 name = "thiserror"
-version = "1.0.58"
+version = "1.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.58"
+version = "1.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -5751,16 +5753,15 @@
 
 [[package]]
 name = "tokio-util"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
 dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
  "pin-project-lite",
  "tokio",
- "tracing",
 ]
 
 [[package]]
@@ -5838,7 +5839,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -5922,9 +5923,9 @@
 
 [[package]]
 name = "type-map"
-version = "0.4.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
+checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
 dependencies = [
  "rustc-hash",
 ]
@@ -6007,9 +6008,9 @@
 
 [[package]]
 name = "unic-langid"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516"
+checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44"
 dependencies = [
  "unic-langid-impl",
  "unic-langid-macros",
@@ -6017,18 +6018,18 @@
 
 [[package]]
 name = "unic-langid-impl"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bd55a2063fdea4ef1f8633243a7b0524cbeef1905ae04c31a1c9b9775c55bc6"
+checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5"
 dependencies = [
  "tinystr",
 ]
 
 [[package]]
 name = "unic-langid-macros"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c854cefb82ff2816410ce606acbad1b3af065140907b29be9229040752b83ec"
+checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e"
 dependencies = [
  "proc-macro-hack",
  "tinystr",
@@ -6038,13 +6039,13 @@
 
 [[package]]
 name = "unic-langid-macros-impl"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fea2a4c80deb4fb3ca51f66b5e2dd91e3642bbce52234bcf22e41668281208e4"
+checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b"
 dependencies = [
  "proc-macro-hack",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "unic-langid-impl",
 ]
 
@@ -6115,9 +6116,9 @@
 
 [[package]]
 name = "unicode-width"
-version = "0.1.11"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-core",
@@ -6276,7 +6277,7 @@
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "wasm-bindgen-shared",
 ]
 
@@ -6310,7 +6311,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -6368,11 +6369,11 @@
 
 [[package]]
 name = "winapi-util"
-version = "0.1.6"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
 dependencies = [
- "winapi",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -6388,7 +6389,7 @@
 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
 dependencies = [
  "windows-core",
- "windows-targets 0.52.4",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -6401,7 +6402,7 @@
  "rayon",
  "serde",
  "serde_json",
- "syn 2.0.58",
+ "syn 2.0.62",
  "windows-metadata",
 ]
 
@@ -6411,7 +6412,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.4",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -6435,7 +6436,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.4",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -6455,17 +6456,18 @@
 
 [[package]]
 name = "windows-targets"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.4",
- "windows_aarch64_msvc 0.52.4",
- "windows_i686_gnu 0.52.4",
- "windows_i686_msvc 0.52.4",
- "windows_x86_64_gnu 0.52.4",
- "windows_x86_64_gnullvm 0.52.4",
- "windows_x86_64_msvc 0.52.4",
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
 ]
 
 [[package]]
@@ -6476,9 +6478,9 @@
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -6488,9 +6490,9 @@
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -6500,9 +6502,15 @@
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -6512,9 +6520,9 @@
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -6524,9 +6532,9 @@
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
@@ -6536,9 +6544,9 @@
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -6548,9 +6556,9 @@
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.4"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
 
 [[package]]
 name = "winnow"
@@ -6626,28 +6634,28 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "synstructure",
 ]
 
 [[package]]
 name = "zerocopy"
-version = "0.7.32"
+version = "0.7.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.32"
+version = "0.7.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
@@ -6667,7 +6675,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
  "synstructure",
 ]
 
@@ -6690,7 +6698,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.58",
+ "syn 2.0.62",
 ]
 
 [[package]]
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index b1a0aa6..259336f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -78,7 +78,7 @@
 #[derive(Copy, Clone)]
 struct PatInfo<'tcx, 'a> {
     binding_mode: ByRef,
-    max_ref_mutbl: Mutability,
+    max_ref_mutbl: MutblCap,
     top_info: TopInfo<'tcx>,
     decl_origin: Option<DeclOrigin<'a>>,
 
@@ -127,22 +127,61 @@
 }
 
 /// Mode for adjusting the expected type and binding mode.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 enum AdjustMode {
     /// Peel off all immediate reference types.
     Peel,
     /// Reset binding mode to the initial mode.
     /// Used for destructuring assignment, where we don't want any match ergonomics.
     Reset,
-    /// Produced by ref patterns.
-    /// Reset the binding mode to the initial mode,
-    /// and if the old biding mode was by-reference
-    /// with mutability matching the pattern,
-    /// mark the pattern as having consumed this reference.
-    ResetAndConsumeRef(Mutability),
     /// Pass on the input binding mode and expected type.
     Pass,
 }
 
+/// `ref mut` patterns (explicit or match-ergonomics)
+/// are not allowed behind an `&` reference.
+///
+/// This includes explicit `ref mut` behind `&` patterns
+/// that match against `&mut` references,
+/// where the code would have compiled
+/// had the pattern been written as `&mut`.
+/// However, the borrow checker will not catch
+/// this last case, so we need to throw an error ourselves.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum MutblCap {
+    /// Mutability restricted to immutable.
+    Not,
+
+    /// Mutability restricted to immutable, but only because of the pattern
+    /// (not the scrutinee type).
+    ///
+    /// The contained span, if present, points to an `&` pattern
+    /// that is the reason for the restriction,
+    /// and which will be reported in a diagnostic.
+    WeaklyNot(Option<Span>),
+
+    /// No restriction on mutability
+    Mut,
+}
+
+impl MutblCap {
+    #[must_use]
+    fn cap_to_weakly_not(self, span: Option<Span>) -> Self {
+        match self {
+            MutblCap::Not => MutblCap::Not,
+            _ => MutblCap::WeaklyNot(span),
+        }
+    }
+
+    #[must_use]
+    fn as_mutbl(self) -> Mutability {
+        match self {
+            MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not,
+            MutblCap::Mut => Mutability::Mut,
+        }
+    }
+}
+
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Type check the given top level pattern against the `expected` type.
     ///
@@ -163,7 +202,7 @@
         let info = TopInfo { expected, origin_expr, span };
         let pat_info = PatInfo {
             binding_mode: ByRef::No,
-            max_ref_mutbl: Mutability::Mut,
+            max_ref_mutbl: MutblCap::Mut,
             top_info: info,
             decl_origin,
             current_depth: 0,
@@ -172,14 +211,13 @@
     }
 
     /// Type check the given `pat` against the `expected` type
-    /// with the provided `def_bm` (default binding mode).
+    /// with the provided `binding_mode` (default binding mode).
     ///
     /// Outside of this module, `check_pat_top` should always be used.
     /// Conversely, inside this module, `check_pat_top` should never be used.
     #[instrument(level = "debug", skip(self, pat_info))]
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
-        let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } =
-            pat_info;
+        let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
 
         let path_res = match &pat.kind {
             PatKind::Path(qpath) => Some(
@@ -188,10 +226,10 @@
             _ => None,
         };
         let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
-        let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) =
-            self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl);
+        let (expected, binding_mode, max_ref_mutbl) =
+            self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
         let pat_info = PatInfo {
-            binding_mode: def_bm,
+            binding_mode,
             max_ref_mutbl,
             top_info: ti,
             decl_origin: pat_info.decl_origin,
@@ -204,8 +242,8 @@
             PatKind::Never => expected,
             PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
-            PatKind::Binding(ba, var_id, _, sub) => {
-                self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info)
+            PatKind::Binding(ba, var_id, ident, sub) => {
+                self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
             }
             PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
                 self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
@@ -227,14 +265,7 @@
             }
             PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
             PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
-            PatKind::Ref(inner, mutbl) => self.check_pat_ref(
-                pat,
-                inner,
-                mutbl,
-                expected,
-                pat_info,
-                ref_pattern_already_consumed,
-            ),
+            PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
             PatKind::Slice(before, slice, after) => {
                 self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
             }
@@ -287,52 +318,22 @@
 
     /// Compute the new expected type and default binding mode from the old ones
     /// as well as the pattern form we are currently checking.
-    ///
-    /// Last entry is only relevant for ref patterns (`&` and `&mut`);
-    /// if `true`, then the ref pattern consumed a match ergonomics inserted reference
-    /// and so does no need to match against a reference in the scrutinee type.
     fn calc_default_binding_mode(
         &self,
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         def_br: ByRef,
         adjust_mode: AdjustMode,
-        max_ref_mutbl: Mutability,
-    ) -> (Ty<'tcx>, ByRef, Mutability, bool) {
-        if let ByRef::Yes(mutbl) = def_br {
-            debug_assert!(mutbl <= max_ref_mutbl);
+        max_ref_mutbl: MutblCap,
+    ) -> (Ty<'tcx>, ByRef, MutblCap) {
+        if let ByRef::Yes(Mutability::Mut) = def_br {
+            debug_assert!(max_ref_mutbl == MutblCap::Mut);
         }
         match adjust_mode {
-            AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false),
-            AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false),
-            AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => {
-                let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl);
-                if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
-                    if mutbls_match {
-                        debug!("consuming inherited reference");
-                        (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true)
-                    } else {
-                        let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
-                            self.peel_off_references(
-                                pat,
-                                expected,
-                                def_br,
-                                Mutability::Not,
-                                max_ref_mutbl,
-                            )
-                        } else {
-                            (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not)
-                        };
-                        (new_ty, new_bm, max_ref_mutbl, false)
-                    }
-                } else {
-                    (expected, ByRef::No, max_ref_mutbl, mutbls_match)
-                }
-            }
+            AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
+            AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
             AdjustMode::Peel => {
-                let peeled =
-                    self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl);
-                (peeled.0, peeled.1, peeled.2, false)
+                self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl)
             }
         }
     }
@@ -378,17 +379,8 @@
                 // a reference type wherefore peeling doesn't give up any expressiveness.
                 _ => AdjustMode::Peel,
             },
-            // When encountering a `& mut? pat` pattern, reset to "by value".
-            // This is so that `x` and `y` here are by value, as they appear to be:
-            //
-            // ```
-            // match &(&22, &44) {
-            //   (&x, &y) => ...
-            // }
-            // ```
-            //
-            // See issue #46688.
-            PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl),
+            // Ref patterns are complicated, we handle them in `check_pat_ref`.
+            PatKind::Ref(..) => AdjustMode::Pass,
             // A `_` pattern works with any expected type, so there's no need to do anything.
             PatKind::Wild
             // A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -414,8 +406,8 @@
         expected: Ty<'tcx>,
         mut def_br: ByRef,
         max_peelable_mutability: Mutability,
-        mut max_ref_mutability: Mutability,
-    ) -> (Ty<'tcx>, ByRef, Mutability) {
+        mut max_ref_mutability: MutblCap,
+    ) -> (Ty<'tcx>, ByRef, MutblCap) {
         let mut expected = self.try_structurally_resolve_type(pat.span, expected);
         // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
         // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -449,9 +441,9 @@
         }
 
         if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
-            def_br = def_br.cap_ref_mutability(max_ref_mutability);
+            def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl());
             if def_br == ByRef::Yes(Mutability::Not) {
-                max_ref_mutability = Mutability::Not;
+                max_ref_mutability = MutblCap::Not;
             }
         }
 
@@ -668,8 +660,9 @@
     fn check_pat_ident(
         &self,
         pat: &'tcx Pat<'tcx>,
-        ba: BindingMode,
+        user_bind_annot: BindingMode,
         var_id: HirId,
+        ident: Ident,
         sub: Option<&'tcx Pat<'tcx>>,
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx, '_>,
@@ -677,7 +670,7 @@
         let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info;
 
         // Determine the binding mode...
-        let bm = match ba {
+        let bm = match user_bind_annot {
             BindingMode(ByRef::No, Mutability::Mut)
                 if !(pat.span.at_least_rust_2024()
                     && self.tcx.features().mut_preserve_binding_mode_2024)
@@ -693,8 +686,30 @@
                 BindingMode(ByRef::No, Mutability::Mut)
             }
             BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
-            BindingMode(ByRef::Yes(_), _) => ba,
+            BindingMode(ByRef::Yes(_), _) => user_bind_annot,
         };
+
+        if bm.0 == ByRef::Yes(Mutability::Mut)
+            && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl
+        {
+            let mut err = struct_span_code_err!(
+                self.tcx.dcx(),
+                ident.span,
+                E0596,
+                "cannot borrow as mutable inside an `&` pattern"
+            );
+
+            if let Some(span) = and_pat_span {
+                err.span_suggestion(
+                    span,
+                    "replace this `&` with `&mut`",
+                    "&mut ",
+                    Applicability::MachineApplicable,
+                );
+            }
+            err.emit();
+        }
+
         // ...and store it in a side table:
         self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
 
@@ -720,7 +735,7 @@
         // If there are multiple arms, make sure they all agree on
         // what the type of the binding `x` ought to be.
         if var_id != pat.hir_id {
-            self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti);
+            self.check_binding_alt_eq_ty(user_bind_annot, pat.span, var_id, local_ty, ti);
         }
 
         if let Some(p) = sub {
@@ -2105,72 +2120,136 @@
         &self,
         pat: &'tcx Pat<'tcx>,
         inner: &'tcx Pat<'tcx>,
-        mutbl: Mutability,
-        expected: Ty<'tcx>,
-        pat_info: PatInfo<'tcx, '_>,
-        consumed_inherited_ref: bool,
+        pat_mutbl: Mutability,
+        mut expected: Ty<'tcx>,
+        mut pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
-        if consumed_inherited_ref
-            && pat.span.at_least_rust_2024()
-            && self.tcx.features().ref_pat_eat_one_layer_2024
-        {
-            self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
-            self.check_pat(inner, expected, pat_info);
-            expected
+        // FIXME: repace with `bool` once final decision on 1 vs 2 layers is made
+        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+        enum MatchErgonomicsMode {
+            EatOneLayer,
+            EatTwoLayers,
+            Legacy,
+        }
+
+        let match_ergonomics_mode =
+            if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
+                MatchErgonomicsMode::EatOneLayer
+            } else if self.tcx.features().ref_pat_everywhere {
+                MatchErgonomicsMode::EatTwoLayers
+            } else {
+                MatchErgonomicsMode::Legacy
+            };
+
+        let mut inherited_ref_mutbl_match = false;
+        if match_ergonomics_mode != MatchErgonomicsMode::Legacy {
+            if pat_mutbl == Mutability::Not {
+                // Prevent the inner pattern from binding with `ref mut`.
+                pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(
+                    inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)),
+                );
+            }
+
+            if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
+                inherited_ref_mutbl_match = pat_mutbl <= inh_mut;
+            }
+
+            if inherited_ref_mutbl_match {
+                pat_info.binding_mode = ByRef::No;
+                if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer {
+                    self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
+                    self.check_pat(inner, expected, pat_info);
+                    return expected;
+                }
+            } else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer
+                && pat_mutbl == Mutability::Mut
+            {
+                // `&mut` patterns pell off `&` references
+                let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references(
+                    pat,
+                    expected,
+                    pat_info.binding_mode,
+                    Mutability::Not,
+                    pat_info.max_ref_mutbl,
+                );
+                expected = new_expected;
+                pat_info.binding_mode = new_bm;
+                pat_info.max_ref_mutbl = max_ref_mutbl;
+            }
         } else {
-            let tcx = self.tcx;
-            let expected = self.shallow_resolve(expected);
-            let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
-                Ok(()) => {
-                    // `demand::subtype` would be good enough, but using `eqtype` turns
-                    // out to be equally general. See (note_1) for details.
+            // Reset binding mode on old editions
+            pat_info.binding_mode = ByRef::No;
+            pat_info.max_ref_mutbl = MutblCap::Mut
+        }
 
-                    // Take region, inner-type from expected type if we can,
-                    // to avoid creating needless variables. This also helps with
-                    // the bad interactions of the given hack detailed in (note_1).
-                    debug!("check_pat_ref: expected={:?}", expected);
-                    match *expected.kind() {
-                        ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty),
-                        _ => {
-                            if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere {
-                                // We already matched against a match-ergonmics inserted reference,
-                                // so we don't need to match against a reference from the original type.
-                                // Save this infor for use in lowering later
-                                self.typeck_results
-                                    .borrow_mut()
-                                    .skipped_ref_pats_mut()
-                                    .insert(pat.hir_id);
-                                (expected, expected)
-                            } else {
-                                let inner_ty = self.next_ty_var(inner.span);
-                                let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
-                                debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
-                                let err = self.demand_eqtype_pat_diag(
-                                    pat.span,
-                                    expected,
-                                    ref_ty,
-                                    pat_info.top_info,
-                                );
+        let tcx = self.tcx;
+        expected = self.try_structurally_resolve_type(pat.span, expected);
+        let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
+            Ok(()) => {
+                // `demand::subtype` would be good enough, but using `eqtype` turns
+                // out to be equally general. See (note_1) for details.
 
-                                // Look for a case like `fn foo(&foo: u32)` and suggest
-                                // `fn foo(foo: &u32)`
-                                if let Some(mut err) = err {
-                                    self.borrow_pat_suggestion(&mut err, pat);
-                                    err.emit();
-                                }
-                                (ref_ty, inner_ty)
-                            }
+                // Take region, inner-type from expected type if we can,
+                // to avoid creating needless variables. This also helps with
+                // the bad interactions of the given hack detailed in (note_1).
+                debug!("check_pat_ref: expected={:?}", expected);
+                match *expected.kind() {
+                    ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => {
+                        if r_mutbl == Mutability::Not
+                            && match_ergonomics_mode != MatchErgonomicsMode::Legacy
+                        {
+                            pat_info.max_ref_mutbl = MutblCap::Not;
                         }
+
+                        (expected, r_ty)
+                    }
+
+                    // `&` pattern eats `&mut` reference
+                    ty::Ref(_, r_ty, Mutability::Mut)
+                        if pat_mutbl == Mutability::Not
+                            && match_ergonomics_mode != MatchErgonomicsMode::Legacy =>
+                    {
+                        (expected, r_ty)
+                    }
+
+                    _ if inherited_ref_mutbl_match
+                        && match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers =>
+                    {
+                        // We already matched against a match-ergonmics inserted reference,
+                        // so we don't need to match against a reference from the original type.
+                        // Save this info for use in lowering later
+                        self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
+                        (expected, expected)
+                    }
+
+                    _ => {
+                        let inner_ty = self.next_ty_var(inner.span);
+                        let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty);
+                        debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
+                        let err = self.demand_eqtype_pat_diag(
+                            pat.span,
+                            expected,
+                            ref_ty,
+                            pat_info.top_info,
+                        );
+
+                        // Look for a case like `fn foo(&foo: u32)` and suggest
+                        // `fn foo(foo: &u32)`
+                        if let Some(mut err) = err {
+                            self.borrow_pat_suggestion(&mut err, pat);
+                            err.emit();
+                        }
+                        (ref_ty, inner_ty)
                     }
                 }
-                Err(guar) => {
-                    let err = Ty::new_error(tcx, guar);
-                    (err, err)
-                }
-            };
-            self.check_pat(inner, inner_ty, pat_info);
-            ref_ty
-        }
+            }
+            Err(guar) => {
+                let err = Ty::new_error(tcx, guar);
+                (err, err)
+            }
+        };
+        self.check_pat(inner, inner_ty, pat_info);
+        ref_ty
     }
 
     /// Create a reference type with a fresh region variable.
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index b80e90c..34153e3 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -202,8 +202,10 @@
 
     // if the current expr looks like this `&mut expr[index]` then just looking
     // at `expr[index]` won't give us the underlying allocation, so we just skip it
-    // the same logic applies field access like `&mut expr.field`
-    if let ExprKind::Index(..) | ExprKind::Field(..) = e_alloc.kind {
+    // the same logic applies field access `&mut expr.field` and reborrows `&mut *expr`.
+    if let ExprKind::Index(..) | ExprKind::Field(..) | ExprKind::Unary(UnOp::Deref, ..) =
+        e_alloc.kind
+    {
         return None;
     }
 
diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
index d667bad..c5b2065 100644
--- a/compiler/rustc_target/src/spec/base/apple/mod.rs
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs
@@ -17,14 +17,9 @@
     Arm64e,
     Arm64_32,
     I386,
-    I386_sim,
     I686,
     X86_64,
     X86_64h,
-    X86_64_sim,
-    X86_64_macabi,
-    Arm64_macabi,
-    Arm64_sim,
 }
 
 impl Arch {
@@ -32,12 +27,12 @@
         match self {
             Armv7k => "armv7k",
             Armv7s => "armv7s",
-            Arm64 | Arm64_macabi | Arm64_sim => "arm64",
+            Arm64 => "arm64",
             Arm64e => "arm64e",
             Arm64_32 => "arm64_32",
-            I386 | I386_sim => "i386",
+            I386 => "i386",
             I686 => "i686",
-            X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
+            X86_64 => "x86_64",
             X86_64h => "x86_64h",
         }
     }
@@ -45,61 +40,70 @@
     pub fn target_arch(self) -> Cow<'static, str> {
         Cow::Borrowed(match self {
             Armv7k | Armv7s => "arm",
-            Arm64 | Arm64e | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64",
-            I386 | I386_sim | I686 => "x86",
-            X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64",
+            Arm64 | Arm64e | Arm64_32 => "aarch64",
+            I386 | I686 => "x86",
+            X86_64 | X86_64h => "x86_64",
         })
     }
 
-    fn target_abi(self) -> &'static str {
-        match self {
-            Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "",
-            X86_64_macabi | Arm64_macabi => "macabi",
-            I386_sim | Arm64_sim | X86_64_sim => "sim",
-        }
-    }
-
-    fn target_cpu(self) -> &'static str {
+    fn target_cpu(self, abi: TargetAbi) -> &'static str {
         match self {
             Armv7k => "cortex-a8",
             Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher.
-            Arm64 => "apple-a7",
+            Arm64 => match abi {
+                TargetAbi::Normal => "apple-a7",
+                TargetAbi::Simulator => "apple-a12",
+                TargetAbi::MacCatalyst => "apple-a12",
+            },
             Arm64e => "apple-a12",
             Arm64_32 => "apple-s4",
             // Only macOS 10.12+ is supported, which means
             // all x86_64/x86 CPUs must be running at least penryn
             // https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
-            I386 | I386_sim | I686 => "penryn",
-            X86_64 | X86_64_sim => "penryn",
-            X86_64_macabi => "penryn",
+            I386 | I686 => "penryn",
+            X86_64 => "penryn",
             // Note: `core-avx2` is slightly more advanced than `x86_64h`, see
             // comments (and disabled features) in `x86_64h_apple_darwin` for
             // details. It is a higher baseline then `penryn` however.
             X86_64h => "core-avx2",
-            Arm64_macabi => "apple-a12",
-            Arm64_sim => "apple-a12",
         }
     }
 
     fn stack_probes(self) -> StackProbeType {
         match self {
             Armv7k | Armv7s => StackProbeType::None,
-            Arm64 | Arm64e | Arm64_32 | I386 | I386_sim | I686 | X86_64 | X86_64h | X86_64_sim
-            | X86_64_macabi | Arm64_macabi | Arm64_sim => StackProbeType::Inline,
+            Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => StackProbeType::Inline,
         }
     }
 }
 
-fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
+#[derive(Copy, Clone, PartialEq)]
+pub enum TargetAbi {
+    Normal,
+    Simulator,
+    MacCatalyst,
+}
+
+impl TargetAbi {
+    fn target_abi(self) -> &'static str {
+        match self {
+            Self::Normal => "",
+            Self::MacCatalyst => "macabi",
+            Self::Simulator => "sim",
+        }
+    }
+}
+
+fn pre_link_args(os: &'static str, arch: Arch, abi: TargetAbi) -> LinkArgs {
     let platform_name: StaticCow<str> = match abi {
-        "sim" => format!("{os}-simulator").into(),
-        "macabi" => "mac-catalyst".into(),
-        _ => os.into(),
+        TargetAbi::Normal => os.into(),
+        TargetAbi::Simulator => format!("{os}-simulator").into(),
+        TargetAbi::MacCatalyst => "mac-catalyst".into(),
     };
 
     let min_version: StaticCow<str> = {
         let (major, minor) = match os {
-            "ios" => ios_deployment_target(arch, abi),
+            "ios" => ios_deployment_target(arch, abi.target_abi()),
             "tvos" => tvos_deployment_target(),
             "watchos" => watchos_deployment_target(),
             "visionos" => visionos_deployment_target(),
@@ -119,7 +123,7 @@
         LinkerFlavor::Darwin(Cc::No, Lld::No),
         [platform_name, min_version, sdk_version].into_iter(),
     );
-    if abi != "macabi" {
+    if abi != TargetAbi::MacCatalyst {
         add_link_args(
             &mut args,
             LinkerFlavor::Darwin(Cc::Yes, Lld::No),
@@ -136,13 +140,11 @@
     args
 }
 
-pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
-    let abi = arch.target_abi();
-
+pub fn opts(os: &'static str, arch: Arch, abi: TargetAbi) -> TargetOptions {
     TargetOptions {
-        abi: abi.into(),
+        abi: abi.target_abi().into(),
         os: os.into(),
-        cpu: arch.target_cpu().into(),
+        cpu: arch.target_cpu(abi).into(),
         link_env_remove: link_env_remove(os),
         vendor: "apple".into(),
         linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
@@ -263,8 +265,7 @@
 
 fn macos_default_deployment_target(arch: Arch) -> (u32, u32) {
     match arch {
-        // Note: Arm64_sim is not included since macOS has no simulator.
-        Arm64 | Arm64e | Arm64_macabi => (11, 0),
+        Arm64 | Arm64e => (11, 0),
         _ => (10, 12),
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
index c59c0ee..4e29641 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{macos_llvm_target, opts, Arch};
+use crate::spec::base::apple::{macos_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64;
-    let mut base = opts("macos", arch);
+    let mut base = opts("macos", arch, TargetAbi::Normal);
     base.cpu = "apple-m1".into();
     base.max_atomic_width = Some(128);
 
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
index 4bbd962..2065568 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{ios_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64;
-    let mut base = opts("ios", arch);
+    let mut base = opts("ios", arch, TargetAbi::Normal);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
index af413e8..4c008f7 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch};
+use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::Arm64_macabi;
-    let mut base = opts("ios", arch);
+    let arch = Arch::Arm64;
+    let mut base = opts("ios", arch, TargetAbi::MacCatalyst);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
index 2c1dd41..4a63abd 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::Arm64_sim;
-    let mut base = opts("ios", arch);
+    let arch = Arch::Arm64;
+    let mut base = opts("ios", arch, TargetAbi::Simulator);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
index b5623c8..3310e6c 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
@@ -1,4 +1,4 @@
-use crate::spec::base::apple::{opts, tvos_llvm_target, Arch};
+use crate::spec::base::apple::{opts, tvos_llvm_target, Arch, TargetAbi};
 use crate::spec::{FramePointer, Target, TargetOptions};
 
 pub fn target() -> Target {
@@ -18,7 +18,7 @@
             features: "+neon,+fp-armv8,+apple-a7".into(),
             max_atomic_width: Some(128),
             frame_pointer: FramePointer::NonLeaf,
-            ..opts("tvos", arch)
+            ..opts("tvos", arch, TargetAbi::Normal)
         },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
index cf4bbb1..b901c66 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
@@ -1,8 +1,8 @@
-use crate::spec::base::apple::{opts, tvos_sim_llvm_target, Arch};
+use crate::spec::base::apple::{opts, tvos_sim_llvm_target, Arch, TargetAbi};
 use crate::spec::{FramePointer, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::Arm64_sim;
+    let arch = Arch::Arm64;
     Target {
         llvm_target: tvos_sim_llvm_target(arch).into(),
         metadata: crate::spec::TargetMetadata {
@@ -18,7 +18,7 @@
             features: "+neon,+fp-armv8,+apple-a7".into(),
             max_atomic_width: Some(128),
             frame_pointer: FramePointer::NonLeaf,
-            ..opts("tvos", arch)
+            ..opts("tvos", arch, TargetAbi::Simulator)
         },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
index bacc467..b0798e5 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{opts, visionos_llvm_target, Arch};
+use crate::spec::base::apple::{opts, visionos_llvm_target, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64;
-    let mut base = opts("visionos", arch);
+    let mut base = opts("visionos", arch, TargetAbi::Normal);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
index 99df73c..7b2d2b6 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{opts, visionos_sim_llvm_target, Arch};
+use crate::spec::base::apple::{opts, visionos_sim_llvm_target, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::Arm64_sim;
-    let mut base = opts("visionos", arch);
+    let arch = Arch::Arm64;
+    let mut base = opts("visionos", arch, TargetAbi::Simulator);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
index 3fbb780..a00a97a 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
@@ -1,8 +1,8 @@
-use crate::spec::base::apple::{opts, Arch};
+use crate::spec::base::apple::{opts, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
-    let base = opts("watchos", Arch::Arm64);
+    let base = opts("watchos", Arch::Arm64, TargetAbi::Normal);
     Target {
         llvm_target: "aarch64-apple-watchos".into(),
         metadata: crate::spec::TargetMetadata {
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
index c13615b..e2f80b7 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
@@ -1,8 +1,8 @@
-use crate::spec::base::apple::{opts, watchos_sim_llvm_target, Arch};
+use crate::spec::base::apple::{opts, watchos_sim_llvm_target, Arch, TargetAbi};
 use crate::spec::{FramePointer, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::Arm64_sim;
+    let arch = Arch::Arm64;
     Target {
         // Clang automatically chooses a more specific target based on
         // WATCHOS_DEPLOYMENT_TARGET.
@@ -22,7 +22,7 @@
             features: "+neon,+fp-armv8,+apple-a7".into(),
             max_atomic_width: Some(128),
             frame_pointer: FramePointer::NonLeaf,
-            ..opts("watchos", arch)
+            ..opts("watchos", arch, TargetAbi::Simulator)
         },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
index 38166a8..3ca8c99 100644
--- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{opts, watchos_llvm_target, Arch};
+use crate::spec::base::apple::{opts, watchos_llvm_target, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64_32;
-    let base = opts("watchos", arch);
+    let base = opts("watchos", arch, TargetAbi::Normal);
     Target {
         llvm_target: watchos_llvm_target(arch).into(),
         metadata: crate::spec::TargetMetadata {
diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
index cf69309..90be518 100644
--- a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{macos_llvm_target, opts, Arch};
+use crate::spec::base::apple::{macos_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64e;
-    let mut base = opts("macos", arch);
+    let mut base = opts("macos", arch, TargetAbi::Normal);
     base.cpu = "apple-m1".into();
     base.max_atomic_width = Some(128);
 
diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
index bff4074..56470d2 100644
--- a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{ios_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64e;
-    let mut base = opts("ios", arch);
+    let mut base = opts("ios", arch, TargetAbi::Normal);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs
index 42ad9e0..5c675c2 100644
--- a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs
+++ b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs
@@ -1,4 +1,4 @@
-use crate::spec::base::apple::{opts, Arch};
+use crate::spec::base::apple::{opts, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
@@ -19,7 +19,7 @@
             max_atomic_width: Some(64),
             dynamic_linking: false,
             position_independent_executables: true,
-            ..opts("watchos", arch)
+            ..opts("watchos", arch, TargetAbi::Normal)
         },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs
index 40e5fc3..4dd475e 100644
--- a/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs
@@ -1,4 +1,4 @@
-use crate::spec::base::apple::{ios_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
@@ -17,7 +17,7 @@
         options: TargetOptions {
             features: "+v7,+vfp4,+neon".into(),
             max_atomic_width: Some(64),
-            ..opts("ios", arch)
+            ..opts("ios", arch, TargetAbi::Normal)
         },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs
index afa92ba..c03a097 100644
--- a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs
@@ -1,10 +1,11 @@
-use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
+    let arch = Arch::I386;
     // i386-apple-ios is a simulator target, even though it isn't declared
     // that way in the target name like the other ones...
-    let arch = Arch::I386_sim;
+    let abi = TargetAbi::Simulator;
     Target {
         // Clang automatically chooses a more specific target based on
         // IPHONEOS_DEPLOYMENT_TARGET.
@@ -22,6 +23,6 @@
             i128:128-f64:32:64-f80:128-n8:16:32-S128"
             .into(),
         arch: arch.target_arch(),
-        options: TargetOptions { max_atomic_width: Some(64), ..opts("ios", arch) },
+        options: TargetOptions { max_atomic_width: Some(64), ..opts("ios", arch, abi) },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs
index 34a447a..aea6a1a 100644
--- a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs
@@ -1,10 +1,10 @@
-use crate::spec::base::apple::{macos_llvm_target, opts, Arch};
+use crate::spec::base::apple::{macos_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions};
 
 pub fn target() -> Target {
     // ld64 only understands i386 and not i686
     let arch = Arch::I386;
-    let mut base = opts("macos", arch);
+    let mut base = opts("macos", arch, TargetAbi::Normal);
     base.max_atomic_width = Some(64);
     base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m32"]);
     base.frame_pointer = FramePointer::Always;
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs
index 1716c59..21acd75 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs
@@ -1,10 +1,10 @@
-use crate::spec::base::apple::{macos_llvm_target, opts, Arch};
+use crate::spec::base::apple::{macos_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::X86_64;
-    let mut base = opts("macos", arch);
+    let mut base = opts("macos", arch, TargetAbi::Normal);
     base.max_atomic_width = Some(128); // penryn+ supports cmpxchg16b
     base.frame_pointer = FramePointer::Always;
     base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs
index fa22c29..ec61b79 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs
@@ -1,11 +1,11 @@
-use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch};
+use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
+    let arch = Arch::X86_64;
     // x86_64-apple-ios is a simulator target, even though it isn't declared
     // that way in the target name like the other ones...
-    let arch = Arch::X86_64_sim;
-    let mut base = opts("ios", arch);
+    let mut base = opts("ios", arch, TargetAbi::Simulator);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
index 9b479de..bd967ee 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
@@ -1,9 +1,9 @@
-use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch};
+use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::X86_64_macabi;
-    let mut base = opts("ios", arch);
+    let arch = Arch::X86_64;
+    let mut base = opts("ios", arch, TargetAbi::MacCatalyst);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs
index f62d31c..55b2e1a 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs
@@ -1,10 +1,11 @@
-use crate::spec::base::apple::{opts, tvos_sim_llvm_target, Arch};
+use crate::spec::base::apple::{opts, tvos_sim_llvm_target, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
+    let arch = Arch::X86_64;
     // x86_64-apple-tvos is a simulator target, even though it isn't declared
     // that way in the target name like the other ones...
-    let arch = Arch::X86_64_sim;
+    let abi = TargetAbi::Simulator;
     Target {
         llvm_target: tvos_sim_llvm_target(arch).into(),
         metadata: crate::spec::TargetMetadata {
@@ -17,6 +18,6 @@
         data_layout:
             "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),
         arch: arch.target_arch(),
-        options: TargetOptions { max_atomic_width: Some(128), ..opts("tvos", arch) },
+        options: TargetOptions { max_atomic_width: Some(128), ..opts("tvos", arch, abi) },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs
index 371aab8..a783eff 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs
@@ -1,8 +1,8 @@
-use crate::spec::base::apple::{opts, watchos_sim_llvm_target, Arch};
+use crate::spec::base::apple::{opts, watchos_sim_llvm_target, Arch, TargetAbi};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
-    let arch = Arch::X86_64_sim;
+    let arch = Arch::X86_64;
     Target {
         llvm_target: watchos_sim_llvm_target(arch).into(),
         metadata: crate::spec::TargetMetadata {
@@ -15,6 +15,9 @@
         data_layout:
             "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),
         arch: arch.target_arch(),
-        options: TargetOptions { max_atomic_width: Some(128), ..opts("watchos", arch) },
+        options: TargetOptions {
+            max_atomic_width: Some(128),
+            ..opts("watchos", arch, TargetAbi::Simulator)
+        },
     }
 }
diff --git a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs
index b17e21e..fe6cbca 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs
@@ -1,10 +1,10 @@
-use crate::spec::base::apple::{macos_llvm_target, opts, Arch};
+use crate::spec::base::apple::{macos_llvm_target, opts, Arch, TargetAbi};
 use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet};
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::X86_64h;
-    let mut base = opts("macos", arch);
+    let mut base = opts("macos", arch, TargetAbi::Normal);
     base.max_atomic_width = Some(128);
     base.frame_pointer = FramePointer::Always;
     base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index b991875..d7dc6ae 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -403,7 +403,7 @@
 //! is, a formatting implementation must and may only return an error if the
 //! passed-in [`Formatter`] returns an error. This is because, contrary to what
 //! the function signature might suggest, string formatting is an infallible
-//! operation. This function only returns a result because writing to the
+//! operation. This function only returns a [`Result`] because writing to the
 //! underlying stream might fail and it must provide a way to propagate the fact
 //! that an error has occurred back up the stack.
 //!
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index c245b42..45b2053 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -366,6 +366,12 @@
     }
 
     #[inline]
+    fn into_inner_with_allocator(this: Self) -> (NonNull<RcBox<T>>, A) {
+        let this = mem::ManuallyDrop::new(this);
+        (this.ptr, unsafe { ptr::read(&this.alloc) })
+    }
+
+    #[inline]
     unsafe fn from_inner_in(ptr: NonNull<RcBox<T>>, alloc: A) -> Self {
         Self { ptr, phantom: PhantomData, alloc }
     }
@@ -1145,12 +1151,9 @@
     /// ```
     #[unstable(feature = "new_uninit", issue = "63291")]
     #[inline]
-    pub unsafe fn assume_init(self) -> Rc<T, A>
-    where
-        A: Clone,
-    {
-        let md_self = mem::ManuallyDrop::new(self);
-        unsafe { Rc::from_inner_in(md_self.ptr.cast(), md_self.alloc.clone()) }
+    pub unsafe fn assume_init(self) -> Rc<T, A> {
+        let (ptr, alloc) = Rc::into_inner_with_allocator(self);
+        unsafe { Rc::from_inner_in(ptr.cast(), alloc) }
     }
 }
 
@@ -1189,12 +1192,9 @@
     /// ```
     #[unstable(feature = "new_uninit", issue = "63291")]
     #[inline]
-    pub unsafe fn assume_init(self) -> Rc<[T], A>
-    where
-        A: Clone,
-    {
-        let md_self = mem::ManuallyDrop::new(self);
-        unsafe { Rc::from_ptr_in(md_self.ptr.as_ptr() as _, md_self.alloc.clone()) }
+    pub unsafe fn assume_init(self) -> Rc<[T], A> {
+        let (ptr, alloc) = Rc::into_inner_with_allocator(self);
+        unsafe { Rc::from_ptr_in(ptr.as_ptr() as _, alloc) }
     }
 }
 
@@ -1809,7 +1809,9 @@
         // reference to the allocation.
         unsafe { &mut this.ptr.as_mut().value }
     }
+}
 
+impl<T: Clone, A: Allocator> Rc<T, A> {
     /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
     /// clone.
     ///
@@ -1845,7 +1847,7 @@
     }
 }
 
-impl<A: Allocator + Clone> Rc<dyn Any, A> {
+impl<A: Allocator> Rc<dyn Any, A> {
     /// Attempt to downcast the `Rc<dyn Any>` to a concrete type.
     ///
     /// # Examples
@@ -1869,10 +1871,8 @@
     pub fn downcast<T: Any>(self) -> Result<Rc<T, A>, Self> {
         if (*self).is::<T>() {
             unsafe {
-                let ptr = self.ptr.cast::<RcBox<T>>();
-                let alloc = self.alloc.clone();
-                forget(self);
-                Ok(Rc::from_inner_in(ptr, alloc))
+                let (ptr, alloc) = Rc::into_inner_with_allocator(self);
+                Ok(Rc::from_inner_in(ptr.cast(), alloc))
             }
         } else {
             Err(self)
@@ -1909,10 +1909,8 @@
     #[unstable(feature = "downcast_unchecked", issue = "90850")]
     pub unsafe fn downcast_unchecked<T: Any>(self) -> Rc<T, A> {
         unsafe {
-            let ptr = self.ptr.cast::<RcBox<T>>();
-            let alloc = self.alloc.clone();
-            mem::forget(self);
-            Rc::from_inner_in(ptr, alloc)
+            let (ptr, alloc) = Rc::into_inner_with_allocator(self);
+            Rc::from_inner_in(ptr.cast(), alloc)
         }
     }
 }
@@ -2661,12 +2659,13 @@
 }
 
 #[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
-impl<T, const N: usize> TryFrom<Rc<[T]>> for Rc<[T; N]> {
-    type Error = Rc<[T]>;
+impl<T, A: Allocator, const N: usize> TryFrom<Rc<[T], A>> for Rc<[T; N], A> {
+    type Error = Rc<[T], A>;
 
-    fn try_from(boxed_slice: Rc<[T]>) -> Result<Self, Self::Error> {
+    fn try_from(boxed_slice: Rc<[T], A>) -> Result<Self, Self::Error> {
         if boxed_slice.len() == N {
-            Ok(unsafe { Rc::from_raw(Rc::into_raw(boxed_slice) as *mut [T; N]) })
+            let (ptr, alloc) = Rc::into_inner_with_allocator(boxed_slice);
+            Ok(unsafe { Rc::from_inner_in(ptr.cast(), alloc) })
         } else {
             Err(boxed_slice)
         }
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 297a273..a35c998 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -280,8 +280,8 @@
 
 impl<T: ?Sized, A: Allocator> Arc<T, A> {
     #[inline]
-    fn internal_into_inner_with_allocator(self) -> (NonNull<ArcInner<T>>, A) {
-        let this = mem::ManuallyDrop::new(self);
+    fn into_inner_with_allocator(this: Self) -> (NonNull<ArcInner<T>>, A) {
+        let this = mem::ManuallyDrop::new(this);
         (this.ptr, unsafe { ptr::read(&this.alloc) })
     }
 
@@ -1290,7 +1290,7 @@
     #[must_use = "`self` will be dropped if the result is not used"]
     #[inline]
     pub unsafe fn assume_init(self) -> Arc<T, A> {
-        let (ptr, alloc) = self.internal_into_inner_with_allocator();
+        let (ptr, alloc) = Arc::into_inner_with_allocator(self);
         unsafe { Arc::from_inner_in(ptr.cast(), alloc) }
     }
 }
@@ -1332,7 +1332,7 @@
     #[must_use = "`self` will be dropped if the result is not used"]
     #[inline]
     pub unsafe fn assume_init(self) -> Arc<[T], A> {
-        let (ptr, alloc) = self.internal_into_inner_with_allocator();
+        let (ptr, alloc) = Arc::into_inner_with_allocator(self);
         unsafe { Arc::from_ptr_in(ptr.as_ptr() as _, alloc) }
     }
 }
@@ -2227,7 +2227,9 @@
         // either unique to begin with, or became one upon cloning the contents.
         unsafe { Self::get_mut_unchecked(this) }
     }
+}
 
+impl<T: Clone, A: Allocator> Arc<T, A> {
     /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
     /// clone.
     ///
@@ -2499,7 +2501,7 @@
     {
         if (*self).is::<T>() {
             unsafe {
-                let (ptr, alloc) = self.internal_into_inner_with_allocator();
+                let (ptr, alloc) = Arc::into_inner_with_allocator(self);
                 Ok(Arc::from_inner_in(ptr.cast(), alloc))
             }
         } else {
@@ -2540,7 +2542,7 @@
         T: Any + Send + Sync,
     {
         unsafe {
-            let (ptr, alloc) = self.internal_into_inner_with_allocator();
+            let (ptr, alloc) = Arc::into_inner_with_allocator(self);
             Arc::from_inner_in(ptr.cast(), alloc)
         }
     }
@@ -3506,7 +3508,7 @@
 
     fn try_from(boxed_slice: Arc<[T], A>) -> Result<Self, Self::Error> {
         if boxed_slice.len() == N {
-            let (ptr, alloc) = boxed_slice.internal_into_inner_with_allocator();
+            let (ptr, alloc) = Arc::into_inner_with_allocator(boxed_slice);
             Ok(unsafe { Arc::from_inner_in(ptr.cast(), alloc) })
         } else {
             Err(boxed_slice)
diff --git a/library/core/src/fmt/fmt_trait_method_doc.md b/library/core/src/fmt/fmt_trait_method_doc.md
new file mode 100644
index 0000000..493d929
--- /dev/null
+++ b/library/core/src/fmt/fmt_trait_method_doc.md
@@ -0,0 +1,8 @@
+Formats the value using the given formatter.
+
+# Errors
+
+This function should return [`Err`] if, and only if, the provided [`Formatter`] returns [`Err`].
+String formatting is considered an infallible operation; this function only
+returns a [`Result`] because writing to the underlying stream might fail and it must
+provide a way to propagate the fact that an error has occurred back up the stack.
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index ce0643a..9b372ea 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -72,14 +72,24 @@
 /// The error type which is returned from formatting a message into a stream.
 ///
 /// This type does not support transmission of an error other than that an error
-/// occurred. Any extra information must be arranged to be transmitted through
-/// some other means.
+/// occurred. This is because, despite the existence of this error,
+/// string formatting is considered an infallible operation.
+/// `fmt()` implementors should not return this `Error` unless they received it from their
+/// [`Formatter`]. The only time your code should create a new instance of this
+/// error is when implementing `fmt::Write`, in order to cancel the formatting operation when
+/// writing to the underlying stream fails.
 ///
-/// An important thing to remember is that the type `fmt::Error` should not be
+/// Any extra information must be arranged to be transmitted through some other means,
+/// such as storing it in a field to be consulted after the formatting operation has been
+/// cancelled. (For example, this is how [`std::io::Write::write_fmt()`] propagates IO errors
+/// during writing.)
+///
+/// This type, `fmt::Error`, should not be
 /// confused with [`std::io::Error`] or [`std::error::Error`], which you may also
 /// have in scope.
 ///
 /// [`std::io::Error`]: ../../std/io/struct.Error.html
+/// [`std::io::Write::write_fmt()`]: ../../std/io/trait.Write.html#method.write_fmt
 /// [`std::error::Error`]: ../../std/error/trait.Error.html
 ///
 /// # Examples
@@ -118,8 +128,10 @@
     /// This function will return an instance of [`std::fmt::Error`][Error] on error.
     ///
     /// The purpose of that error is to abort the formatting operation when the underlying
-    /// destination encounters some error preventing it from accepting more text; it should
-    /// generally be propagated rather than handled, at least when implementing formatting traits.
+    /// destination encounters some error preventing it from accepting more text;
+    /// in particular, it does not communicate any information about *what* error occurred.
+    /// It should generally be propagated rather than handled, at least when implementing
+    /// formatting traits.
     ///
     /// # Examples
     ///
@@ -586,7 +598,7 @@
 #[rustc_diagnostic_item = "Debug"]
 #[rustc_trivial_field_reads]
 pub trait Debug {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     ///
     /// # Examples
     ///
@@ -703,7 +715,7 @@
 #[rustc_diagnostic_item = "Display"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Display {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     ///
     /// # Examples
     ///
@@ -777,7 +789,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Octal {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -836,7 +848,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Binary {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -891,7 +903,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait LowerHex {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -946,7 +958,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait UpperHex {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -997,7 +1009,7 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_diagnostic_item = "Pointer"]
 pub trait Pointer {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -1048,7 +1060,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait LowerExp {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
@@ -1099,7 +1111,7 @@
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait UpperExp {
-    /// Formats the value using the given formatter.
+    #[doc = include_str!("fmt_trait_method_doc.md")]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index 5c4f0bf..8ad0452 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -114,18 +114,17 @@
     /// Returns a byte slice with leading ASCII whitespace bytes removed.
     ///
     /// 'Whitespace' refers to the definition used by
-    /// `u8::is_ascii_whitespace`.
+    /// [`u8::is_ascii_whitespace`].
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
     /// assert_eq!(b"  ".trim_ascii_start(), b"");
     /// assert_eq!(b"".trim_ascii_start(), b"");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii_start(&self) -> &[u8] {
         let mut bytes = self;
@@ -144,18 +143,17 @@
     /// Returns a byte slice with trailing ASCII whitespace bytes removed.
     ///
     /// 'Whitespace' refers to the definition used by
-    /// `u8::is_ascii_whitespace`.
+    /// [`u8::is_ascii_whitespace`].
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
     /// assert_eq!(b"  ".trim_ascii_end(), b"");
     /// assert_eq!(b"".trim_ascii_end(), b"");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii_end(&self) -> &[u8] {
         let mut bytes = self;
@@ -175,18 +173,17 @@
     /// removed.
     ///
     /// 'Whitespace' refers to the definition used by
-    /// `u8::is_ascii_whitespace`.
+    /// [`u8::is_ascii_whitespace`].
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
     /// assert_eq!(b"  ".trim_ascii(), b"");
     /// assert_eq!(b"".trim_ascii(), b"");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii(&self) -> &[u8] {
         self.trim_ascii_start().trim_ascii_end()
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index b6f6590..669cdc9 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -2531,15 +2531,14 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!(" \t \u{3000}hello world\n".trim_ascii_start(), "\u{3000}hello world\n");
     /// assert_eq!("  ".trim_ascii_start(), "");
     /// assert_eq!("".trim_ascii_start(), "");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
     #[must_use = "this returns the trimmed string as a new slice, \
                   without modifying the original"]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii_start(&self) -> &str {
         // SAFETY: Removing ASCII characters from a `&str` does not invalidate
@@ -2557,15 +2556,14 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!("\r hello world\u{3000}\n ".trim_ascii_end(), "\r hello world\u{3000}");
     /// assert_eq!("  ".trim_ascii_end(), "");
     /// assert_eq!("".trim_ascii_end(), "");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
     #[must_use = "this returns the trimmed string as a new slice, \
                   without modifying the original"]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii_end(&self) -> &str {
         // SAFETY: Removing ASCII characters from a `&str` does not invalidate
@@ -2584,15 +2582,14 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(byte_slice_trim_ascii)]
-    ///
     /// assert_eq!("\r hello world\n ".trim_ascii(), "hello world");
     /// assert_eq!("  ".trim_ascii(), "");
     /// assert_eq!("".trim_ascii(), "");
     /// ```
-    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
     #[must_use = "this returns the trimmed string as a new slice, \
                   without modifying the original"]
+    #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn trim_ascii(&self) -> &str {
         // SAFETY: Removing ASCII characters from a `&str` does not invalidate
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 52729ba..1720fe8 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -31,10 +31,11 @@
 addr2line = { version = "0.21.0", optional = true, default-features = false }
 
 [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
-libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
+libc = { version = "=0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
 
+# Pin libc (pending https://github.com/rust-lang/rust/pull/124560)
 [target.'cfg(all(windows, target_env = "msvc"))'.dependencies]
-libc = { version = "0.2.153", default-features = false }
+libc = { version = "=0.2.153", default-features = false }
 
 [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
 object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 77fa46d..045cde5 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -320,11 +320,13 @@
     (
         "tests",
         &[
+            // tidy-alphabetical-start
             "tests/assembly",
             "tests/codegen",
             "tests/codegen-units",
             "tests/coverage",
             "tests/coverage-run-rustdoc",
+            "tests/crashes",
             "tests/debuginfo",
             "tests/incremental",
             "tests/mir-opt",
@@ -340,6 +342,7 @@
             "tests/rustdoc-ui",
             "tests/ui",
             "tests/ui-fulldeps",
+            // tidy-alphabetical-end
         ],
     ),
 ];
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 9898d49..9710365 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -129,6 +129,26 @@
 }
 
 #[test]
+fn check_missing_paths_for_x_test_tests() {
+    let build = Build::new(configure("test", &["A-A"], &["A-A"]));
+
+    let (_, tests_remap_paths) =
+        PATH_REMAP.iter().find(|(target_path, _)| *target_path == "tests").unwrap();
+
+    let tests_dir = fs::read_dir(build.src.join("tests")).unwrap();
+    for dir in tests_dir {
+        let path = dir.unwrap().path();
+
+        // Skip if not a test directory.
+        if path.ends_with("tests/auxiliary") || !path.is_dir() {
+            continue
+        }
+
+        assert!(tests_remap_paths.iter().any(|item| path.ends_with(*item)), "{} is missing in PATH_REMAP tests list.", path.display());
+    }
+}
+
+#[test]
 fn test_exclude() {
     let mut config = configure("test", &["A-A"], &["A-A"]);
     config.skip = vec!["src/tools/tidy".into()];
diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile
index dab0667..e718437 100644
--- a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile
@@ -25,5 +25,5 @@
 
 ENV HOSTS=armv7-unknown-linux-gnueabihf
 
-ENV RUST_CONFIGURE_ARGS --enable-full-tools --disable-docs
+ENV RUST_CONFIGURE_ARGS --enable-full-tools --enable-profiler --disable-docs
 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 4eeaaa2..3a71dd8 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -8,6 +8,9 @@
 mod stripper;
 pub(crate) use stripper::*;
 
+mod strip_aliased_non_local;
+pub(crate) use self::strip_aliased_non_local::STRIP_ALIASED_NON_LOCAL;
+
 mod strip_hidden;
 pub(crate) use self::strip_hidden::STRIP_HIDDEN;
 
@@ -71,6 +74,7 @@
 pub(crate) const PASSES: &[Pass] = &[
     CHECK_CUSTOM_CODE_CLASSES,
     CHECK_DOC_TEST_VISIBILITY,
+    STRIP_ALIASED_NON_LOCAL,
     STRIP_HIDDEN,
     STRIP_PRIVATE,
     STRIP_PRIV_IMPORTS,
@@ -86,6 +90,7 @@
     ConditionalPass::always(CHECK_CUSTOM_CODE_CLASSES),
     ConditionalPass::always(COLLECT_TRAIT_IMPLS),
     ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
+    ConditionalPass::always(STRIP_ALIASED_NON_LOCAL),
     ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
     ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
     ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs
new file mode 100644
index 0000000..848cbd5
--- /dev/null
+++ b/src/librustdoc/passes/strip_aliased_non_local.rs
@@ -0,0 +1,57 @@
+use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::Visibility;
+
+use crate::clean;
+use crate::clean::Item;
+use crate::core::DocContext;
+use crate::fold::{strip_item, DocFolder};
+use crate::passes::Pass;
+
+pub(crate) const STRIP_ALIASED_NON_LOCAL: Pass = Pass {
+    name: "strip-aliased-non-local",
+    run: strip_aliased_non_local,
+    description: "strips all non-local private aliased items from the output",
+};
+
+fn strip_aliased_non_local(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
+    let mut stripper = AliasedNonLocalStripper { tcx: cx.tcx };
+    stripper.fold_crate(krate)
+}
+
+struct AliasedNonLocalStripper<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> DocFolder for AliasedNonLocalStripper<'tcx> {
+    fn fold_item(&mut self, i: Item) -> Option<Item> {
+        Some(match *i.kind {
+            clean::TypeAliasItem(..) => {
+                let mut stripper = NonLocalStripper { tcx: self.tcx };
+                // don't call `fold_item` as that could strip the type-alias it-self
+                // which we don't want to strip out
+                stripper.fold_item_recur(i)
+            }
+            _ => self.fold_item_recur(i),
+        })
+    }
+}
+
+struct NonLocalStripper<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> DocFolder for NonLocalStripper<'tcx> {
+    fn fold_item(&mut self, i: Item) -> Option<Item> {
+        // If not local, we want to respect the original visibility of
+        // the field and not the one given by the user for the currrent crate.
+        //
+        // FIXME(#125009): Not-local should probably consider same Cargo workspace
+        if !i.def_id().map_or(true, |did| did.is_local()) {
+            if i.visibility(self.tcx) != Some(Visibility::Public) || i.is_doc_hidden() {
+                return Some(strip_item(i));
+            }
+        }
+
+        Some(self.fold_item_recur(i))
+    }
+}
diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore
index 924a93e..97e006e 100644
--- a/src/tools/miri/.gitignore
+++ b/src/tools/miri/.gitignore
@@ -9,5 +9,5 @@
 perf.data
 perf.data.old
 flamegraph.svg
-tests/extern-so/libtestlib.so
+tests/native-lib/libtestlib.so
 .auto-*
diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md
index 60bc1d5..092ad46 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -72,14 +72,14 @@
 
 You can (cross-)run the entire test suite using:
 
-```
+```sh
 ./miri test
-MIRI_TEST_TARGET=i686-unknown-linux-gnu ./miri test
+./miri test --target i686-unknown-linux-gnu
 ```
 
 `./miri test FILTER` only runs those tests that contain `FILTER` in their filename (including the
-base directory, e.g. `./miri test fail` will run all compile-fail tests). These filters are passed
-to `cargo test`, so for multiple filters you need to use `./miri test -- FILTER1 FILTER2`.
+base directory, e.g. `./miri test fail` will run all compile-fail tests). Multiple filters are
+supported: `./miri test FILTER1 FILTER2` runs all tests that contain either string.
 
 #### Fine grained logging
 
@@ -139,9 +139,8 @@
 in the `miri` toolchain's sysroot to prevent conflicts with other toolchains.
 The Miri binaries in the `cargo` bin directory (usually `~/.cargo/bin`) are managed by rustup.
 
-There's a test for the cargo wrapper in the `test-cargo-miri` directory; run
-`./run-test.py` in there to execute it. Like `./miri test`, this respects the
-`MIRI_TEST_TARGET` environment variable to execute the test for another target.
+There's a test for the cargo wrapper in the `test-cargo-miri` directory; run `./run-test.py` in
+there to execute it. You can pass `--target` to execute the test for another target.
 
 ### Using a modified standard library
 
@@ -287,3 +286,41 @@
 [url "git@github.com:"]
     pushInsteadOf = https://github.com/
 ```
+
+## Internal environment variables
+
+The following environment variables are *internal* and must not be used by
+anyone but Miri itself. They are used to communicate between different Miri
+binaries, and as such worth documenting:
+
+* `CARGO_EXTRA_FLAGS` is understood by `./miri` and passed to all host cargo invocations.
+* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to
+  actually not interpret the code but compile it like rustc would. With `target`, Miri sets
+  some compiler flags to prepare the code for interpretation; with `host`, this is not done.
+  This environment variable is useful to be sure that the compiled `rlib`s are compatible
+  with Miri.
+* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build,
+  which will re-invoke `cargo-miri` as the `rustc` to use for this build.
+* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is
+  running as a child process of `rustdoc`, which invokes it twice for each doc-test
+  and requires special treatment, most notably a check-only build before interpretation.
+  This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper.
+* `MIRI_CWD` when set to any value tells the Miri driver to change to the given
+  directory after loading all the source files, but before commencing
+  interpretation. This is useful if the interpreted program wants a different
+  working directory at run-time than at build-time.
+* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which
+  crates should be given special treatment in diagnostics, in addition to the
+  crate currently being compiled.
+* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the
+  value of `RUSTDOC` from before it was overwritten.
+* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap
+  run the standard library tests in Miri.
+* `MIRI_TEST_TARGET` is set by `./miri test` (and `./x.py test miri`) to tell the test harness about
+  the chosen target.
+* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to
+  perform verbose logging.
+* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host*
+  operations.
+* `RUSTC_BLESS` is set by `./miri test` (and `./x.py test miri`) to indicate bless-mode to the test
+  harness.
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 5b3e2a5..208a8b9 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -1,39 +1,34 @@
 # Miri
 
-An experimental interpreter for [Rust][rust]'s
-[mid-level intermediate representation][mir] (MIR). It can run binaries and
-test suites of cargo projects and detect certain classes of
-[undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html),
-for example:
+Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test
+suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For
+instance:
 
 * Out-of-bounds memory accesses and use-after-free
 * Invalid use of uninitialized data
 * Violation of intrinsic preconditions (an [`unreachable_unchecked`] being
   reached, calling [`copy_nonoverlapping`] with overlapping ranges, ...)
 * Not sufficiently aligned memory accesses and references
-* Violation of *some* basic type invariants (a `bool` that is not 0 or 1, for example,
+* Violation of basic type invariants (a `bool` that is not 0 or 1, for example,
   or an invalid enum discriminant)
 * **Experimental**: Violations of the [Stacked Borrows] rules governing aliasing
   for reference types
 * **Experimental**: Violations of the [Tree Borrows] aliasing rules, as an optional
   alternative to [Stacked Borrows]
-* **Experimental**: Data races
+* **Experimental**: Data races and emulation of weak memory effects, i.e.,
+  atomic reads can return outdated values.
 
 On top of that, Miri will also tell you about memory leaks: when there is memory
 still allocated at the end of the execution, and that memory is not reachable
 from a global `static`, Miri will raise an error.
 
-Miri supports almost all Rust language features; in particular, unwinding and
-concurrency are properly supported (including some experimental emulation of
-weak memory effects, i.e., reads can return outdated values).
-
 You can use Miri to emulate programs on other targets, e.g. to ensure that
 byte-level data manipulation works correctly both on little-endian and
 big-endian systems. See
 [cross-interpretation](#cross-interpretation-running-for-different-targets)
 below.
 
-Miri has already discovered some [real-world bugs](#bugs-found-by-miri). If you
+Miri has already discovered many [real-world bugs](#bugs-found-by-miri). If you
 found a bug with Miri, we'd appreciate if you tell us and we'll add it to the
 list!
 
@@ -45,24 +40,27 @@
 (In particular, the "fake" system RNG APIs make Miri **not suited for
 cryptographic use**! Do not generate keys using Miri.)
 
-All that said, be aware that Miri will **not catch all cases of undefined
-behavior** in your program, and cannot run all programs:
+All that said, be aware that Miri does **not catch every violation of the Rust specification** in
+your program, not least because there is no such specification. Miri uses its own approximation of
+what is and is not Undefined Behavior in Rust. To the best of our knowledge, all Undefined Behavior
+that has the potential to affect a program's correctness *is* being detected by Miri (modulo
+[bugs][I-misses-ub]), but you should consult [the Reference][reference-ub] for the official
+definition of Undefined Behavior. Miri will be updated with the Rust compiler to protect against UB
+as it is understood by the current compiler, but it makes no promises about future versions of
+rustc.
 
-* There are still plenty of open questions around the basic invariants for some
-  types and when these invariants even have to hold. Miri tries to avoid false
-  positives here, so if your program runs fine in Miri right now that is by no
-  means a guarantee that it is UB-free when these questions get answered.
+Further caveats that Miri users should be aware of:
 
-    In particular, Miri does not check that references point to valid data.
 * If the program relies on unspecified details of how data is laid out, it will
   still run fine in Miri -- but might break (including causing UB) on different
-  compiler versions or different platforms.
+  compiler versions or different platforms. (You can use `-Zrandomize-layout`
+  to detect some of these cases.)
 * Program execution is non-deterministic when it depends, for example, on where
   exactly in memory allocations end up, or on the exact interleaving of
   concurrent threads. Miri tests one of many possible executions of your
-  program. You can alleviate this to some extent by running Miri with different
-  values for `-Zmiri-seed`, but that will still by far not explore all possible
-  executions.
+  program, but it will miss bugs that only occur in a different possible execution.
+  You can alleviate this to some extent by running Miri with different
+  values for `-Zmiri-seed`, but that will still by far not explore all possible executions.
 * Miri runs the program as a platform-independent interpreter, so the program
   has no access to most platform-specific APIs or FFI. A few APIs have been
   implemented (such as printing to stdout, accessing environment variables, and
@@ -70,8 +68,8 @@
   not support networking. System API support varies between targets; if you run
   on Windows it is a good idea to use `--target x86_64-unknown-linux-gnu` to get
   better support.
-* Weak memory emulation may [produce weak behaviours](https://github.com/rust-lang/miri/issues/2301)
-  unobservable by compiled programs running on real hardware when `SeqCst` fences are used, and it
+* Weak memory emulation may [produce weak behaviors](https://github.com/rust-lang/miri/issues/2301)
+  when `SeqCst` fences are used that are not actually permitted by the Rust memory model, and it
   cannot produce all behaviors possibly observable on real hardware.
 
 Moreover, Miri fundamentally cannot tell you whether your code is *sound*. [Soundness] is the property
@@ -87,6 +85,8 @@
 [Stacked Borrows]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
 [Tree Borrows]: https://perso.crans.org/vanille/treebor/
 [Soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library
+[reference-ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+[I-misses-ub]: https://github.com/rust-lang/miri/labels/I-misses-UB
 
 
 ## Using Miri
@@ -97,14 +97,8 @@
 rustup +nightly component add miri
 ```
 
-If `rustup` says the `miri` component is unavailable, that's because not all
-nightly releases come with all tools. Check out
-[this website](https://rust-lang.github.io/rustup-components-history) to
-determine a nightly version that comes with Miri and install that using `rustup
-toolchain install nightly-YYYY-MM-DD`. Either way, all of the following commands
-assume the right toolchain is pinned via `rustup override set nightly` or
-`rustup override set nightly-YYYY-MM-DD`. (Alternatively, use `cargo
-+nightly`/`cargo +nightly-YYYY-MM-DD` for each of the following commands.)
+All the following commands assume the nightly toolchain is pinned via `rustup override set nightly`.
+Alternatively, use `cargo +nightly` for each of the following commands.
 
 Now you can run your project in Miri:
 
@@ -118,12 +112,12 @@
 example, `cargo miri test filter` only runs the tests containing `filter` in
 their name.
 
-You can pass arguments to Miri via `MIRIFLAGS`. For example,
+You can pass [flags][miri-flags] to Miri via `MIRIFLAGS`. For example,
 `MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run` runs the program
 without checking the aliasing of references.
 
 When compiling code via `cargo miri`, the `cfg(miri)` config flag is set for code
-that will be interpret under Miri. You can use this to ignore test cases that fail
+that will be interpreted under Miri. You can use this to ignore test cases that fail
 under Miri because they do things Miri does not support:
 
 ```rust
@@ -159,10 +153,8 @@
 
 ### Running Miri on CI
 
-To run Miri on CI, make sure that you handle the case where the latest nightly
-does not ship the Miri component because it currently does not build. `rustup
-toolchain install --component` knows how to handle this situation, so the
-following snippet should always work:
+When running Miri on CI, use the following snippet to install a nightly toolchain with the Miri
+component:
 
 ```sh
 rustup toolchain install nightly --component miri
@@ -227,7 +219,7 @@
 - We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
   - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`.
   - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
-  - `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works.
+  - `solaris` / `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works.
   - `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works.
 - For targets on other operating systems, Miri might fail before even reaching the `main` function.
 
@@ -273,25 +265,12 @@
 RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test
 ```
 
-#### "found possibly newer version of crate `std` which `<dependency>` depends on"
-
-Your build directory may contain artifacts from an earlier build that have/have
-not been built for Miri. Run `cargo clean` before switching from non-Miri to
-Miri builds and vice-versa.
-
 #### "found crate `std` compiled by an incompatible version of rustc"
 
 You may be running `cargo miri` with a different compiler version than the one
 used to build the custom libstd that Miri uses, and Miri failed to detect that.
 Try running `cargo miri clean`.
 
-#### "no mir for `std::rt::lang_start_internal`"
-
-This means the sysroot you are using was not compiled with Miri in mind.  This
-should never happen when you use `cargo miri` because that takes care of setting
-up the sysroot.  If you are using `miri` (the Miri driver) directly, see the
-[contributors' guide](CONTRIBUTING.md) for how to use `./miri` to best do that.
-
 
 ## Miri `-Z` flags and environment variables
 [miri-flags]: #miri--z-flags-and-environment-variables
@@ -395,17 +374,17 @@
   this flag is **unsound**.
 * `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
   memory effects.
-* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
-  for FFI calls. Functions not provided by that file are still executed via the usual Miri shims.
-  **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
-  And of course, Miri cannot do any checks on the actions taken by the external code.
+* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
+  for calling native functions from inside the interpreter via FFI. Functions not provided by that
+  file are still executed via the usual Miri shims.
+  **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself!
+  And of course, Miri cannot do any checks on the actions taken by the native code.
   Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
   working on file descriptors, you will have to replace *all* of them, or the two kinds of
   file descriptors will be mixed up.
   This is **work in progress**; currently, only integer arguments and return values are
   supported (and no, pointer/integer casts to work around this limitation will not work;
-  they will fail horribly). It also only works on unix hosts for now.
-  Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
+  they will fail horribly). It also only works on Linux hosts for now.
 * `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
    This can be used to find which parts of your program are executing slowly under Miri.
    The profile is written out to a file inside a directory called `<name>`, and can be processed
@@ -484,50 +463,14 @@
 * `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic
   setup -- only set this if you do not want to use the automatically created sysroot. When invoking
   `cargo miri setup`, this indicates where the sysroot will be put.
-* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target
-  architecture to test against.  `miri` and `cargo miri` accept the `--target` flag for the same
-  purpose.
 * `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests.
   By default, the number of cores is used.
 * `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing
   and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target
   name; this environment variable is only needed when that heuristic fails.)
-* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all
-  `stderr` and `stdout` files instead of checking whether the output matches.
 * `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the
   `stderr` or `stdout` files match the actual output.
 
-The following environment variables are *internal* and must not be used by
-anyone but Miri itself. They are used to communicate between different Miri
-binaries, and as such worth documenting:
-
-* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to
-  actually not interpret the code but compile it like rustc would. With `target`, Miri sets
-  some compiler flags to prepare the code for interpretation; with `host`, this is not done.
-  This environment variable is useful to be sure that the compiled `rlib`s are compatible
-  with Miri.
-* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build,
-  which will re-invoke `cargo-miri` as the `rustc` to use for this build.
-* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is
-  running as a child process of `rustdoc`, which invokes it twice for each doc-test
-  and requires special treatment, most notably a check-only build before interpretation.
-  This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper.
-* `MIRI_CWD` when set to any value tells the Miri driver to change to the given
-  directory after loading all the source files, but before commencing
-  interpretation. This is useful if the interpreted program wants a different
-  working directory at run-time than at build-time.
-* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which
-  crates should be given special treatment in diagnostics, in addition to the
-  crate currently being compiled.
-* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the
-  value of `RUSTDOC` from before it was overwritten.
-* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap
-  run the standard library tests in Miri.
-* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to
-  perform verbose logging.
-* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host*
-  operations.
-
 [testing-miri]: CONTRIBUTING.md#testing-the-miri-driver
 
 ## Miri `extern` functions
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index 713c978..f5fbb05 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -31,24 +31,26 @@
 endgroup
 
 # Run tests. Recognizes these variables:
-# - MIRI_TEST_TARGET: the target to test. Empty for host target.
+# - TEST_TARGET: the target to test. Empty for host target.
 # - GC_STRESS: if non-empty, run the GC stress test for the main test suite.
 # - MIR_OPT: if non-empty, re-run test `pass` tests with mir-opt-level=4
 # - MANY_SEEDS: if set to N, run the "many-seeds" tests N times
 # - TEST_BENCH: if non-empty, check that the benchmarks all build
 # - CARGO_MIRI_ENV: if non-empty, set some env vars and config to potentially confuse cargo-miri
 function run_tests {
-  if [ -n "${MIRI_TEST_TARGET-}" ]; then
-    begingroup "Testing foreign architecture $MIRI_TEST_TARGET"
+  if [ -n "${TEST_TARGET-}" ]; then
+    begingroup "Testing foreign architecture $TEST_TARGET"
+    TARGET_FLAG="--target $TEST_TARGET"
   else
     begingroup "Testing host architecture"
+    TARGET_FLAG=""
   fi
 
   ## ui test suite
   if [ -n "${GC_STRESS-}" ]; then
-    time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test
+    time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test $TARGET_FLAG
   else
-    time ./miri test
+    time ./miri test $TARGET_FLAG
   fi
 
   ## advanced tests
@@ -59,17 +61,17 @@
     # them. Also error locations change so we don't run the failing tests.
     # We explicitly enable debug-assertions here, they are disabled by -O but we have tests
     # which exist to check that we panic on debug assertion failures.
-    time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic}
+    time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $TARGET_FLAG tests/{pass,panic}
   fi
   if [ -n "${MANY_SEEDS-}" ]; then
     # Also run some many-seeds tests.
     time for FILE in tests/many-seeds/*.rs; do
-      ./miri run "--many-seeds=0..$MANY_SEEDS" "$FILE"
+      ./miri run "--many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE"
     done
   fi
   if [ -n "${TEST_BENCH-}" ]; then
     # Check that the benchmarks build and run, but only once.
-    time HYPERFINE="hyperfine -w0 -r1" ./miri bench
+    time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG
   fi
 
   ## test-cargo-miri
@@ -91,7 +93,7 @@
     echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml
   fi
   # Run the actual test
-  time ${PYTHON} test-cargo-miri/run-test.py
+  time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG
   # Clean up
   unset RUSTC MIRI
   rm -rf .cargo
@@ -100,17 +102,18 @@
 }
 
 function run_tests_minimal {
-  if [ -n "${MIRI_TEST_TARGET-}" ]; then
-    begingroup "Testing MINIMAL foreign architecture $MIRI_TEST_TARGET: only testing $@"
+  if [ -n "${TEST_TARGET-}" ]; then
+    begingroup "Testing MINIMAL foreign architecture $TEST_TARGET: only testing $@"
+    TARGET_FLAG="--target $TEST_TARGET"
   else
-    echo "run_tests_minimal requires MIRI_TEST_TARGET to be set"
+    echo "run_tests_minimal requires TEST_TARGET to be set"
     exit 1
   fi
 
-  time ./miri test -- "$@"
+  time ./miri test $TARGET_FLAG "$@"
 
   # Ensure that a small smoke test of cargo-miri works.
-  time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET}
+  time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml $TARGET_FLAG
 
   endgroup
 }
@@ -126,34 +129,33 @@
     # Extra tier 1
     # With reduced many-seed count to avoid spending too much time on that.
     # (All OSes and ABIs are run with 64 seeds at least once though via the macOS runner.)
-    MANY_SEEDS=16 MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests
-    MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
-    MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests
-    MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests
+    MANY_SEEDS=16 TEST_TARGET=i686-unknown-linux-gnu run_tests
+    MANY_SEEDS=16 TEST_TARGET=aarch64-unknown-linux-gnu run_tests
+    MANY_SEEDS=16 TEST_TARGET=x86_64-apple-darwin run_tests
+    MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests
     ;;
   aarch64-apple-darwin)
     # Host (tier 2)
     GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
     # Extra tier 1
-    MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests
-    MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests
+    MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests
+    MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests
     # Extra tier 2
-    MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests
-    MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
+    TEST_TARGET=arm-unknown-linux-gnueabi run_tests
+    TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
     # Partially supported targets (tier 2)
     VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims
     BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures
-    MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus
-    MIRI_TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus
-    MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random
-    # TODO fix solaris stack guard
-    # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync
-    MIRI_TEST_TARGET=aarch64-linux-android  run_tests_minimal $VERY_BASIC hello panic/panic
-    MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
-    MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
-    MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
+    TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
+    TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
+    TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
+    TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $VERY_BASIC hello panic/panic
+    TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
+    TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
+    TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
     # Custom target JSON file
-    MIRI_TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std
+    TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std
     ;;
   i686-pc-windows-msvc)
     # Host
@@ -163,7 +165,7 @@
     # Extra tier 1
     # We really want to ensure a Linux target works on a Windows host,
     # and a 64bit target works on a 32bit host.
-    MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests
+    TEST_TARGET=x86_64-unknown-linux-gnu run_tests
     ;;
   *)
     echo "FATAL: unknown host target: $HOST_TARGET"
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index 6d07455..8e2b07a 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -1,5 +1,5 @@
 use std::env;
-use std::ffi::OsString;
+use std::ffi::{OsStr, OsString};
 use std::io::Write;
 use std::ops::Not;
 use std::ops::Range;
@@ -23,7 +23,9 @@
 
 impl MiriEnv {
     /// Returns the location of the sysroot.
-    fn build_miri_sysroot(&mut self, quiet: bool) -> Result<PathBuf> {
+    ///
+    /// If the target is None the sysroot will be built for the host machine.
+    fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<PathBuf> {
         if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
             // Sysroot already set, use that.
             return Ok(miri_sysroot.into());
@@ -35,26 +37,28 @@
         self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
         self.build(&manifest_path, &[], quiet)?;
 
-        let target = &match self.sh.var("MIRI_TEST_TARGET") {
-            Ok(target) => vec!["--target".into(), target],
-            Err(_) => vec![],
-        };
+        let target_flag =
+            if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] };
+        let target_flag = &target_flag;
+
         if !quiet {
-            match self.sh.var("MIRI_TEST_TARGET") {
-                Ok(target) => eprintln!("$ (building Miri sysroot for {target})"),
-                Err(_) => eprintln!("$ (building Miri sysroot)"),
+            if let Some(target) = target {
+                eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy());
+            } else {
+                eprintln!("$ (building Miri sysroot)");
             }
         }
+
         let output = cmd!(self.sh,
             "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
-             miri setup --print-sysroot {target...}"
+             miri setup --print-sysroot {target_flag...}"
         ).read();
         let Ok(output) = output else {
             // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error.
             cmd!(
                 self.sh,
                 "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} --
-                miri setup {target...}"
+                miri setup {target_flag...}"
             )
             .run()
             .with_context(|| "`cargo miri setup` failed")?;
@@ -161,13 +165,13 @@
             Command::Install { flags } => Self::install(flags),
             Command::Build { flags } => Self::build(flags),
             Command::Check { flags } => Self::check(flags),
-            Command::Test { bless, flags } => Self::test(bless, flags),
+            Command::Test { bless, flags, target } => Self::test(bless, flags, target),
             Command::Run { dep, verbose, many_seeds, flags } =>
                 Self::run(dep, verbose, many_seeds, flags),
             Command::Fmt { flags } => Self::fmt(flags),
             Command::Clippy { flags } => Self::clippy(flags),
             Command::Cargo { flags } => Self::cargo(flags),
-            Command::Bench { benches } => Self::bench(benches),
+            Command::Bench { target, benches } => Self::bench(target, benches),
             Command::Toolchain { flags } => Self::toolchain(flags),
             Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
             Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
@@ -369,7 +373,7 @@
         Ok(())
     }
 
-    fn bench(benches: Vec<OsString>) -> Result<()> {
+    fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
         // The hyperfine to use
         let hyperfine = env::var("HYPERFINE");
         let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@@ -377,8 +381,6 @@
         let Some((program_name, args)) = hyperfine.split_first() else {
             bail!("expected HYPERFINE environment variable to be non-empty");
         };
-        // Extra flags to pass to cargo.
-        let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
         // Make sure we have an up-to-date Miri installed and selected the right toolchain.
         Self::install(vec![])?;
 
@@ -394,6 +396,14 @@
         } else {
             benches.to_owned()
         };
+        let target_flag = if let Some(target) = target {
+            let mut flag = OsString::from("--target=");
+            flag.push(target);
+            flag
+        } else {
+            OsString::new()
+        };
+        let target_flag = &target_flag;
         // Run the requested benchmarks
         for bench in benches {
             let current_bench = path!(benches_dir / bench / "Cargo.toml");
@@ -401,7 +411,7 @@
             // That seems to make Windows CI happy.
             cmd!(
                 sh,
-                "{program_name} {args...} 'cargo miri run '{cargo_extra_flags}' --manifest-path \"'{current_bench}'\"'"
+                "{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
             )
             .run()?;
         }
@@ -446,16 +456,26 @@
         Ok(())
     }
 
-    fn test(bless: bool, flags: Vec<OsString>) -> Result<()> {
+    fn test(bless: bool, mut flags: Vec<OsString>, target: Option<OsString>) -> Result<()> {
         let mut e = MiriEnv::new()?;
-        // Prepare a sysroot.
-        e.build_miri_sysroot(/* quiet */ false)?;
 
-        // Then test, and let caller control flags.
-        // Only in root project as `cargo-miri` has no tests.
+        // Prepare a sysroot.
+        e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;
+
+        // Forward information to test harness.
         if bless {
             e.sh.set_var("RUSTC_BLESS", "Gesundheit");
         }
+        if let Some(target) = target {
+            // Tell the harness which target to test.
+            e.sh.set_var("MIRI_TEST_TARGET", target);
+        }
+
+        // Make sure the flags are going to the test harness, not cargo.
+        flags.insert(0, "--".into());
+
+        // Then test, and let caller control flags.
+        // Only in root project as `cargo-miri` has no tests.
         e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?;
         Ok(())
     }
@@ -467,32 +487,16 @@
         mut flags: Vec<OsString>,
     ) -> Result<()> {
         let mut e = MiriEnv::new()?;
-        // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so
-        // that we set the MIRI_SYSROOT up the right way. We must make sure that
-        // MIRI_TEST_TARGET and `--target` are in sync.
-        use itertools::Itertools;
-        let target = flags
-            .iter()
-            .take_while(|arg| *arg != "--")
-            .tuple_windows()
-            .find(|(first, _)| *first == "--target");
-        if let Some((_, target)) = target {
-            // Found it!
-            e.sh.set_var("MIRI_TEST_TARGET", target);
-        } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") {
-            // Convert `MIRI_TEST_TARGET` into `--target`.
-            flags.push("--target".into());
-            flags.push(target.into());
-        }
+        let target = arg_flag_value(&flags, "--target");
+
         // Scan for "--edition", set one ourselves if that flag is not present.
-        let have_edition =
-            flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition");
+        let have_edition = arg_flag_value(&flags, "--edition").is_some();
         if !have_edition {
             flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
         }
 
         // Prepare a sysroot, and add it to the flags.
-        let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose)?;
+        let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
         flags.push("--sysroot".into());
         flags.push(miri_sysroot.into());
 
diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs
index f0ebbc8..a8626ce 100644
--- a/src/tools/miri/miri-script/src/main.rs
+++ b/src/tools/miri/miri-script/src/main.rs
@@ -31,7 +31,10 @@
     /// Build miri, set up a sysroot and then run the test suite.
     Test {
         bless: bool,
-        /// Flags that are passed through to `cargo test`.
+        /// The cross-interpretation target.
+        /// If none then the host is the target.
+        target: Option<OsString>,
+        /// Flags that are passed through to the test harness.
         flags: Vec<OsString>,
     },
     /// Build miri, set up a sysroot and then run the driver with the given <flags>.
@@ -58,6 +61,7 @@
     Cargo { flags: Vec<OsString> },
     /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
     Bench {
+        target: Option<OsString>,
         /// List of benchmarks to run. By default all benchmarks are run.
         benches: Vec<OsString>,
     },
@@ -84,9 +88,9 @@
 ./miri check <flags>:
 Just check miri. <flags> are passed to `cargo check`.
 
-./miri test [--bless] <flags>:
-Build miri, set up a sysroot and then run the test suite. <flags> are passed
-to the final `cargo test` invocation.
+./miri test [--bless] [--target <target>] <flags>:
+Build miri, set up a sysroot and then run the test suite.
+<flags> are passed to the test harness.
 
 ./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] <flags>:
 Build miri, set up a sysroot and then run the driver with the given <flags>.
@@ -110,7 +114,7 @@
 working directory. Note that the binaries are placed in the `miri` toolchain
 sysroot, to prevent conflicts with other toolchains.
 
-./miri bench <benches>:
+./miri bench [--target <target>] <benches>:
 Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
 <benches> can explicitly list the benchmarks to run; by default, all of them are run.
 
@@ -147,12 +151,30 @@
         Some("build") => Command::Build { flags: args.collect() },
         Some("check") => Command::Check { flags: args.collect() },
         Some("test") => {
-            let bless = args.peek().is_some_and(|a| a.to_str() == Some("--bless"));
-            if bless {
-                // Consume the flag.
+            let mut target = None;
+            let mut bless = false;
+
+            while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
+                match arg {
+                    "--bless" => bless = true,
+                    "--target" => {
+                        // Skip "--target"
+                        args.next().unwrap();
+                        // Next argument is the target triple.
+                        let val = args.peek().ok_or_else(|| {
+                            anyhow!("`--target` must be followed by target triple")
+                        })?;
+                        target = Some(val.to_owned());
+                    }
+                    // Only parse the leading flags.
+                    _ => break,
+                }
+
+                // Consume the flag, look at the next one.
                 args.next().unwrap();
             }
-            Command::Test { bless, flags: args.collect() }
+
+            Command::Test { bless, flags: args.collect(), target }
         }
         Some("run") => {
             let mut dep = false;
@@ -188,7 +210,29 @@
         Some("clippy") => Command::Clippy { flags: args.collect() },
         Some("cargo") => Command::Cargo { flags: args.collect() },
         Some("install") => Command::Install { flags: args.collect() },
-        Some("bench") => Command::Bench { benches: args.collect() },
+        Some("bench") => {
+            let mut target = None;
+            while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
+                match arg {
+                    "--target" => {
+                        // Skip "--target"
+                        args.next().unwrap();
+                        // Next argument is the target triple.
+                        let val = args.peek().ok_or_else(|| {
+                            anyhow!("`--target` must be followed by target triple")
+                        })?;
+                        target = Some(val.to_owned());
+                    }
+                    // Only parse the leading flags.
+                    _ => break,
+                }
+
+                // Consume the flag, look at the next one.
+                args.next().unwrap();
+            }
+
+            Command::Bench { target, benches: args.collect() }
+        }
         Some("toolchain") => Command::Toolchain { flags: args.collect() },
         Some("rustc-pull") => {
             let commit = args.next().map(|a| a.to_string_lossy().into_owned());
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
index 23b5e93..2b5791f 100644
--- a/src/tools/miri/miri-script/src/util.rs
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -27,6 +27,30 @@
     flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
 }
 
+pub fn arg_flag_value(
+    args: impl IntoIterator<Item = impl AsRef<OsStr>>,
+    flag: &str,
+) -> Option<OsString> {
+    let mut args = args.into_iter();
+    while let Some(arg) = args.next() {
+        let arg = arg.as_ref();
+        if arg == "--" {
+            return None;
+        }
+        let Some(arg) = arg.to_str() else {
+            // Skip non-UTF-8 arguments.
+            continue;
+        };
+        if arg == flag {
+            // Next one is the value.
+            return Some(args.next()?.as_ref().to_owned());
+        } else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) {
+            return Some(val.to_owned().into());
+        }
+    }
+    None
+}
+
 /// Some extra state we track for building Miri, such as the right RUSTFLAGS.
 pub struct MiriEnv {
     /// miri_dir is the root of the miri repository checkout we are working in.
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index ca6f4d5..3636c85 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-d568423a7a4ddb4b49323d96078a22f94df55fbd
+ef15976387ad9c1cdceaabf469e0cf35f5852f6d
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 44201cb..305e9cd 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -575,18 +575,15 @@
                 "full" => BacktraceStyle::Full,
                 _ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
             };
-        } else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") {
+        } else if let Some(param) = arg.strip_prefix("-Zmiri-native-lib=") {
             let filename = param.to_string();
             if std::path::Path::new(&filename).exists() {
-                if let Some(other_filename) = miri_config.external_so_file {
-                    show_error!(
-                        "-Zmiri-extern-so-file is already set to {}",
-                        other_filename.display()
-                    );
+                if let Some(other_filename) = miri_config.native_lib {
+                    show_error!("-Zmiri-native-lib is already set to {}", other_filename.display());
                 }
-                miri_config.external_so_file = Some(filename.into());
+                miri_config.native_lib = Some(filename.into());
             } else {
-                show_error!("-Zmiri-extern-so-file `{}` does not exist", filename);
+                show_error!("-Zmiri-native-lib `{}` does not exist", filename);
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") {
             let num_cpus = param
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 97c5e9a..78c092f 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -142,8 +142,8 @@
     /// Whether Stacked Borrows and Tree Borrows retagging should recurse into fields of datatypes.
     pub retag_fields: RetagFields,
     /// The location of a shared object file to load when calling external functions
-    /// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory
-    pub external_so_file: Option<PathBuf>,
+    /// FIXME! consider allowing users to specify paths to multiple files, or to a directory
+    pub native_lib: Option<PathBuf>,
     /// Run a garbage collector for BorTags every N basic blocks.
     pub gc_interval: u32,
     /// The number of CPUs to be reported by miri.
@@ -188,7 +188,7 @@
             preemption_rate: 0.01, // 1%
             report_progress: None,
             retag_fields: RetagFields::Yes,
-            external_so_file: None,
+            native_lib: None,
             gc_interval: 10_000,
             num_cpus: 1,
             page_size: None,
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 40c2008..4050ae3 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -751,26 +751,23 @@
 
     /// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
     /// as a platform-specific errnum.
-    fn io_error_to_errnum(
-        &self,
-        err_kind: std::io::ErrorKind,
-    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+    fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar<Provenance>> {
         let this = self.eval_context_ref();
         let target = &this.tcx.sess.target;
         if target.families.iter().any(|f| f == "unix") {
             for &(name, kind) in UNIX_IO_ERROR_TABLE {
-                if err_kind == kind {
+                if err.kind() == kind {
                     return Ok(this.eval_libc(name));
                 }
             }
-            throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
+            throw_unsup_format!("unsupported io error: {err}")
         } else if target.families.iter().any(|f| f == "windows") {
             for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
-                if err_kind == kind {
+                if err.kind() == kind {
                     return Ok(this.eval_windows("c", name));
                 }
             }
-            throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind);
+            throw_unsup_format!("unsupported io error: {err}");
         } else {
             throw_unsup_format!(
                 "converting io::Error into errnum is unsupported for OS {}",
@@ -812,8 +809,8 @@
     }
 
     /// Sets the last OS error using a `std::io::ErrorKind`.
-    fn set_last_error_from_io_error(&mut self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx> {
-        self.set_last_error(self.io_error_to_errnum(err_kind)?)
+    fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> {
+        self.set_last_error(self.io_error_to_errnum(err)?)
     }
 
     /// Helper function that consumes an `std::io::Result<T>` and returns an
@@ -829,7 +826,7 @@
         match result {
             Ok(ok) => Ok(ok),
             Err(e) => {
-                self.eval_context_mut().set_last_error_from_io_error(e.kind())?;
+                self.eval_context_mut().set_last_error_from_io_error(e)?;
                 Ok((-1).into())
             }
         }
@@ -982,29 +979,46 @@
         Ok((true, string_length))
     }
 
-    /// Read a sequence of u16 until the first null terminator.
-    fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
+    /// Helper function to read a sequence of unsigned integers of the given size and alignment
+    /// until the first null terminator.
+    fn read_c_str_with_char_size<T>(
+        &self,
+        mut ptr: Pointer<Option<Provenance>>,
+        size: Size,
+        align: Align,
+    ) -> InterpResult<'tcx, Vec<T>>
+    where
+        T: TryFrom<u128>,
+        <T as TryFrom<u128>>::Error: std::fmt::Debug,
+    {
+        assert_ne!(size, Size::ZERO);
+
         let this = self.eval_context_ref();
-        let size2 = Size::from_bytes(2);
-        this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
+
+        this.check_ptr_align(ptr, align)?;
 
         let mut wchars = Vec::new();
         loop {
             // FIXME: We are re-getting the allocation each time around the loop.
             // Would be nice if we could somehow "extend" an existing AllocRange.
-            let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result
-            let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?;
-            if wchar == 0 {
+            let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result
+            let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
+            if wchar_int == 0 {
                 break;
             } else {
-                wchars.push(wchar);
-                ptr = ptr.offset(size2, this)?;
+                wchars.push(wchar_int.try_into().unwrap());
+                ptr = ptr.offset(size, this)?;
             }
         }
 
         Ok(wchars)
     }
 
+    /// Read a sequence of u16 until the first null terminator.
+    fn read_wide_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
+        self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
+    }
+
     /// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what
     /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
     /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
@@ -1037,6 +1051,14 @@
         Ok((true, string_length))
     }
 
+    /// Read a sequence of wchar_t until the first null terminator.
+    /// Always returns a `Vec<u32>` no matter the size of `wchar_t`.
+    fn read_wchar_t_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u32>> {
+        let this = self.eval_context_ref();
+        let wchar_t = this.libc_ty_layout("wchar_t");
+        self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
+    }
+
     /// Check that the ABI is what we expect.
     fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
         if abi != exp_abi {
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 1680b98..54eb6a3 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(let_chains)]
 #![feature(lint_reasons)]
 #![feature(trait_upcasting)]
+#![feature(strict_overflow_ops)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 51b96bf..8854b18 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -535,11 +535,11 @@
     // The total number of blocks that have been executed.
     pub(crate) basic_block_count: u64,
 
-    /// Handle of the optional shared object file for external functions.
+    /// Handle of the optional shared object file for native functions.
     #[cfg(target_os = "linux")]
-    pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
+    pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
     #[cfg(not(target_os = "linux"))]
-    pub external_so_lib: Option<!>,
+    pub native_lib: Option<!>,
 
     /// Run a garbage collector for BorTags every N basic blocks.
     pub(crate) gc_interval: u32,
@@ -665,7 +665,7 @@
             basic_block_count: 0,
             clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
             #[cfg(target_os = "linux")]
-            external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
+            native_lib: config.native_lib.as_ref().map(|lib_file_path| {
                 let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
                 // Check if host target == the session target.
                 if env!("TARGET") != target_triple {
@@ -687,7 +687,7 @@
                 )
             }),
             #[cfg(not(target_os = "linux"))]
-            external_so_lib: config.external_so_file.as_ref().map(|_| {
+            native_lib: config.native_lib.as_ref().map(|_| {
                 panic!("loading external .so files is only supported on Linux")
             }),
             gc_interval: config.gc_interval,
@@ -802,7 +802,7 @@
             preemption_rate: _,
             report_progress: _,
             basic_block_count: _,
-            external_so_lib: _,
+            native_lib: _,
             gc_interval: _,
             since_gc: _,
             num_cpus: _,
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs
index 7953159..1deb9a5 100644
--- a/src/tools/miri/src/shims/alloc.rs
+++ b/src/tools/miri/src/shims/alloc.rs
@@ -19,22 +19,36 @@
 
 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
-    /// Returns the minimum alignment for the target architecture for allocations of the given size.
-    fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
+    /// Returns the alignment that `malloc` would guarantee for requests of the given size.
+    fn malloc_align(&self, size: u64) -> Align {
         let this = self.eval_context_ref();
-        // List taken from `library/std/src/sys/pal/common/alloc.rs`.
-        // This list should be kept in sync with the one from libstd.
-        let min_align = match this.tcx.sess.target.arch.as_ref() {
+        // The C standard says: "The pointer returned if the allocation succeeds is suitably aligned
+        // so that it may be assigned to a pointer to any type of object with a fundamental
+        // alignment requirement and size less than or equal to the size requested."
+        // So first we need to figure out what the limits are for "fundamental alignment".
+        // This is given by `alignof(max_align_t)`. The following list is taken from
+        // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should
+        // be kept in sync.
+        let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() {
             "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8,
             "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" =>
                 16,
             arch => bug!("unsupported target architecture for malloc: `{}`", arch),
         };
-        // Windows always aligns, even small allocations.
-        // Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
-        // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
-        if kind == MiriMemoryKind::WinHeap || size >= min_align {
-            return Align::from_bytes(min_align).unwrap();
+        // The C standard only requires sufficient alignment for any *type* with size less than or
+        // equal to the size requested. Types one can define in standard C seem to never have an alignment
+        // bigger than their size. So if the size is 2, then only alignment 2 is guaranteed, even if
+        // `max_fundamental_align` is bigger.
+        // This matches what some real-world implementations do, see e.g.
+        // - https://github.com/jemalloc/jemalloc/issues/1533
+        // - https://github.com/llvm/llvm-project/issues/53540
+        // - https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm
+        if size >= max_fundamental_align {
+            return Align::from_bytes(max_fundamental_align).unwrap();
+        }
+        // C doesn't have zero-sized types, so presumably nothing is guaranteed here.
+        if size == 0 {
+            return Align::ONE;
         }
         // We have `size < min_align`. Round `size` *down* to the next power of two and use that.
         fn prev_power_of_two(x: u64) -> u64 {
@@ -82,34 +96,25 @@
         &mut self,
         size: u64,
         zero_init: bool,
-        kind: MiriMemoryKind,
     ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
         let this = self.eval_context_mut();
-        if size == 0 {
-            Ok(Pointer::null())
-        } else {
-            let align = this.min_align(size, kind);
-            let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
-            if zero_init {
-                // We just allocated this, the access is definitely in-bounds and fits into our address space.
-                this.write_bytes_ptr(
-                    ptr.into(),
-                    iter::repeat(0u8).take(usize::try_from(size).unwrap()),
-                )
-                .unwrap();
-            }
-            Ok(ptr.into())
+        let align = this.malloc_align(size);
+        let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?;
+        if zero_init {
+            // We just allocated this, the access is definitely in-bounds and fits into our address space.
+            this.write_bytes_ptr(
+                ptr.into(),
+                iter::repeat(0u8).take(usize::try_from(size).unwrap()),
+            )
+            .unwrap();
         }
+        Ok(ptr.into())
     }
 
-    fn free(
-        &mut self,
-        ptr: Pointer<Option<Provenance>>,
-        kind: MiriMemoryKind,
-    ) -> InterpResult<'tcx> {
+    fn free(&mut self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         if !this.ptr_is_null(ptr)? {
-            this.deallocate_ptr(ptr, None, kind.into())?;
+            this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
         }
         Ok(())
     }
@@ -118,19 +123,12 @@
         &mut self,
         old_ptr: Pointer<Option<Provenance>>,
         new_size: u64,
-        kind: MiriMemoryKind,
     ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
         let this = self.eval_context_mut();
-        let new_align = this.min_align(new_size, kind);
+        let new_align = this.malloc_align(new_size);
         if this.ptr_is_null(old_ptr)? {
             // Here we must behave like `malloc`.
-            if new_size == 0 {
-                Ok(Pointer::null())
-            } else {
-                let new_ptr =
-                    this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?;
-                Ok(new_ptr.into())
-            }
+            self.malloc(new_size, /*zero_init*/ false)
         } else {
             if new_size == 0 {
                 // C, in their infinite wisdom, made this UB.
@@ -142,7 +140,7 @@
                     None,
                     Size::from_bytes(new_size),
                     new_align,
-                    kind.into(),
+                    MiriMemoryKind::C.into(),
                 )?;
                 Ok(new_ptr.into())
             }
diff --git a/src/tools/miri/src/shims/ffi_support.rs b/src/tools/miri/src/shims/ffi_support.rs
deleted file mode 100644
index 6da119e..0000000
--- a/src/tools/miri/src/shims/ffi_support.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-use libffi::{high::call as ffi, low::CodePtr};
-use std::ops::Deref;
-
-use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy};
-use rustc_span::Symbol;
-use rustc_target::abi::HasDataLayout;
-
-use crate::*;
-
-impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
-
-pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
-    /// Extract the scalar value from the result of reading a scalar from the machine,
-    /// and convert it to a `CArg`.
-    fn scalar_to_carg(
-        k: Scalar<Provenance>,
-        arg_type: Ty<'tcx>,
-        cx: &impl HasDataLayout,
-    ) -> InterpResult<'tcx, CArg> {
-        match arg_type.kind() {
-            // If the primitive provided can be converted to a type matching the type pattern
-            // then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
-            // the ints
-            ty::Int(IntTy::I8) => {
-                return Ok(CArg::Int8(k.to_i8()?));
-            }
-            ty::Int(IntTy::I16) => {
-                return Ok(CArg::Int16(k.to_i16()?));
-            }
-            ty::Int(IntTy::I32) => {
-                return Ok(CArg::Int32(k.to_i32()?));
-            }
-            ty::Int(IntTy::I64) => {
-                return Ok(CArg::Int64(k.to_i64()?));
-            }
-            ty::Int(IntTy::Isize) => {
-                // This will fail if host != target, but then the entire FFI thing probably won't work well
-                // in that situation.
-                return Ok(CArg::ISize(k.to_target_isize(cx)?.try_into().unwrap()));
-            }
-            // the uints
-            ty::Uint(UintTy::U8) => {
-                return Ok(CArg::UInt8(k.to_u8()?));
-            }
-            ty::Uint(UintTy::U16) => {
-                return Ok(CArg::UInt16(k.to_u16()?));
-            }
-            ty::Uint(UintTy::U32) => {
-                return Ok(CArg::UInt32(k.to_u32()?));
-            }
-            ty::Uint(UintTy::U64) => {
-                return Ok(CArg::UInt64(k.to_u64()?));
-            }
-            ty::Uint(UintTy::Usize) => {
-                // This will fail if host != target, but then the entire FFI thing probably won't work well
-                // in that situation.
-                return Ok(CArg::USize(k.to_target_usize(cx)?.try_into().unwrap()));
-            }
-            _ => {}
-        }
-        // If no primitives were returned then we have an unsupported type.
-        throw_unsup_format!(
-            "unsupported scalar argument type to external C function: {:?}",
-            arg_type
-        );
-    }
-
-    /// Call external C function and
-    /// store output, depending on return type in the function signature.
-    fn call_external_c_and_store_return<'a>(
-        &mut self,
-        link_name: Symbol,
-        dest: &MPlaceTy<'tcx, Provenance>,
-        ptr: CodePtr,
-        libffi_args: Vec<libffi::high::Arg<'a>>,
-    ) -> InterpResult<'tcx, ()> {
-        let this = self.eval_context_mut();
-
-        // Unsafe because of the call to external C code.
-        // Because this is calling a C function it is not necessarily sound,
-        // but there is no way around this and we've checked as much as we can.
-        unsafe {
-            // If the return type of a function is a primitive integer type,
-            // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified
-            // primitive integer type, and then write this value out to the miri memory as an integer.
-            match dest.layout.ty.kind() {
-                // ints
-                ty::Int(IntTy::I8) => {
-                    let x = ffi::call::<i8>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Int(IntTy::I16) => {
-                    let x = ffi::call::<i16>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Int(IntTy::I32) => {
-                    let x = ffi::call::<i32>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Int(IntTy::I64) => {
-                    let x = ffi::call::<i64>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Int(IntTy::Isize) => {
-                    let x = ffi::call::<isize>(ptr, libffi_args.as_slice());
-                    // `isize` doesn't `impl Into<i128>`, so convert manually.
-                    // Convert to `i64` since this covers both 32- and 64-bit machines.
-                    this.write_int(i64::try_from(x).unwrap(), dest)?;
-                    return Ok(());
-                }
-                // uints
-                ty::Uint(UintTy::U8) => {
-                    let x = ffi::call::<u8>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Uint(UintTy::U16) => {
-                    let x = ffi::call::<u16>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Uint(UintTy::U32) => {
-                    let x = ffi::call::<u32>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Uint(UintTy::U64) => {
-                    let x = ffi::call::<u64>(ptr, libffi_args.as_slice());
-                    this.write_int(x, dest)?;
-                    return Ok(());
-                }
-                ty::Uint(UintTy::Usize) => {
-                    let x = ffi::call::<usize>(ptr, libffi_args.as_slice());
-                    // `usize` doesn't `impl Into<i128>`, so convert manually.
-                    // Convert to `u64` since this covers both 32- and 64-bit machines.
-                    this.write_int(u64::try_from(x).unwrap(), dest)?;
-                    return Ok(());
-                }
-                // Functions with no declared return type (i.e., the default return)
-                // have the output_type `Tuple([])`.
-                ty::Tuple(t_list) =>
-                    if t_list.len() == 0 {
-                        ffi::call::<()>(ptr, libffi_args.as_slice());
-                        return Ok(());
-                    },
-                _ => {}
-            }
-            // FIXME ellen! deal with all the other return types
-            throw_unsup_format!("unsupported return type to external C function: {:?}", link_name);
-        }
-    }
-
-    /// Get the pointer to the function of the specified name in the shared object file,
-    /// if it exists. The function must be in the shared object file specified: we do *not*
-    /// return pointers to functions in dependencies of the library.  
-    fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> {
-        let this = self.eval_context_mut();
-        // Try getting the function from the shared library.
-        // On windows `_lib_path` will be unused, hence the name starting with `_`.
-        let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap();
-        let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe {
-            match lib.get(link_name.as_str().as_bytes()) {
-                Ok(x) => x,
-                Err(_) => {
-                    return None;
-                }
-            }
-        };
-
-        // FIXME: this is a hack!
-        // The `libloading` crate will automatically load system libraries like `libc`.
-        // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202
-        // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the
-        // library if it can't find the symbol in the library itself.
-        // So, in order to check if the function was actually found in the specified
-        // `machine.external_so_lib` we need to check its `dli_fname` and compare it to
-        // the specified SO file path.
-        // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`,
-        // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411
-        // using the `libc` crate where this interface is public.
-        // No `libc::dladdr` on windows.
-        let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
-        unsafe {
-            if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
-                if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
-                    != _lib_path.to_str().unwrap()
-                {
-                    return None;
-                }
-            }
-        }
-        // Return a pointer to the function.
-        Some(CodePtr(*func.deref() as *mut _))
-    }
-
-    /// Call specified external C function, with supplied arguments.
-    /// Need to convert all the arguments from their hir representations to
-    /// a form compatible with C (through `libffi` call).
-    /// Then, convert return from the C call into a corresponding form that
-    /// can be stored in Miri internal memory.
-    fn call_external_c_fct(
-        &mut self,
-        link_name: Symbol,
-        dest: &MPlaceTy<'tcx, Provenance>,
-        args: &[OpTy<'tcx, Provenance>],
-    ) -> InterpResult<'tcx, bool> {
-        // Get the pointer to the function in the shared object file if it exists.
-        let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) {
-            Some(ptr) => ptr,
-            None => {
-                // Shared object file does not export this function -- try the shims next.
-                return Ok(false);
-            }
-        };
-
-        let this = self.eval_context_mut();
-
-        // Get the function arguments, and convert them to `libffi`-compatible form.
-        let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
-        for cur_arg in args.iter() {
-            libffi_args.push(Self::scalar_to_carg(
-                this.read_scalar(cur_arg)?,
-                cur_arg.layout.ty,
-                this,
-            )?);
-        }
-
-        // Convert them to `libffi::high::Arg` type.
-        let libffi_args = libffi_args
-            .iter()
-            .map(|cur_arg| cur_arg.arg_downcast())
-            .collect::<Vec<libffi::high::Arg<'_>>>();
-
-        // Call the function and store output, depending on return type in the function signature.
-        self.call_external_c_and_store_return(link_name, dest, code_ptr, libffi_args)?;
-        Ok(true)
-    }
-}
-
-#[derive(Debug, Clone)]
-/// Enum of supported arguments to external C functions.
-// We introduce this enum instead of just calling `ffi::arg` and storing a list
-// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference
-// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html
-// and we need to store a copy of the value, and pass a reference to this copy to C instead.
-pub enum CArg {
-    /// 8-bit signed integer.
-    Int8(i8),
-    /// 16-bit signed integer.
-    Int16(i16),
-    /// 32-bit signed integer.
-    Int32(i32),
-    /// 64-bit signed integer.
-    Int64(i64),
-    /// isize.
-    ISize(isize),
-    /// 8-bit unsigned integer.
-    UInt8(u8),
-    /// 16-bit unsigned integer.
-    UInt16(u16),
-    /// 32-bit unsigned integer.
-    UInt32(u32),
-    /// 64-bit unsigned integer.
-    UInt64(u64),
-    /// usize.
-    USize(usize),
-}
-
-impl<'a> CArg {
-    /// Convert a `CArg` to a `libffi` argument type.
-    fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
-        match self {
-            CArg::Int8(i) => ffi::arg(i),
-            CArg::Int16(i) => ffi::arg(i),
-            CArg::Int32(i) => ffi::arg(i),
-            CArg::Int64(i) => ffi::arg(i),
-            CArg::ISize(i) => ffi::arg(i),
-            CArg::UInt8(i) => ffi::arg(i),
-            CArg::UInt16(i) => ffi::arg(i),
-            CArg::UInt32(i) => ffi::arg(i),
-            CArg::UInt64(i) => ffi::arg(i),
-            CArg::USize(i) => ffi::arg(i),
-        }
-    }
-}
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index c51a27b..d431c28 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -211,12 +211,12 @@
 
         // First deal with any external C functions in linked .so file.
         #[cfg(target_os = "linux")]
-        if this.machine.external_so_lib.as_ref().is_some() {
-            use crate::shims::ffi_support::EvalContextExt as _;
+        if this.machine.native_lib.as_ref().is_some() {
+            use crate::shims::native_lib::EvalContextExt as _;
             // An Ok(false) here means that the function being called was not exported
             // by the specified `.so` file; we should continue and check if it corresponds to
             // a provided shim.
-            if this.call_external_c_fct(link_name, dest, args)? {
+            if this.call_native_fn(link_name, dest, args)? {
                 return Ok(EmulateItemResult::NeedsJumping);
             }
         }
@@ -421,7 +421,7 @@
             "malloc" => {
                 let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let size = this.read_target_usize(size)?;
-                let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?;
+                let res = this.malloc(size, /*zero_init:*/ false)?;
                 this.write_pointer(res, dest)?;
             }
             "calloc" => {
@@ -432,20 +432,20 @@
                 let size = items
                     .checked_mul(len)
                     .ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?;
-                let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?;
+                let res = this.malloc(size, /*zero_init:*/ true)?;
                 this.write_pointer(res, dest)?;
             }
             "free" => {
                 let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
-                this.free(ptr, MiriMemoryKind::C)?;
+                this.free(ptr)?;
             }
             "realloc" => {
                 let [old_ptr, new_size] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let old_ptr = this.read_pointer(old_ptr)?;
                 let new_size = this.read_target_usize(new_size)?;
-                let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
+                let res = this.realloc(old_ptr, new_size)?;
                 this.write_pointer(res, dest)?;
             }
 
@@ -657,6 +657,16 @@
                     dest,
                 )?;
             }
+            "wcslen" => {
+                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
+                // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
+                let n = this.read_wchar_t_str(ptr)?.len();
+                this.write_scalar(
+                    Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
+                    dest,
+                )?;
+            }
             "memcpy" => {
                 let [ptr_dest, ptr_src, n] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index 1104845..aaa3c69 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -2,9 +2,9 @@
 
 mod alloc;
 mod backtrace;
-#[cfg(target_os = "linux")]
-pub mod ffi_support;
 pub mod foreign_items;
+#[cfg(target_os = "linux")]
+pub mod native_lib;
 pub mod unix;
 pub mod windows;
 mod x86;
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
new file mode 100644
index 0000000..f9b8563
--- /dev/null
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -0,0 +1,242 @@
+//! Implements calling functions from a native library.
+use libffi::{high::call as ffi, low::CodePtr};
+use std::ops::Deref;
+
+use rustc_middle::ty::{self as ty, IntTy, UintTy};
+use rustc_span::Symbol;
+use rustc_target::abi::{Abi, HasDataLayout};
+
+use crate::*;
+
+impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    /// Call native host function and return the output as an immediate.
+    fn call_native_with_args<'a>(
+        &mut self,
+        link_name: Symbol,
+        dest: &MPlaceTy<'tcx, Provenance>,
+        ptr: CodePtr,
+        libffi_args: Vec<libffi::high::Arg<'a>>,
+    ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
+        let this = self.eval_context_mut();
+
+        // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
+        // as the specified primitive integer type
+        let scalar = match dest.layout.ty.kind() {
+            // ints
+            ty::Int(IntTy::I8) => {
+                // Unsafe because of the call to native code.
+                // Because this is calling a C function it is not necessarily sound,
+                // but there is no way around this and we've checked as much as we can.
+                let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) };
+                Scalar::from_i8(x)
+            }
+            ty::Int(IntTy::I16) => {
+                let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) };
+                Scalar::from_i16(x)
+            }
+            ty::Int(IntTy::I32) => {
+                let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) };
+                Scalar::from_i32(x)
+            }
+            ty::Int(IntTy::I64) => {
+                let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) };
+                Scalar::from_i64(x)
+            }
+            ty::Int(IntTy::Isize) => {
+                let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) };
+                Scalar::from_target_isize(x.try_into().unwrap(), this)
+            }
+            // uints
+            ty::Uint(UintTy::U8) => {
+                let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) };
+                Scalar::from_u8(x)
+            }
+            ty::Uint(UintTy::U16) => {
+                let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) };
+                Scalar::from_u16(x)
+            }
+            ty::Uint(UintTy::U32) => {
+                let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) };
+                Scalar::from_u32(x)
+            }
+            ty::Uint(UintTy::U64) => {
+                let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) };
+                Scalar::from_u64(x)
+            }
+            ty::Uint(UintTy::Usize) => {
+                let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) };
+                Scalar::from_target_usize(x.try_into().unwrap(), this)
+            }
+            // Functions with no declared return type (i.e., the default return)
+            // have the output_type `Tuple([])`.
+            ty::Tuple(t_list) if t_list.len() == 0 => {
+                unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
+                return Ok(ImmTy::uninit(dest.layout));
+            }
+            _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name),
+        };
+        Ok(ImmTy::from_scalar(scalar, dest.layout))
+    }
+
+    /// Get the pointer to the function of the specified name in the shared object file,
+    /// if it exists. The function must be in the shared object file specified: we do *not*
+    /// return pointers to functions in dependencies of the library.  
+    fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> {
+        let this = self.eval_context_mut();
+        // Try getting the function from the shared library.
+        // On windows `_lib_path` will be unused, hence the name starting with `_`.
+        let (lib, _lib_path) = this.machine.native_lib.as_ref().unwrap();
+        let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe {
+            match lib.get(link_name.as_str().as_bytes()) {
+                Ok(x) => x,
+                Err(_) => {
+                    return None;
+                }
+            }
+        };
+
+        // FIXME: this is a hack!
+        // The `libloading` crate will automatically load system libraries like `libc`.
+        // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202
+        // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the
+        // library if it can't find the symbol in the library itself.
+        // So, in order to check if the function was actually found in the specified
+        // `machine.external_so_lib` we need to check its `dli_fname` and compare it to
+        // the specified SO file path.
+        // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`,
+        // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411
+        // using the `libc` crate where this interface is public.
+        let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
+        unsafe {
+            if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
+                if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
+                    != _lib_path.to_str().unwrap()
+                {
+                    return None;
+                }
+            }
+        }
+        // Return a pointer to the function.
+        Some(CodePtr(*func.deref() as *mut _))
+    }
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    /// Call the native host function, with supplied arguments.
+    /// Needs to convert all the arguments from their Miri representations to
+    /// a native form (through `libffi` call).
+    /// Then, convert the return value from the native form into something that
+    /// can be stored in Miri's internal memory.
+    fn call_native_fn(
+        &mut self,
+        link_name: Symbol,
+        dest: &MPlaceTy<'tcx, Provenance>,
+        args: &[OpTy<'tcx, Provenance>],
+    ) -> InterpResult<'tcx, bool> {
+        let this = self.eval_context_mut();
+        // Get the pointer to the function in the shared object file if it exists.
+        let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) {
+            Some(ptr) => ptr,
+            None => {
+                // Shared object file does not export this function -- try the shims next.
+                return Ok(false);
+            }
+        };
+
+        // Get the function arguments, and convert them to `libffi`-compatible form.
+        let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
+        for arg in args.iter() {
+            if !matches!(arg.layout.abi, Abi::Scalar(_)) {
+                throw_unsup_format!("only scalar argument types are support for native calls")
+            }
+            libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?);
+        }
+
+        // Convert them to `libffi::high::Arg` type.
+        let libffi_args = libffi_args
+            .iter()
+            .map(|arg| arg.arg_downcast())
+            .collect::<Vec<libffi::high::Arg<'_>>>();
+
+        // Call the function and store output, depending on return type in the function signature.
+        let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
+        this.write_immediate(*ret, dest)?;
+        Ok(true)
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Enum of supported arguments to external C functions.
+// We introduce this enum instead of just calling `ffi::arg` and storing a list
+// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference
+// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html
+// and we need to store a copy of the value, and pass a reference to this copy to C instead.
+enum CArg {
+    /// 8-bit signed integer.
+    Int8(i8),
+    /// 16-bit signed integer.
+    Int16(i16),
+    /// 32-bit signed integer.
+    Int32(i32),
+    /// 64-bit signed integer.
+    Int64(i64),
+    /// isize.
+    ISize(isize),
+    /// 8-bit unsigned integer.
+    UInt8(u8),
+    /// 16-bit unsigned integer.
+    UInt16(u16),
+    /// 32-bit unsigned integer.
+    UInt32(u32),
+    /// 64-bit unsigned integer.
+    UInt64(u64),
+    /// usize.
+    USize(usize),
+}
+
+impl<'a> CArg {
+    /// Convert a `CArg` to a `libffi` argument type.
+    fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
+        match self {
+            CArg::Int8(i) => ffi::arg(i),
+            CArg::Int16(i) => ffi::arg(i),
+            CArg::Int32(i) => ffi::arg(i),
+            CArg::Int64(i) => ffi::arg(i),
+            CArg::ISize(i) => ffi::arg(i),
+            CArg::UInt8(i) => ffi::arg(i),
+            CArg::UInt16(i) => ffi::arg(i),
+            CArg::UInt32(i) => ffi::arg(i),
+            CArg::UInt64(i) => ffi::arg(i),
+            CArg::USize(i) => ffi::arg(i),
+        }
+    }
+}
+
+/// Extract the scalar value from the result of reading a scalar from the machine,
+/// and convert it to a `CArg`.
+fn imm_to_carg<'tcx>(
+    v: ImmTy<'tcx, Provenance>,
+    cx: &impl HasDataLayout,
+) -> InterpResult<'tcx, CArg> {
+    Ok(match v.layout.ty.kind() {
+        // If the primitive provided can be converted to a type matching the type pattern
+        // then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
+        // the ints
+        ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
+        ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
+        ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
+        ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
+        ty::Int(IntTy::Isize) =>
+            CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
+        // the uints
+        ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
+        ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
+        ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
+        ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
+        ty::Uint(UintTy::Usize) =>
+            CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
+        _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
+    })
+}
diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs
index 9082d13..6fcfb69 100644
--- a/src/tools/miri/src/shims/unix/env.rs
+++ b/src/tools/miri/src/shims/unix/env.rs
@@ -228,7 +228,7 @@
 
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`getcwd`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(Pointer::null());
         }
 
@@ -241,7 +241,7 @@
                 let erange = this.eval_libc("ERANGE");
                 this.set_last_error(erange)?;
             }
-            Err(e) => this.set_last_error_from_io_error(e.kind())?,
+            Err(e) => this.set_last_error_from_io_error(e)?,
         }
 
         Ok(Pointer::null())
@@ -255,7 +255,7 @@
 
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`chdir`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
 
             return Ok(-1);
         }
@@ -263,7 +263,7 @@
         match env::set_current_dir(path) {
             Ok(()) => Ok(0),
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(-1)
             }
         }
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index e536b78..a53cd60 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -312,7 +312,7 @@
             // Reject if isolation is enabled.
             if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
                 this.reject_in_isolation("`fcntl`", reject_with)?;
-                this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+                this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
                 return Ok(-1);
             }
 
@@ -394,7 +394,7 @@
                 Ok(read_bytes)
             }
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(-1)
             }
         }
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index aaf4724..08f64e4 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -310,7 +310,7 @@
                         this.write_null(dest)?;
                     }
                     Some(len) => {
-                        let res = this.realloc(ptr, len, MiriMemoryKind::C)?;
+                        let res = this.realloc(ptr, len)?;
                         this.write_pointer(res, dest)?;
                     }
                 }
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 0587479..eb24155 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -137,37 +137,28 @@
         &mut self,
         file_type: std::io::Result<FileType>,
     ) -> InterpResult<'tcx, i32> {
+        #[cfg(unix)]
+        use std::os::unix::fs::FileTypeExt;
+
         let this = self.eval_context_mut();
         match file_type {
             Ok(file_type) => {
-                if file_type.is_dir() {
-                    Ok(this.eval_libc("DT_DIR").to_u8()?.into())
-                } else if file_type.is_file() {
-                    Ok(this.eval_libc("DT_REG").to_u8()?.into())
-                } else if file_type.is_symlink() {
-                    Ok(this.eval_libc("DT_LNK").to_u8()?.into())
-                } else {
+                match () {
+                    _ if file_type.is_dir() => Ok(this.eval_libc("DT_DIR").to_u8()?.into()),
+                    _ if file_type.is_file() => Ok(this.eval_libc("DT_REG").to_u8()?.into()),
+                    _ if file_type.is_symlink() => Ok(this.eval_libc("DT_LNK").to_u8()?.into()),
                     // Certain file types are only supported when the host is a Unix system.
-                    // (i.e. devices and sockets) If it is, check those cases, if not, fall back to
-                    // DT_UNKNOWN sooner.
-
                     #[cfg(unix)]
-                    {
-                        use std::os::unix::fs::FileTypeExt;
-                        if file_type.is_block_device() {
-                            Ok(this.eval_libc("DT_BLK").to_u8()?.into())
-                        } else if file_type.is_char_device() {
-                            Ok(this.eval_libc("DT_CHR").to_u8()?.into())
-                        } else if file_type.is_fifo() {
-                            Ok(this.eval_libc("DT_FIFO").to_u8()?.into())
-                        } else if file_type.is_socket() {
-                            Ok(this.eval_libc("DT_SOCK").to_u8()?.into())
-                        } else {
-                            Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into())
-                        }
-                    }
-                    #[cfg(not(unix))]
-                    Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into())
+                    _ if file_type.is_block_device() =>
+                        Ok(this.eval_libc("DT_BLK").to_u8()?.into()),
+                    #[cfg(unix)]
+                    _ if file_type.is_char_device() => Ok(this.eval_libc("DT_CHR").to_u8()?.into()),
+                    #[cfg(unix)]
+                    _ if file_type.is_fifo() => Ok(this.eval_libc("DT_FIFO").to_u8()?.into()),
+                    #[cfg(unix)]
+                    _ if file_type.is_socket() => Ok(this.eval_libc("DT_SOCK").to_u8()?.into()),
+                    // Fallback
+                    _ => Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()),
                 }
             }
             Err(e) =>
@@ -387,7 +378,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`open`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -443,7 +434,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`unlink`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -474,7 +465,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`symlink`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -775,7 +766,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rename`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -803,7 +794,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`mkdir`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -831,7 +822,7 @@
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rmdir`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(-1);
         }
 
@@ -868,7 +859,7 @@
                 Ok(Scalar::from_target_usize(id, this))
             }
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(Scalar::null_ptr(this))
             }
         }
@@ -956,7 +947,7 @@
                 None
             }
             Some(Err(e)) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 None
             }
         };
@@ -1308,13 +1299,12 @@
                 Ok(path_bytes.len().try_into().unwrap())
             }
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(-1)
             }
         }
     }
 
-    #[cfg_attr(not(unix), allow(unused))]
     fn isatty(
         &mut self,
         miri_fd: &OpTy<'tcx, Provenance>,
@@ -1392,7 +1382,7 @@
                 Ok(Scalar::from_maybe_pointer(dest, this))
             }
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(Scalar::from_target_usize(0, this))
             }
         }
@@ -1467,8 +1457,8 @@
         #[cfg(unix)]
         {
             use std::os::unix::fs::OpenOptionsExt;
-            fopts.mode(0o600);
             // Do not allow others to read or modify this file.
+            fopts.mode(0o600);
             fopts.custom_flags(libc::O_EXCL);
         }
         #[cfg(windows)]
@@ -1513,7 +1503,7 @@
                         _ => {
                             // "On error, -1 is returned, and errno is set to
                             // indicate the error"
-                            this.set_last_error_from_io_error(e.kind())?;
+                            this.set_last_error_from_io_error(e)?;
                             return Ok(-1);
                         }
                     },
@@ -1592,7 +1582,7 @@
         let metadata = match metadata {
             Ok(metadata) => metadata,
             Err(e) => {
-                ecx.set_last_error_from_io_error(e.kind())?;
+                ecx.set_last_error_from_io_error(e)?;
                 return Ok(None);
             }
         };
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index ecf82f2..7cd749a 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -117,6 +117,7 @@
                     // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                     // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                     id if id == sys_getrandom => {
+                        // Used by getrandom 0.1
                         // The first argument is the syscall id, so skip over it.
                         if args.len() < 4 {
                             throw_ub_format!(
@@ -124,7 +125,16 @@
                                 args.len()
                             );
                         }
-                        getrandom(this, &args[1], &args[2], &args[3], dest)?;
+
+                        let ptr = this.read_pointer(&args[1])?;
+                        let len = this.read_target_usize(&args[2])?;
+                        // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
+                        // neither of which have any effect on our current PRNG.
+                        // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
+                        let _flags = this.read_scalar(&args[3])?.to_i32();
+
+                        this.gen_random(ptr, len)?;
+                        this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
                     }
                     // `futex` is used by some synchronization primitives.
                     id if id == sys_futex => {
@@ -196,24 +206,3 @@
         Ok(EmulateItemResult::NeedsJumping)
     }
 }
-
-// Shims the linux `getrandom` syscall.
-fn getrandom<'tcx>(
-    this: &mut MiriInterpCx<'_, 'tcx>,
-    ptr: &OpTy<'tcx, Provenance>,
-    len: &OpTy<'tcx, Provenance>,
-    flags: &OpTy<'tcx, Provenance>,
-    dest: &MPlaceTy<'tcx, Provenance>,
-) -> InterpResult<'tcx> {
-    let ptr = this.read_pointer(ptr)?;
-    let len = this.read_target_usize(len)?;
-
-    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
-    // neither of which have any effect on our current PRNG.
-    // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
-    let _flags = this.read_scalar(flags)?.to_i32();
-
-    this.gen_random(ptr, len)?;
-    this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
-    Ok(())
-}
diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs
index f52dc23..0254735 100644
--- a/src/tools/miri/src/shims/unix/mem.rs
+++ b/src/tools/miri/src/shims/unix/mem.rs
@@ -42,9 +42,12 @@
         let map_shared = this.eval_libc_i32("MAP_SHARED");
         let map_fixed = this.eval_libc_i32("MAP_FIXED");
 
-        // This is a horrible hack, but on MacOS the guard page mechanism uses mmap
+        // This is a horrible hack, but on MacOS and Solaris the guard page mechanism uses mmap
         // in a way we do not support. We just give it the return value it expects.
-        if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 {
+        if this.frame_in_std()
+            && matches!(&*this.tcx.sess.target.os, "macos" | "solaris")
+            && (flags & map_fixed) != 0
+        {
             return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this));
         }
 
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index c01ae0e..c216d8a 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -2,7 +2,6 @@
 use rustc_target::spec::abi::Abi;
 
 use crate::*;
-use shims::EmulateItemResult;
 
 pub fn is_dyn_sym(_name: &str) -> bool {
     false
@@ -26,6 +25,24 @@
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
+            "stack_getbounds" => {
+                let [stack] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
+
+                this.write_int_fields_named(
+                    &[
+                        ("ss_sp", this.machine.stack_addr.into()),
+                        ("ss_size", this.machine.stack_size.into()),
+                        // field set to 0 means not in an alternate signal stack
+                        // https://docs.oracle.com/cd/E86824_01/html/E54766/stack-getbounds-3c.html
+                        ("ss_flags", 0),
+                    ],
+                    &stack,
+                )?;
+
+                this.write_null(dest)?;
+            }
+
             _ => return Ok(EmulateItemResult::NotSupported),
         }
         Ok(EmulateItemResult::NeedsJumping)
diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs
index b3bc5d4..0e52959 100644
--- a/src/tools/miri/src/shims/windows/env.rs
+++ b/src/tools/miri/src/shims/windows/env.rs
@@ -153,7 +153,7 @@
 
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
             return Ok(Scalar::from_u32(0));
         }
 
@@ -166,7 +166,7 @@
                     this.write_path_to_wide_str(&cwd, buf, size)?,
                 )));
             }
-            Err(e) => this.set_last_error_from_io_error(e.kind())?,
+            Err(e) => this.set_last_error_from_io_error(e)?,
         }
         Ok(Scalar::from_u32(0))
     }
@@ -185,7 +185,7 @@
 
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+            this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
 
             return Ok(this.eval_windows("c", "FALSE"));
         }
@@ -193,7 +193,7 @@
         match env::set_current_dir(path) {
             Ok(()) => Ok(this.eval_windows("c", "TRUE")),
             Err(e) => {
-                this.set_last_error_from_io_error(e.kind())?;
+                this.set_last_error_from_io_error(e)?;
                 Ok(this.eval_windows("c", "FALSE"))
             }
         }
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index dba5b7a..086abf1 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -5,10 +5,9 @@
 use std::str;
 
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
+use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi;
 
-use crate::shims::alloc::EvalContextExt as _;
 use crate::shims::os_str::bytes_to_os_str;
 use crate::shims::windows::*;
 use crate::*;
@@ -225,7 +224,7 @@
                 let filename = this.read_path_from_wide_str(filename)?;
                 let result = match win_absolute(&filename)? {
                     Err(err) => {
-                        this.set_last_error_from_io_error(err.kind())?;
+                        this.set_last_error_from_io_error(err)?;
                         Scalar::from_u32(0) // return zero upon failure
                     }
                     Ok(abs_filename) => {
@@ -248,8 +247,21 @@
                 let size = this.read_target_usize(size)?;
                 let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY
                 let zero_init = (flags & heap_zero_memory) == heap_zero_memory;
-                let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
-                this.write_pointer(res, dest)?;
+                // Alignment is twice the pointer size.
+                // Source: <https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc>
+                let align = this.tcx.pointer_size().bytes().strict_mul(2);
+                let ptr = this.allocate_ptr(
+                    Size::from_bytes(size),
+                    Align::from_bytes(align).unwrap(),
+                    MiriMemoryKind::WinHeap.into(),
+                )?;
+                if zero_init {
+                    this.write_bytes_ptr(
+                        ptr.into(),
+                        iter::repeat(0u8).take(usize::try_from(size).unwrap()),
+                    )?;
+                }
+                this.write_pointer(ptr, dest)?;
             }
             "HeapFree" => {
                 let [handle, flags, ptr] =
@@ -257,23 +269,41 @@
                 this.read_target_isize(handle)?;
                 this.read_scalar(flags)?.to_u32()?;
                 let ptr = this.read_pointer(ptr)?;
-                this.free(ptr, MiriMemoryKind::WinHeap)?;
+                // "This pointer can be NULL." It doesn't say what happens then, but presumably nothing.
+                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree)
+                if !this.ptr_is_null(ptr)? {
+                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinHeap.into())?;
+                }
                 this.write_scalar(Scalar::from_i32(1), dest)?;
             }
             "HeapReAlloc" => {
-                let [handle, flags, ptr, size] =
+                let [handle, flags, old_ptr, size] =
                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(handle)?;
                 this.read_scalar(flags)?.to_u32()?;
-                let ptr = this.read_pointer(ptr)?;
+                let old_ptr = this.read_pointer(old_ptr)?;
                 let size = this.read_target_usize(size)?;
-                let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
-                this.write_pointer(res, dest)?;
+                let align = this.tcx.pointer_size().bytes().strict_mul(2); // same as above
+                // The docs say that `old_ptr` must come from an earlier HeapAlloc or HeapReAlloc,
+                // so unlike C `realloc` we do *not* allow a NULL here.
+                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc)
+                let new_ptr = this.reallocate_ptr(
+                    old_ptr,
+                    None,
+                    Size::from_bytes(size),
+                    Align::from_bytes(align).unwrap(),
+                    MiriMemoryKind::WinHeap.into(),
+                )?;
+                this.write_pointer(new_ptr, dest)?;
             }
             "LocalFree" => {
                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
-                this.free(ptr, MiriMemoryKind::WinLocal)?;
+                // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
+                // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
+                if !this.ptr_is_null(ptr)? {
+                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinLocal.into())?;
+                }
                 this.write_null(dest)?;
             }
 
@@ -513,6 +543,7 @@
                 throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
             }
             "SystemFunction036" => {
+                // used by getrandom 0.1
                 // This is really 'RtlGenRandom'.
                 let [ptr, len] =
                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
@@ -522,6 +553,7 @@
                 this.write_scalar(Scalar::from_bool(true), dest)?;
             }
             "ProcessPrng" => {
+                // used by `std`
                 let [ptr, len] =
                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
@@ -530,6 +562,7 @@
                 this.write_scalar(Scalar::from_i32(1), dest)?;
             }
             "BCryptGenRandom" => {
+                // used by getrandom 0.2
                 let [algorithm, ptr, len, flags] =
                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
                 let algorithm = this.read_scalar(algorithm)?;
diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py
index 2639d29..83f3e4c 100755
--- a/src/tools/miri/test-cargo-miri/run-test.py
+++ b/src/tools/miri/test-cargo-miri/run-test.py
@@ -10,6 +10,7 @@
 import re
 import subprocess
 import sys
+import argparse
 
 CGREEN  = '\33[32m'
 CBOLD   = '\33[1m'
@@ -25,8 +26,8 @@
     args = ["cargo", "miri", cmd] + CARGO_EXTRA_FLAGS
     if quiet:
         args += ["-q"]
-    if 'MIRI_TEST_TARGET' in os.environ:
-        args += ["--target", os.environ['MIRI_TEST_TARGET']]
+    if ARGS.target:
+        args += ["--target", ARGS.target]
     return args
 
 def normalize_stdout(str):
@@ -35,7 +36,7 @@
     return str
 
 def check_output(actual, path, name):
-    if os.environ.get("RUSTC_BLESS", "0") != "0":
+    if ARGS.bless:
         # Write the output only if bless is set
         open(path, mode='w').write(actual)
         return True
@@ -133,7 +134,7 @@
 
 def test_cargo_miri_test():
     # rustdoc is not run on foreign targets
-    is_foreign = 'MIRI_TEST_TARGET' in os.environ
+    is_foreign = ARGS.target is not None
     default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref"
     filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref"
 
@@ -182,16 +183,22 @@
         env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
     )
 
+args_parser = argparse.ArgumentParser(description='`cargo miri` testing')
+args_parser.add_argument('--target', help='the target to test')
+args_parser.add_argument('--bless', help='bless the reference files', action='store_true')
+ARGS = args_parser.parse_args()
+
 os.chdir(os.path.dirname(os.path.realpath(__file__)))
 os.environ["CARGO_TARGET_DIR"] = "target" # this affects the location of the target directory that we need to check
 os.environ["RUST_TEST_NOCAPTURE"] = "0" # this affects test output, so make sure it is not set
 os.environ["RUST_TEST_THREADS"] = "1" # avoid non-deterministic output due to concurrent test runs
 
-target_str = " for target {}".format(os.environ['MIRI_TEST_TARGET']) if 'MIRI_TEST_TARGET' in os.environ else ""
+target_str = " for target {}".format(ARGS.target) if ARGS.target else ""
 print(CGREEN + CBOLD + "## Running `cargo miri` tests{}".format(target_str) + CEND)
 
 test_cargo_miri_run()
 test_cargo_miri_test()
+
 # Ensure we did not create anything outside the expected target dir.
 for target_dir in ["target", "custom-run", "custom-test", "config-cli"]:
     if os.listdir(target_dir) != ["miri"]:
diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock
index 3dd5eb9..c73d13a 100644
--- a/src/tools/miri/test_dependencies/Cargo.lock
+++ b/src/tools/miri/test_dependencies/Cargo.lock
@@ -18,12 +18,6 @@
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
-name = "autocfg"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
-
-[[package]]
 name = "backtrace"
 version = "0.3.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -51,12 +45,6 @@
 checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
-name = "bytes"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
-
-[[package]]
 name = "cc"
 version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -142,16 +130,6 @@
 checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
 name = "log"
 version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -192,7 +170,6 @@
  "libc",
  "num_cpus",
  "page_size",
- "rand",
  "tempfile",
  "tokio",
  "windows-sys 0.52.0",
@@ -234,41 +211,12 @@
 ]
 
 [[package]]
-name = "parking_lot"
-version = "0.12.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets 0.52.5",
-]
-
-[[package]]
 name = "pin-project-lite"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
 
 [[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[package]]
 name = "proc-macro2"
 version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -287,45 +235,6 @@
 ]
 
 [[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.14",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
 name = "rustc-demangle"
 version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -345,27 +254,6 @@
 ]
 
 [[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-
-[[package]]
 name = "socket2"
 version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -405,13 +293,10 @@
 checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
 dependencies = [
  "backtrace",
- "bytes",
  "libc",
  "mio",
  "num_cpus",
- "parking_lot",
  "pin-project-lite",
- "signal-hook-registry",
  "socket2",
  "tokio-macros",
  "windows-sys 0.48.0",
diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml
index 9f28e4d..1894f53 100644
--- a/src/tools/miri/test_dependencies/Cargo.toml
+++ b/src/tools/miri/test_dependencies/Cargo.toml
@@ -15,11 +15,10 @@
 
 getrandom_01 = { package = "getrandom", version = "0.1" }
 getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }
-rand = { version = "0.8", features = ["small_rng"] }
 
 [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
 page_size = "0.6"
-tokio = { version = "1.24", features = ["full"] }
+tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] }
 
 [target.'cfg(windows)'.dependencies]
 windows-sys = { version = "0.52", features = [ "Win32_Foundation", "Win32_System_Threading" ] }
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
index 94ca349..f22f17b 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 /// Test that destroying a pthread_cond twice fails, even without a check for number validity
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
index b5427d55..d0ccab4 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@ignore-target-apple: Our macOS condattr don't have any fields so we do not notice this.
 
 /// Test that destroying a pthread_condattr twice fails, even without a check for number validity
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs
index 7e6f490..9cd0a35 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@error-in-other-file: the main thread terminated without waiting for all remaining threads
 
 // Check that we terminate the program when the main thread terminates.
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs
index e1d3704..96dd99e 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 //! The thread function must have exactly one argument.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs
index 7408634..d8fbc68 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 //! The thread function must have exactly one argument.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs
index 0b810dc..e89d7a9 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 // Joining a detached thread is undefined behavior.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs
index 04ca4bb..cbad743 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 // Joining an already joined thread is undefined behavior.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs
index 7576518..002498e 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 // Joining the main thread is undefined behavior.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs
index 966f416..f5b687a 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 // Joining the same thread from multiple threads is undefined behavior.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs
index 0c25c69..4bc1c82 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 // We are making scheduler assumptions here.
 //@compile-flags: -Zmiri-preemption-rate=0
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs
similarity index 90%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs
index 8b25107..0a494c5 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //
 // Check that if we pass NULL attribute, then we get the default mutex type.
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs
index 60d56d4..0328115 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs
similarity index 91%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs
index f443768..1038b89 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //
 // Check that if we do not set the mutex type, it is the default.
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs
similarity index 91%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs
index ec3965c..e474712 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     unsafe {
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
index 622c3ea..46f0c5f 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 /// Test that destroying a pthread_mutex twice fails, even without a check for number validity
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs
index 5ea09fa..f311934 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     unsafe {
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs
similarity index 92%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs
index 8ce7542..2b5886d 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     unsafe {
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs
index b567752..686a394 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
similarity index 91%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
index 474a277..51f00d6 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs
similarity index 83%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs
index 603580f..fa4575b 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs
similarity index 83%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs
index ae44f22..e734a62 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
similarity index 88%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
index 800986f..e96f7fc 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs
similarity index 82%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs
index 782c95b..dffeee2 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs
index 1b498ad..328372b 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs
similarity index 78%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs
index 05f7e7a..ced6b7a 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs
index 0f02c32..4174751 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs
similarity index 82%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs
index 538f14e..099b8dc 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs
index 10be5b3..43b3ab0 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs
similarity index 82%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs
index 2c963d3..2704ff1 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 fn main() {
     let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs
index dd09947..9a2cd09 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr
rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs
similarity index 89%
rename from src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs
rename to src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs
index 2b9e7a3..a1895fe 100644
--- a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs
+++ b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc env support on Windows
 
 use std::env;
 use std::thread;
diff --git a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.stderr b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.stderr
rename to src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs
similarity index 82%
rename from src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs
index 09da850..42b7e2b 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc IO on Windows
 //@compile-flags: -Zmiri-disable-isolation
 
 // FIXME: standard handles cannot be closed (https://github.com/rust-lang/rust/issues/40032)
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.stderr b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/close_stdout.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs
similarity index 83%
rename from src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs
index a45f805..3c62015 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc IO on Windows
 
 fn main() -> std::io::Result<()> {
     let mut bytes = [0u8; 512];
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.stderr b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs
similarity index 86%
rename from src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs
index ba9f404..6d951a3 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No mkstemp on Windows
 //@compile-flags: -Zmiri-disable-isolation
 
 fn main() {
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs
similarity index 83%
rename from src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs
index 073fca4..624f584 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc IO on Windows
 
 fn main() -> std::io::Result<()> {
     let mut bytes = [0u8; 512];
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.stderr b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs
similarity index 89%
rename from src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs
index ae231d4..d783967 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc IO on Windows
 //@compile-flags: -Zmiri-disable-isolation
 
 fn main() {
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs
similarity index 79%
rename from src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs
rename to src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs
index d039ad7..683c55e 100644
--- a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs
+++ b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc IO on Windows
 
 fn main() -> std::io::Result<()> {
     let bytes = b"hello";
diff --git a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.stderr b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.stderr
rename to src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.stderr
diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs
new file mode 100644
index 0000000..3298d61
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs
@@ -0,0 +1,7 @@
+fn main() {
+    unsafe {
+        let ptr = libc::malloc(0);
+        libc::free(ptr);
+        libc::free(ptr); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr
new file mode 100644
index 0000000..6437c9d
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
+  --> $DIR/malloc_zero_double_free.rs:LL:CC
+   |
+LL |         libc::free(ptr);
+   |         ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> $DIR/malloc_zero_double_free.rs:LL:CC
+   |
+LL |         let ptr = libc::malloc(0);
+   |                   ^^^^^^^^^^^^^^^
+help: ALLOC was deallocated here:
+  --> $DIR/malloc_zero_double_free.rs:LL:CC
+   |
+LL |         libc::free(ptr);
+   |         ^^^^^^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at $DIR/malloc_zero_double_free.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs
new file mode 100644
index 0000000..b09a74f
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs
@@ -0,0 +1,5 @@
+fn main() {
+    unsafe {
+        let _ptr = libc::malloc(0); //~ERROR: memory leak
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr
new file mode 100644
index 0000000..65ce0dc
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr
@@ -0,0 +1,15 @@
+error: memory leaked: ALLOC (C heap, size: 0, align: 1), allocated here:
+  --> $DIR/malloc_zero_memory_leak.rs:LL:CC
+   |
+LL |         let _ptr = libc::malloc(0);
+   |                    ^^^^^^^^^^^^^^^
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/malloc_zero_memory_leak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/shims/memchr_null.rs b/src/tools/miri/tests/fail-dep/libc/memchr_null.rs
similarity index 77%
rename from src/tools/miri/tests/fail-dep/shims/memchr_null.rs
rename to src/tools/miri/tests/fail-dep/libc/memchr_null.rs
index 6bc7af7..672cc10 100644
--- a/src/tools/miri/tests/fail-dep/shims/memchr_null.rs
+++ b/src/tools/miri/tests/fail-dep/libc/memchr_null.rs
@@ -1,5 +1,3 @@
-//@ignore-target-windows: No libc on Windows
-
 use std::ptr;
 
 // null is explicitly called out as UB in the C docs.
diff --git a/src/tools/miri/tests/fail-dep/shims/memchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memchr_null.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/memchr_null.stderr
rename to src/tools/miri/tests/fail-dep/libc/memchr_null.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_null.rs b/src/tools/miri/tests/fail-dep/libc/memcmp_null.rs
similarity index 78%
rename from src/tools/miri/tests/fail-dep/shims/memcmp_null.rs
rename to src/tools/miri/tests/fail-dep/libc/memcmp_null.rs
index a4e0034..066af4a 100644
--- a/src/tools/miri/tests/fail-dep/shims/memcmp_null.rs
+++ b/src/tools/miri/tests/fail-dep/libc/memcmp_null.rs
@@ -1,5 +1,3 @@
-//@ignore-target-windows: No libc on Windows
-
 use std::ptr;
 
 // null is explicitly called out as UB in the C docs.
diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_null.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/memcmp_null.stderr
rename to src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs
similarity index 90%
rename from src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs
rename to src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs
index f2ddc20..e28aa26 100644
--- a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs
+++ b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs
@@ -1,4 +1,3 @@
-//@ignore-target-windows: No libc on Windows
 //@compile-flags: -Zmiri-permissive-provenance
 
 // C says that passing "invalid" pointers is UB for all string functions.
diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/memcmp_zero.stderr
rename to src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs
similarity index 88%
rename from src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs
rename to src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs
index 5283fea..b37ed7d 100644
--- a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs
+++ b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs
@@ -1,4 +1,3 @@
-//@ignore-target-windows: No libc on Windows
 //@compile-flags: -Zmiri-permissive-provenance
 // C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`.
 
diff --git a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/memcpy_zero.stderr
rename to src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/memrchr_null.rs b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs
similarity index 81%
rename from src/tools/miri/tests/fail-dep/shims/memrchr_null.rs
rename to src/tools/miri/tests/fail-dep/libc/memrchr_null.rs
index b6707d5..f06336b 100644
--- a/src/tools/miri/tests/fail-dep/shims/memrchr_null.rs
+++ b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No `memrchr` on Windows
 //@ignore-target-apple: No `memrchr` on some apple targets
 
 use std::ptr;
diff --git a/src/tools/miri/tests/fail-dep/shims/memrchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/memrchr_null.stderr
rename to src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs
similarity index 90%
rename from src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs
rename to src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs
index 70f7a6a..9d55a49 100644
--- a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs
+++ b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No mmap on Windows
 
 #![feature(rustc_private)]
 
diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.stderr b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.stderr
rename to src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs
similarity index 90%
rename from src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs
rename to src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs
index c97b013..05ac232 100644
--- a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs
+++ b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No mmap on Windows
 
 #![feature(rustc_private)]
 
diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr
rename to src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.stderr
diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs b/src/tools/miri/tests/fail-dep/libc/munmap_partial.rs
similarity index 93%
rename from src/tools/miri/tests/fail-dep/shims/munmap_partial.rs
rename to src/tools/miri/tests/fail-dep/libc/munmap_partial.rs
index d7aef47..4386dc7 100644
--- a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs
+++ b/src/tools/miri/tests/fail-dep/libc/munmap_partial.rs
@@ -1,7 +1,7 @@
 //! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped
 //! region of address space, but to LLVM that would be partial deallocation, which LLVM does not
 //! support. So even though the man pages say this sort of use is possible, we must report UB.
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No mmap on Windows
 //@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment"
 
 fn main() {
diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr b/src/tools/miri/tests/fail-dep/libc/munmap_partial.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr
rename to src/tools/miri/tests/fail-dep/libc/munmap_partial.stderr
diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.rs b/src/tools/miri/tests/fail-dep/libc/realloc-zero.rs
similarity index 81%
rename from src/tools/miri/tests/fail-dep/realloc-zero.rs
rename to src/tools/miri/tests/fail-dep/libc/realloc-zero.rs
index 1482798..0e210f3 100644
--- a/src/tools/miri/tests/fail-dep/realloc-zero.rs
+++ b/src/tools/miri/tests/fail-dep/libc/realloc-zero.rs
@@ -1,5 +1,3 @@
-//@ignore-target-windows: No libc on Windows
-
 fn main() {
     unsafe {
         let p1 = libc::malloc(20);
diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.stderr b/src/tools/miri/tests/fail-dep/libc/realloc-zero.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/realloc-zero.stderr
rename to src/tools/miri/tests/fail-dep/libc/realloc-zero.stderr
diff --git a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs
similarity index 87%
rename from src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs
rename to src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs
index 6ef842c..cd8422f 100644
--- a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs
+++ b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs
@@ -1,6 +1,6 @@
 //! `signal()` is special on Linux and macOS that it's only supported within libstd.
 //! The implementation is not complete enough to permit user code to call it.
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No `libc::signal` on Windows
 //@normalize-stderr-test: "OS `.*`" -> "$$OS"
 
 fn main() {
diff --git a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.stderr b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr
similarity index 100%
rename from src/tools/miri/tests/fail-dep/unsupported_incomplete_function.stderr
rename to src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr
diff --git a/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs b/src/tools/miri/tests/native-lib/fail/function_not_in_so.rs
similarity index 100%
rename from src/tools/miri/tests/extern-so/fail/function_not_in_so.rs
rename to src/tools/miri/tests/native-lib/fail/function_not_in_so.rs
diff --git a/src/tools/miri/tests/extern-so/fail/function_not_in_so.stderr b/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr
similarity index 100%
rename from src/tools/miri/tests/extern-so/fail/function_not_in_so.stderr
rename to src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr
diff --git a/src/tools/miri/tests/extern-so/libtest.map b/src/tools/miri/tests/native-lib/libtest.map
similarity index 100%
rename from src/tools/miri/tests/extern-so/libtest.map
rename to src/tools/miri/tests/native-lib/libtest.map
diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs b/src/tools/miri/tests/native-lib/pass/call_extern_c_fn.rs
similarity index 100%
rename from src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs
rename to src/tools/miri/tests/native-lib/pass/call_extern_c_fn.rs
diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.stdout b/src/tools/miri/tests/native-lib/pass/call_extern_c_fn.stdout
similarity index 100%
rename from src/tools/miri/tests/extern-so/pass/call_extern_c_fn.stdout
rename to src/tools/miri/tests/native-lib/pass/call_extern_c_fn.stdout
diff --git a/src/tools/miri/tests/extern-so/test.c b/src/tools/miri/tests/native-lib/test.c
similarity index 100%
rename from src/tools/miri/tests/extern-so/test.c
rename to src/tools/miri/tests/native-lib/test.c
diff --git a/src/tools/miri/tests/panic/unsupported_syscall.rs b/src/tools/miri/tests/panic/unsupported_syscall.rs
index 31d666e..30f9da5 100644
--- a/src/tools/miri/tests/panic/unsupported_syscall.rs
+++ b/src/tools/miri/tests/panic/unsupported_syscall.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: no `syscall` on Windows
 //@ignore-target-apple: `syscall` is not supported on macOS
 //@compile-flags: -Zmiri-panic-on-unsupported
 
diff --git a/src/tools/miri/tests/pass-dep/calloc.rs b/src/tools/miri/tests/pass-dep/calloc.rs
deleted file mode 100644
index 62ab63c..0000000
--- a/src/tools/miri/tests/pass-dep/calloc.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-//@ignore-target-windows: No libc on Windows
-
-use core::slice;
-
-fn main() {
-    unsafe {
-        let p1 = libc::calloc(0, 0);
-        assert!(p1.is_null());
-
-        let p2 = libc::calloc(20, 0);
-        assert!(p2.is_null());
-
-        let p3 = libc::calloc(0, 20);
-        assert!(p3.is_null());
-
-        let p4 = libc::calloc(4, 8);
-        assert!(!p4.is_null());
-        let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8);
-        assert_eq!(&slice, &[0_u8; 4 * 8]);
-        libc::free(p4);
-    }
-}
diff --git a/src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs
similarity index 92%
rename from src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs
rename to src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs
index 5c29a1b..86a47ba 100644
--- a/src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs
+++ b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No libc env support on Windows
 
 use std::ffi::CStr;
 use std::thread;
diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs
index f362caa..d758168 100644
--- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs
+++ b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS.
 //@compile-flags: -Zmiri-disable-isolation
 
diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs
index 66c0895..f1a3c5d 100644
--- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs
+++ b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS.
 
 /// Test that conditional variable timeouts are working properly
diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs
index ae87474..0eaab96 100644
--- a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs
+++ b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 //! Test that pthread_key destructors are run in the right order.
 //! Note that these are *not* used by actual `thread_local!` on Linux! Those use
 //! `thread_local_dtor::register_dtor` from the stdlib instead. In Miri this hits the fallback path
diff --git a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs
index 716119a..1198168 100644
--- a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs
+++ b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No `dlsym` on Windows
 //@compile-flags: -Zmiri-permissive-provenance
 
 #[path = "../utils/mod.rs"]
diff --git a/src/tools/miri/tests/pass-dep/getrandom.rs b/src/tools/miri/tests/pass-dep/getrandom.rs
index c0d9296..53de3af 100644
--- a/src/tools/miri/tests/pass-dep/getrandom.rs
+++ b/src/tools/miri/tests/pass-dep/getrandom.rs
@@ -1,8 +1,9 @@
 // mac-os `getrandom_01` does some pointer shenanigans
 //@compile-flags: -Zmiri-permissive-provenance
+//@revisions: isolation no_isolation
+//@[no_isolation]compile-flags: -Zmiri-disable-isolation
 
 /// Test direct calls of getrandom 0.1 and 0.2.
-/// Make sure they work even with isolation enabled (i.e., we do not hit a file-based fallback path).
 fn main() {
     let mut data = vec![0; 16];
     getrandom_01::getrandom(&mut data).unwrap();
diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs b/src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs
rename to src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs
diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr b/src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr
rename to src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs
new file mode 100644
index 0000000..d72edd7
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs
@@ -0,0 +1,51 @@
+// Symlink tests are separate since they don't in general work on a Windows host.
+//@ignore-host-windows: creating symlinks requires admin permissions on Windows
+//@ignore-target-windows: File handling is not implemented yet
+//@compile-flags: -Zmiri-disable-isolation
+
+use std::ffi::CString;
+use std::io::{Error, ErrorKind};
+use std::os::unix::ffi::OsStrExt;
+
+#[path = "../../utils/mod.rs"]
+mod utils;
+
+fn main() {
+    let bytes = b"Hello, World!\n";
+    let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes);
+    let expected_path = path.as_os_str().as_bytes();
+
+    let symlink_path = utils::prepare("miri_test_fs_symlink.txt");
+    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
+
+    // Test that the expected string gets written to a buffer of proper
+    // length, and that a trailing null byte is not written.
+    let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
+    let symlink_c_ptr = symlink_c_str.as_ptr();
+
+    // Make the buf one byte larger than it needs to be,
+    // and check that the last byte is not overwritten.
+    let mut large_buf = vec![0xFF; expected_path.len() + 1];
+    let res =
+        unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
+    // Check that the resolved path was properly written into the buf.
+    assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
+    assert_eq!(large_buf.last(), Some(&0xFF));
+    assert_eq!(res, large_buf.len() as isize - 1);
+
+    // Test that the resolved path is truncated if the provided buffer
+    // is too small.
+    let mut small_buf = [0u8; 2];
+    let res =
+        unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
+    assert_eq!(small_buf, &expected_path[..small_buf.len()]);
+    assert_eq!(res, small_buf.len() as isize);
+
+    // Test that we report a proper error for a missing path.
+    let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
+    let res = unsafe {
+        libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
+    };
+    assert_eq!(res, -1);
+    assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
+}
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
similarity index 93%
rename from src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
rename to src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
index 5185db0..088a632 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: no libc on Windows
+//@ignore-target-windows: File handling is not implemented yet
 //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
 //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"
 
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.stderr
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr
rename to src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.stderr
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs
similarity index 78%
rename from src/tools/miri/tests/pass-dep/shims/libc-fs.rs
rename to src/tools/miri/tests/pass-dep/libc/libc-fs.rs
index 0dd849a..80c9757 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs
@@ -1,11 +1,11 @@
-//@ignore-target-windows: no libc on Windows
+//@ignore-target-windows: File handling is not implemented yet
 //@compile-flags: -Zmiri-disable-isolation
 
 #![feature(io_error_more)]
 #![feature(io_error_uncategorized)]
 
 use std::ffi::{CStr, CString, OsString};
-use std::fs::{canonicalize, remove_dir_all, remove_file, File};
+use std::fs::{canonicalize, remove_file, File};
 use std::io::{Error, ErrorKind, Write};
 use std::os::unix::ffi::OsStrExt;
 use std::os::unix::io::AsRawFd;
@@ -21,7 +21,6 @@
     test_ftruncate::<libc::off_t>(libc::ftruncate);
     #[cfg(target_os = "linux")]
     test_ftruncate::<libc::off64_t>(libc::ftruncate64);
-    test_readlink();
     test_file_open_unix_allow_two_args();
     test_file_open_unix_needs_three_args();
     test_file_open_unix_extra_third_arg();
@@ -38,33 +37,8 @@
     test_isatty();
 }
 
-/// Prepare: compute filename and make sure the file does not exist.
-fn prepare(filename: &str) -> PathBuf {
-    let path = utils::tmp().join(filename);
-    // Clean the paths for robustness.
-    remove_file(&path).ok();
-    path
-}
-
-/// Prepare directory: compute directory name and make sure it does not exist.
-#[allow(unused)]
-fn prepare_dir(dirname: &str) -> PathBuf {
-    let path = utils::tmp().join(&dirname);
-    // Clean the directory for robustness.
-    remove_dir_all(&path).ok();
-    path
-}
-
-/// Prepare like above, and also write some initial content to the file.
-fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
-    let path = prepare(filename);
-    let mut file = File::create(&path).unwrap();
-    file.write(content).unwrap();
-    path
-}
-
 fn test_file_open_unix_allow_two_args() {
-    let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);
+    let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);
 
     let mut name = path.into_os_string();
     name.push("\0");
@@ -73,7 +47,7 @@
 }
 
 fn test_file_open_unix_needs_three_args() {
-    let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);
+    let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);
 
     let mut name = path.into_os_string();
     name.push("\0");
@@ -82,7 +56,7 @@
 }
 
 fn test_file_open_unix_extra_third_arg() {
-    let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);
+    let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);
 
     let mut name = path.into_os_string();
     name.push("\0");
@@ -106,49 +80,9 @@
     assert!(canonicalize(too_long).is_err());
 }
 
-fn test_readlink() {
-    let bytes = b"Hello, World!\n";
-    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
-    let expected_path = path.as_os_str().as_bytes();
-
-    let symlink_path = prepare("miri_test_fs_symlink.txt");
-    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
-
-    // Test that the expected string gets written to a buffer of proper
-    // length, and that a trailing null byte is not written.
-    let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
-    let symlink_c_ptr = symlink_c_str.as_ptr();
-
-    // Make the buf one byte larger than it needs to be,
-    // and check that the last byte is not overwritten.
-    let mut large_buf = vec![0xFF; expected_path.len() + 1];
-    let res =
-        unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
-    // Check that the resolved path was properly written into the buf.
-    assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
-    assert_eq!(large_buf.last(), Some(&0xFF));
-    assert_eq!(res, large_buf.len() as isize - 1);
-
-    // Test that the resolved path is truncated if the provided buffer
-    // is too small.
-    let mut small_buf = [0u8; 2];
-    let res =
-        unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
-    assert_eq!(small_buf, &expected_path[..small_buf.len()]);
-    assert_eq!(res, small_buf.len() as isize);
-
-    // Test that we report a proper error for a missing path.
-    let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
-    let res = unsafe {
-        libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
-    };
-    assert_eq!(res, -1);
-    assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
-}
-
 fn test_rename() {
-    let path1 = prepare("miri_test_libc_fs_source.txt");
-    let path2 = prepare("miri_test_libc_fs_rename_destination.txt");
+    let path1 = utils::prepare("miri_test_libc_fs_source.txt");
+    let path2 = utils::prepare("miri_test_libc_fs_rename_destination.txt");
 
     let file = File::create(&path1).unwrap();
     drop(file);
@@ -178,7 +112,7 @@
     // https://docs.rs/libc/latest/i686-unknown-linux-gnu/libc/type.off_t.html
 
     let bytes = b"hello";
-    let path = prepare("miri_test_libc_fs_ftruncate.txt");
+    let path = utils::prepare("miri_test_libc_fs_ftruncate.txt");
     let mut file = File::create(&path).unwrap();
     file.write(bytes).unwrap();
     file.sync_all().unwrap();
@@ -209,7 +143,7 @@
 fn test_o_tmpfile_flag() {
     use std::fs::{create_dir, OpenOptions};
     use std::os::unix::fs::OpenOptionsExt;
-    let dir_path = prepare_dir("miri_test_fs_dir");
+    let dir_path = utils::prepare_dir("miri_test_fs_dir");
     create_dir(&dir_path).unwrap();
     // test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution
     assert_eq!(
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.stderr b/src/tools/miri/tests/pass-dep/libc/libc-fs.stderr
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/libc-fs.stderr
rename to src/tools/miri/tests/pass-dep/libc/libc-fs.stderr
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.stdout b/src/tools/miri/tests/pass-dep/libc/libc-fs.stdout
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/libc-fs.stdout
rename to src/tools/miri/tests/pass-dep/libc/libc-fs.stdout
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
similarity index 70%
rename from src/tools/miri/tests/pass-dep/shims/libc-misc.rs
rename to src/tools/miri/tests/pass-dep/libc/libc-mem.rs
index 9644920..5df3ace 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
@@ -1,33 +1,5 @@
-//@ignore-target-windows: No libc on Windows
-//@compile-flags: -Zmiri-disable-isolation
-#![feature(io_error_more)]
-#![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
-
-use std::mem::{self, transmute};
-use std::ptr;
-
-/// Tests whether each thread has its own `__errno_location`.
-fn test_thread_local_errno() {
-    #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-    use libc::___errno as __errno_location;
-    #[cfg(target_os = "linux")]
-    use libc::__errno_location;
-    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
-    use libc::__error as __errno_location;
-
-    unsafe {
-        *__errno_location() = 0xBEEF;
-        std::thread::spawn(|| {
-            assert_eq!(*__errno_location(), 0);
-            *__errno_location() = 0xBAD1DEA;
-            assert_eq!(*__errno_location(), 0xBAD1DEA);
-        })
-        .join()
-        .unwrap();
-        assert_eq!(*__errno_location(), 0xBEEF);
-    }
-}
+#![feature(strict_provenance, pointer_is_aligned_to)]
+use std::{mem, ptr, slice};
 
 fn test_memcpy() {
     unsafe {
@@ -106,49 +78,75 @@
     }
 }
 
-#[cfg(target_os = "linux")]
-fn test_sigrt() {
-    let min = libc::SIGRTMIN();
-    let max = libc::SIGRTMAX();
-
-    // "The Linux kernel supports a range of 33 different real-time
-    // signals, numbered 32 to 64"
-    assert!(min >= 32);
-    assert!(max >= 32);
-    assert!(min <= 64);
-    assert!(max <= 64);
-
-    // "POSIX.1-2001 requires that an implementation support at least
-    // _POSIX_RTSIG_MAX (8) real-time signals."
-    assert!(min < max);
-    assert!(max - min >= 8)
-}
-
-fn test_dlsym() {
-    let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) };
-    assert!(addr as usize == 0);
-
-    let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) };
-    assert!(addr as usize != 0);
-    let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) };
-    assert_eq!(isatty(999), 0);
-    let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
-    assert_eq!(errno, libc::EBADF);
-}
-
-#[cfg(not(any(target_os = "macos", target_os = "illumos")))]
-fn test_reallocarray() {
-    unsafe {
-        let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2);
-        assert!(!p.is_null());
+fn test_malloc() {
+    // Test that small allocations sometimes *are* not very aligned.
+    let saw_unaligned = (0..64).any(|_| unsafe {
+        let p = libc::malloc(3);
         libc::free(p);
-        p = libc::malloc(16);
-        let r = libc::reallocarray(p, 2, 32);
-        assert!(!r.is_null());
-        libc::free(r);
+        (p as usize) % 4 != 0 // find any that this is *not* 4-aligned
+    });
+    assert!(saw_unaligned);
+
+    unsafe {
+        let p1 = libc::malloc(20);
+        p1.write_bytes(0u8, 20);
+
+        // old size < new size
+        let p2 = libc::realloc(p1, 40);
+        let slice = slice::from_raw_parts(p2 as *const u8, 20);
+        assert_eq!(&slice, &[0_u8; 20]);
+
+        // old size == new size
+        let p3 = libc::realloc(p2, 40);
+        let slice = slice::from_raw_parts(p3 as *const u8, 20);
+        assert_eq!(&slice, &[0_u8; 20]);
+
+        // old size > new size
+        let p4 = libc::realloc(p3, 10);
+        let slice = slice::from_raw_parts(p4 as *const u8, 10);
+        assert_eq!(&slice, &[0_u8; 10]);
+
+        libc::free(p4);
+    }
+
+    unsafe {
+        // Realloc with size 0 is okay for the null pointer (and acts like `malloc(0)`)
+        let p2 = libc::realloc(ptr::null_mut(), 0);
+        assert!(!p2.is_null());
+        libc::free(p2);
+    }
+
+    unsafe {
+        let p1 = libc::realloc(ptr::null_mut(), 20);
+        assert!(!p1.is_null());
+
+        libc::free(p1);
     }
 }
 
+fn test_calloc() {
+    unsafe {
+        let p1 = libc::calloc(0, 0);
+        assert!(!p1.is_null());
+        libc::free(p1);
+
+        let p2 = libc::calloc(20, 0);
+        assert!(!p2.is_null());
+        libc::free(p2);
+
+        let p3 = libc::calloc(0, 20);
+        assert!(!p3.is_null());
+        libc::free(p3);
+
+        let p4 = libc::calloc(4, 8);
+        assert!(!p4.is_null());
+        let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8);
+        assert_eq!(&slice, &[0_u8; 4 * 8]);
+        libc::free(p4);
+    }
+}
+
+#[cfg(not(target_os = "windows"))]
 fn test_memalign() {
     // A normal allocation.
     unsafe {
@@ -225,18 +223,37 @@
     }
 }
 
-fn main() {
-    test_thread_local_errno();
+#[cfg(not(any(
+    target_os = "windows",
+    target_os = "macos",
+    target_os = "illumos",
+    target_os = "solaris"
+)))]
+fn test_reallocarray() {
+    unsafe {
+        let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2);
+        assert!(!p.is_null());
+        libc::free(p);
+        p = libc::malloc(16);
+        let r = libc::reallocarray(p, 2, 32);
+        assert!(!r.is_null());
+        libc::free(r);
+    }
+}
 
-    test_dlsym();
+fn main() {
+    test_malloc();
+    test_calloc();
+    #[cfg(not(target_os = "windows"))]
+    test_memalign();
+    #[cfg(not(any(
+        target_os = "windows",
+        target_os = "macos",
+        target_os = "illumos",
+        target_os = "solaris"
+    )))]
+    test_reallocarray();
 
     test_memcpy();
     test_strcpy();
-
-    test_memalign();
-    #[cfg(not(any(target_os = "macos", target_os = "illumos")))]
-    test_reallocarray();
-
-    #[cfg(target_os = "linux")]
-    test_sigrt();
 }
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
new file mode 100644
index 0000000..736e0bf
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
@@ -0,0 +1,68 @@
+//@ignore-target-windows: only very limited libc on Windows
+//@compile-flags: -Zmiri-disable-isolation
+#![feature(io_error_more)]
+#![feature(pointer_is_aligned_to)]
+#![feature(strict_provenance)]
+
+use std::mem::transmute;
+
+/// Tests whether each thread has its own `__errno_location`.
+fn test_thread_local_errno() {
+    #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+    use libc::___errno as __errno_location;
+    #[cfg(target_os = "linux")]
+    use libc::__errno_location;
+    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+    use libc::__error as __errno_location;
+
+    unsafe {
+        *__errno_location() = 0xBEEF;
+        std::thread::spawn(|| {
+            assert_eq!(*__errno_location(), 0);
+            *__errno_location() = 0xBAD1DEA;
+            assert_eq!(*__errno_location(), 0xBAD1DEA);
+        })
+        .join()
+        .unwrap();
+        assert_eq!(*__errno_location(), 0xBEEF);
+    }
+}
+
+#[cfg(target_os = "linux")]
+fn test_sigrt() {
+    let min = libc::SIGRTMIN();
+    let max = libc::SIGRTMAX();
+
+    // "The Linux kernel supports a range of 33 different real-time
+    // signals, numbered 32 to 64"
+    assert!(min >= 32);
+    assert!(max >= 32);
+    assert!(min <= 64);
+    assert!(max <= 64);
+
+    // "POSIX.1-2001 requires that an implementation support at least
+    // _POSIX_RTSIG_MAX (8) real-time signals."
+    assert!(min < max);
+    assert!(max - min >= 8)
+}
+
+fn test_dlsym() {
+    let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) };
+    assert!(addr as usize == 0);
+
+    let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) };
+    assert!(addr as usize != 0);
+    let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) };
+    assert_eq!(isatty(999), 0);
+    let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
+    assert_eq!(errno, libc::EBADF);
+}
+
+fn main() {
+    test_thread_local_errno();
+
+    test_dlsym();
+
+    #[cfg(target_os = "linux")]
+    test_sigrt();
+}
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-random.rs b/src/tools/miri/tests/pass-dep/libc/libc-random.rs
similarity index 100%
rename from src/tools/miri/tests/pass-dep/shims/libc-random.rs
rename to src/tools/miri/tests/pass-dep/libc/libc-random.rs
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
similarity index 97%
rename from src/tools/miri/tests/pass-dep/shims/libc-time.rs
rename to src/tools/miri/tests/pass-dep/libc/libc-time.rs
index 69c75bd..ea1e49a 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-time.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: no libc on Windows
+//@ignore-target-windows: no libc time APIs on Windows
 //@compile-flags: -Zmiri-disable-isolation
 use std::ffi::CStr;
 use std::{env, mem, ptr};
diff --git a/src/tools/miri/tests/pass-dep/shims/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs
similarity index 98%
rename from src/tools/miri/tests/pass-dep/shims/mmap.rs
rename to src/tools/miri/tests/pass-dep/libc/mmap.rs
index 5acdedc..a0787c6 100644
--- a/src/tools/miri/tests/pass-dep/shims/mmap.rs
+++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No mmap on Windows
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
 #![feature(strict_provenance)]
 
diff --git a/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
similarity index 99%
rename from src/tools/miri/tests/pass-dep/shims/pthread-sync.rs
rename to src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
index 12d3f2b..1427526 100644
--- a/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 // We use `yield` to test specific interleavings, so disable automatic preemption.
 //@compile-flags: -Zmiri-preemption-rate=0
 #![feature(sync_unsafe_cell)]
diff --git a/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
similarity index 97%
rename from src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs
rename to src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
index bc78204..4c4f542 100644
--- a/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows: No libc on Windows
+//@ignore-target-windows: No pthreads on Windows
 use std::ffi::CStr;
 #[cfg(not(target_os = "freebsd"))]
 use std::ffi::CString;
diff --git a/src/tools/miri/tests/pass-dep/malloc.rs b/src/tools/miri/tests/pass-dep/malloc.rs
deleted file mode 100644
index 35cd137..0000000
--- a/src/tools/miri/tests/pass-dep/malloc.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-//@ignore-target-windows: No libc on Windows
-
-use core::{ptr, slice};
-
-fn main() {
-    // Test that small allocations sometimes *are* not very aligned.
-    let saw_unaligned = (0..64).any(|_| unsafe {
-        let p = libc::malloc(3);
-        libc::free(p);
-        (p as usize) % 4 != 0 // find any that this is *not* 4-aligned
-    });
-    assert!(saw_unaligned);
-
-    unsafe {
-        // Use calloc for initialized memory
-        let p1 = libc::calloc(20, 1);
-
-        // old size < new size
-        let p2 = libc::realloc(p1, 40);
-        let slice = slice::from_raw_parts(p2 as *const u8, 20);
-        assert_eq!(&slice, &[0_u8; 20]);
-
-        // old size == new size
-        let p3 = libc::realloc(p2, 40);
-        let slice = slice::from_raw_parts(p3 as *const u8, 20);
-        assert_eq!(&slice, &[0_u8; 20]);
-
-        // old size > new size
-        let p4 = libc::realloc(p3, 10);
-        let slice = slice::from_raw_parts(p4 as *const u8, 10);
-        assert_eq!(&slice, &[0_u8; 10]);
-
-        libc::free(p4);
-    }
-
-    unsafe {
-        // Realloc with size 0 is okay for the null pointer
-        let p2 = libc::realloc(ptr::null_mut(), 0);
-        assert!(p2.is_null());
-    }
-
-    unsafe {
-        let p1 = libc::realloc(ptr::null_mut(), 20);
-        assert!(!p1.is_null());
-
-        libc::free(p1);
-    }
-}
diff --git a/src/tools/miri/tests/pass-dep/rand.rs b/src/tools/miri/tests/pass-dep/rand.rs
deleted file mode 100644
index 0dce6d8..0000000
--- a/src/tools/miri/tests/pass-dep/rand.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-//@compile-flags: -Zmiri-strict-provenance
-use rand::prelude::*;
-
-// Test using the `rand` crate to generate randomness.
-fn main() {
-    // Fully deterministic seeding.
-    let mut rng = SmallRng::seed_from_u64(42);
-    let _val = rng.gen::<i32>();
-    let _val = rng.gen::<isize>();
-    let _val = rng.gen::<i128>();
-
-    // Try seeding with "real" entropy.
-    let mut rng = SmallRng::from_entropy();
-    let _val = rng.gen::<i32>();
-    let _val = rng.gen::<isize>();
-    let _val = rng.gen::<i128>();
-
-    // Also try per-thread RNG.
-    let mut rng = rand::thread_rng();
-    let _val = rng.gen::<i32>();
-    let _val = rng.gen::<isize>();
-    let _val = rng.gen::<i128>();
-}
diff --git a/src/tools/miri/tests/pass-dep/wcslen.rs b/src/tools/miri/tests/pass-dep/wcslen.rs
new file mode 100644
index 0000000..c5c9d99
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/wcslen.rs
@@ -0,0 +1,20 @@
+fn to_c_wchar_t_str(s: &str) -> Vec<libc::wchar_t> {
+    let mut r = Vec::<libc::wchar_t>::new();
+    for c in s.bytes() {
+        if c == 0 {
+            panic!("can't contain a null character");
+        }
+        if c >= 128 {
+            panic!("only ASCII supported");
+        }
+        r.push(c.into());
+    }
+    r.push(0);
+    r
+}
+
+pub fn main() {
+    let s = to_c_wchar_t_str("Rust");
+    let len = unsafe { libc::wcslen(s.as_ptr()) };
+    assert_eq!(len, 4);
+}
diff --git a/src/tools/miri/tests/pass/shims/fs-symlink.rs b/src/tools/miri/tests/pass/shims/fs-symlink.rs
new file mode 100644
index 0000000..4d5103b
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/fs-symlink.rs
@@ -0,0 +1,50 @@
+// Symlink tests are separate since they don't in general work on a Windows host.
+//@ignore-host-windows: creating symlinks requires admin permissions on Windows
+//@ignore-target-windows: File handling is not implemented yet
+//@compile-flags: -Zmiri-disable-isolation
+
+use std::fs::{read_link, remove_file, File};
+use std::io::{Read, Result};
+use std::path::Path;
+
+#[path = "../../utils/mod.rs"]
+mod utils;
+
+fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
+    // Test that the file metadata is correct.
+    let metadata = path.metadata()?;
+    // `path` should point to a file.
+    assert!(metadata.is_file());
+    // The size of the file must be equal to the number of written bytes.
+    assert_eq!(bytes.len() as u64, metadata.len());
+    Ok(())
+}
+
+fn main() {
+    let bytes = b"Hello, World!\n";
+    let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes);
+    let symlink_path = utils::prepare("miri_test_fs_symlink.txt");
+
+    // Creating a symbolic link should succeed.
+    #[cfg(unix)]
+    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
+    #[cfg(windows)]
+    std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap();
+    // Test that the symbolic link has the same contents as the file.
+    let mut symlink_file = File::open(&symlink_path).unwrap();
+    let mut contents = Vec::new();
+    symlink_file.read_to_end(&mut contents).unwrap();
+    assert_eq!(bytes, contents.as_slice());
+
+    // Test that metadata of a symbolic link (i.e., the file it points to) is correct.
+    check_metadata(bytes, &symlink_path).unwrap();
+    // Test that the metadata of a symbolic link is correct when not following it.
+    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
+    // Check that we can follow the link.
+    assert_eq!(read_link(&symlink_path).unwrap(), path);
+    // Removing symbolic link should succeed.
+    remove_file(&symlink_path).unwrap();
+
+    // Removing file should succeed.
+    remove_file(&path).unwrap();
+}
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index 8a500b8..35980fa 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -1,21 +1,17 @@
 //@ignore-target-windows: File handling is not implemented yet
 //@compile-flags: -Zmiri-disable-isolation
 
-// If this test is failing for you locally, you can try
-// 1. Deleting the files `/tmp/miri_*`
-// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files
-
 #![feature(io_error_more)]
 #![feature(io_error_uncategorized)]
 
 use std::collections::HashMap;
 use std::ffi::OsString;
 use std::fs::{
-    canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename,
-    File, OpenOptions,
+    canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File,
+    OpenOptions,
 };
 use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write};
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 #[path = "../../utils/mod.rs"]
 mod utils;
@@ -29,7 +25,6 @@
     test_metadata();
     test_file_set_len();
     test_file_sync();
-    test_symlink();
     test_errors();
     test_rename();
     test_directory();
@@ -37,30 +32,6 @@
     test_from_raw_os_error();
 }
 
-/// Prepare: compute filename and make sure the file does not exist.
-fn prepare(filename: &str) -> PathBuf {
-    let path = utils::tmp().join(filename);
-    // Clean the paths for robustness.
-    remove_file(&path).ok();
-    path
-}
-
-/// Prepare directory: compute directory name and make sure it does not exist.
-fn prepare_dir(dirname: &str) -> PathBuf {
-    let path = utils::tmp().join(&dirname);
-    // Clean the directory for robustness.
-    remove_dir_all(&path).ok();
-    path
-}
-
-/// Prepare like above, and also write some initial content to the file.
-fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
-    let path = prepare(filename);
-    let mut file = File::create(&path).unwrap();
-    file.write(content).unwrap();
-    path
-}
-
 fn test_path_conversion() {
     let tmp = utils::tmp();
     assert!(tmp.is_absolute(), "{:?} is not absolute", tmp);
@@ -69,7 +40,7 @@
 
 fn test_file() {
     let bytes = b"Hello, World!\n";
-    let path = prepare("miri_test_fs_file.txt");
+    let path = utils::prepare("miri_test_fs_file.txt");
 
     // Test creating, writing and closing a file (closing is tested when `file` is dropped).
     let mut file = File::create(&path).unwrap();
@@ -96,7 +67,7 @@
 
 fn test_file_clone() {
     let bytes = b"Hello, World!\n";
-    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);
+    let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes);
 
     // Cloning a file should be successful.
     let file = File::open(&path).unwrap();
@@ -111,7 +82,7 @@
 }
 
 fn test_file_create_new() {
-    let path = prepare("miri_test_fs_file_create_new.txt");
+    let path = utils::prepare("miri_test_fs_file_create_new.txt");
 
     // Creating a new file that doesn't yet exist should succeed.
     OpenOptions::new().write(true).create_new(true).open(&path).unwrap();
@@ -129,7 +100,7 @@
 
 fn test_seek() {
     let bytes = b"Hello, entire World!\n";
-    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);
+    let path = utils::prepare_with_content("miri_test_fs_seek.txt", bytes);
 
     let mut file = File::open(&path).unwrap();
     let mut contents = Vec::new();
@@ -168,7 +139,7 @@
 
 fn test_metadata() {
     let bytes = b"Hello, meta-World!\n";
-    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);
+    let path = utils::prepare_with_content("miri_test_fs_metadata.txt", bytes);
 
     // Test that metadata of an absolute path is correct.
     check_metadata(bytes, &path).unwrap();
@@ -182,7 +153,7 @@
 
 fn test_file_set_len() {
     let bytes = b"Hello, World!\n";
-    let path = prepare_with_content("miri_test_fs_set_len.txt", bytes);
+    let path = utils::prepare_with_content("miri_test_fs_set_len.txt", bytes);
 
     // Test extending the file
     let mut file = OpenOptions::new().read(true).write(true).open(&path).unwrap();
@@ -208,7 +179,7 @@
 
 fn test_file_sync() {
     let bytes = b"Hello, World!\n";
-    let path = prepare_with_content("miri_test_fs_sync.txt", bytes);
+    let path = utils::prepare_with_content("miri_test_fs_sync.txt", bytes);
 
     // Test that we can call sync_data and sync_all (can't readily test effects of this operation)
     let file = OpenOptions::new().write(true).open(&path).unwrap();
@@ -223,38 +194,9 @@
     remove_file(&path).unwrap();
 }
 
-fn test_symlink() {
-    let bytes = b"Hello, World!\n";
-    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
-    let symlink_path = prepare("miri_test_fs_symlink.txt");
-
-    // Creating a symbolic link should succeed.
-    #[cfg(unix)]
-    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
-    #[cfg(windows)]
-    std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap();
-    // Test that the symbolic link has the same contents as the file.
-    let mut symlink_file = File::open(&symlink_path).unwrap();
-    let mut contents = Vec::new();
-    symlink_file.read_to_end(&mut contents).unwrap();
-    assert_eq!(bytes, contents.as_slice());
-
-    // Test that metadata of a symbolic link (i.e., the file it points to) is correct.
-    check_metadata(bytes, &symlink_path).unwrap();
-    // Test that the metadata of a symbolic link is correct when not following it.
-    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
-    // Check that we can follow the link.
-    assert_eq!(read_link(&symlink_path).unwrap(), path);
-    // Removing symbolic link should succeed.
-    remove_file(&symlink_path).unwrap();
-
-    // Removing file should succeed.
-    remove_file(&path).unwrap();
-}
-
 fn test_errors() {
     let bytes = b"Hello, World!\n";
-    let path = prepare("miri_test_fs_errors.txt");
+    let path = utils::prepare("miri_test_fs_errors.txt");
 
     // The following tests also check that the `__errno_location()` shim is working properly.
     // Opening a non-existing file should fail with a "not found" error.
@@ -269,8 +211,8 @@
 
 fn test_rename() {
     // Renaming a file should succeed.
-    let path1 = prepare("miri_test_fs_rename_source.txt");
-    let path2 = prepare("miri_test_fs_rename_destination.txt");
+    let path1 = utils::prepare("miri_test_fs_rename_source.txt");
+    let path2 = utils::prepare("miri_test_fs_rename_destination.txt");
 
     let file = File::create(&path1).unwrap();
     drop(file);
@@ -289,7 +231,7 @@
 }
 
 fn test_canonicalize() {
-    let dir_path = prepare_dir("miri_test_fs_dir");
+    let dir_path = utils::prepare_dir("miri_test_fs_dir");
     create_dir(&dir_path).unwrap();
     let path = dir_path.join("test_file");
     drop(File::create(&path).unwrap());
@@ -301,7 +243,7 @@
 }
 
 fn test_directory() {
-    let dir_path = prepare_dir("miri_test_fs_dir");
+    let dir_path = utils::prepare_dir("miri_test_fs_dir");
     // Creating a directory should succeed.
     create_dir(&dir_path).unwrap();
     // Test that the metadata of a directory is correct.
diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs
index c9d5392..3a8b098 100644
--- a/src/tools/miri/tests/ui.rs
+++ b/src/tools/miri/tests/ui.rs
@@ -27,11 +27,12 @@
     flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
 }
 
-// Build the shared object file for testing external C function calls.
-fn build_so_for_c_ffi_tests() -> PathBuf {
+// Build the shared object file for testing native function calls.
+fn build_native_lib() -> PathBuf {
     let cc = option_env!("CC").unwrap_or("cc");
     // Target directory that we can write to.
-    let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so");
+    let so_target_dir =
+        Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib");
     // Create the directory if it does not already exist.
     std::fs::create_dir_all(&so_target_dir)
         .expect("Failed to create directory for shared object file");
@@ -41,18 +42,18 @@
             "-shared",
             "-o",
             so_file_path.to_str().unwrap(),
-            "tests/extern-so/test.c",
+            "tests/native-lib/test.c",
             // Only add the functions specified in libcode.version to the shared object file.
             // This is to avoid automatically adding `malloc`, etc.
             // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
             "-fPIC",
-            "-Wl,--version-script=tests/extern-so/libtest.map",
+            "-Wl,--version-script=tests/native-lib/libtest.map",
         ])
         .output()
-        .expect("failed to generate shared object file for testing external C function calls");
+        .expect("failed to generate shared object file for testing native function calls");
     if !cc_output.status.success() {
         panic!(
-            "error in generating shared object file for testing external C function calls:\n{}",
+            "error generating shared object file for testing native function calls:\n{}",
             String::from_utf8_lossy(&cc_output.stderr),
         );
     }
@@ -132,13 +133,12 @@
     config.program.args.push("--target".into());
     config.program.args.push(target.into());
 
-    // If we're testing the extern-so functionality, then build the shared object file for testing
+    // If we're testing the native-lib functionality, then build the shared object file for testing
     // external C function calls and push the relevant compiler flag.
-    if path.starts_with("tests/extern-so/") {
-        assert!(cfg!(target_os = "linux"));
-        let so_file_path = build_so_for_c_ffi_tests();
-        let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file=");
-        flag.push(so_file_path.into_os_string());
+    if path.starts_with("tests/native-lib/") {
+        let native_lib = build_native_lib();
+        let mut flag = std::ffi::OsString::from("-Zmiri-native-lib=");
+        flag.push(native_lib.into_os_string());
         config.program.args.push(flag);
     }
 
@@ -292,10 +292,10 @@
         tmpdir.path(),
     )?;
     if cfg!(target_os = "linux") {
-        ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies, tmpdir.path())?;
+        ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
         ui(
             Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
-            "tests/extern-so/fail",
+            "tests/native-lib/fail",
             &target,
             WithoutDependencies,
             tmpdir.path(),
diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs
index 0242a22..7340908 100644
--- a/src/tools/miri/tests/utils/fs.rs
+++ b/src/tools/miri/tests/utils/fs.rs
@@ -1,4 +1,5 @@
 use std::ffi::OsString;
+use std::fs;
 use std::path::PathBuf;
 
 use super::miri_extern;
@@ -27,3 +28,26 @@
     // These are host paths. We need to convert them to the target.
     host_to_target_path(path)
 }
+
+/// Prepare: compute filename and make sure the file does not exist.
+pub fn prepare(filename: &str) -> PathBuf {
+    let path = tmp().join(filename);
+    // Clean the paths for robustness.
+    fs::remove_file(&path).ok();
+    path
+}
+
+/// Prepare like above, and also write some initial content to the file.
+pub fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
+    let path = prepare(filename);
+    fs::write(&path, content).unwrap();
+    path
+}
+
+/// Prepare directory: compute directory name and make sure it does not exist.
+pub fn prepare_dir(dirname: &str) -> PathBuf {
+    let path = tmp().join(&dirname);
+    // Clean the directory for robustness.
+    fs::remove_dir_all(&path).ok();
+    path
+}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index e87950b..7459242 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -252,7 +252,6 @@
 run-make/rustdoc-scrape-examples-remap/Makefile
 run-make/rustdoc-scrape-examples-test/Makefile
 run-make/rustdoc-scrape-examples-whitespace/Makefile
-run-make/rustdoc-shared-flags/Makefile
 run-make/rustdoc-target-spec-json-path/Makefile
 run-make/rustdoc-themes/Makefile
 run-make/rustdoc-verify-output-files/Makefile
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 1d24c59..1b73b67 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -395,6 +395,7 @@
     "windows_aarch64_gnullvm",
     "windows_aarch64_msvc",
     "windows_i686_gnu",
+    "windows_i686_gnullvm",
     "windows_i686_msvc",
     "windows_x86_64_gnu",
     "windows_x86_64_gnullvm",
diff --git a/tests/auxiliary/rust_test_helpers.c b/tests/auxiliary/rust_test_helpers.c
index 965df44..34cc7fd 100644
--- a/tests/auxiliary/rust_test_helpers.c
+++ b/tests/auxiliary/rust_test_helpers.c
@@ -27,10 +27,10 @@
     return u;
 }
 
-typedef void *(*dbg_callback)(void*);
+typedef uint64_t (*dbg_callback)(uint64_t);
 
-void *
-rust_dbg_call(dbg_callback cb, void *data) {
+uint64_t
+rust_dbg_call(dbg_callback cb, uint64_t data) {
     return cb(data);
 }
 
diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs
index 7b95533..25ea5dd 100644
--- a/tests/codegen/option-niche-eq.rs
+++ b/tests/codegen/option-niche-eq.rs
@@ -7,7 +7,7 @@
 use core::ptr::NonNull;
 use core::num::NonZero;
 
-// CHECK-lABEL: @non_zero_eq
+// CHECK-LABEL: @non_zero_eq
 #[no_mangle]
 pub fn non_zero_eq(l: Option<NonZero<u32>>, r: Option<NonZero<u32>>) -> bool {
     // CHECK: start:
@@ -16,7 +16,7 @@
     l == r
 }
 
-// CHECK-lABEL: @non_zero_signed_eq
+// CHECK-LABEL: @non_zero_signed_eq
 #[no_mangle]
 pub fn non_zero_signed_eq(l: Option<NonZero<i64>>, r: Option<NonZero<i64>>) -> bool {
     // CHECK: start:
@@ -25,7 +25,7 @@
     l == r
 }
 
-// CHECK-lABEL: @non_null_eq
+// CHECK-LABEL: @non_null_eq
 #[no_mangle]
 pub fn non_null_eq(l: Option<NonNull<u8>>, r: Option<NonNull<u8>>) -> bool {
     // CHECK: start:
@@ -34,7 +34,7 @@
     l == r
 }
 
-// CHECK-lABEL: @ordering_eq
+// CHECK-LABEL: @ordering_eq
 #[no_mangle]
 pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool {
     // CHECK: start:
@@ -54,7 +54,7 @@
     G,
 }
 
-// CHECK-lABEL: @niche_eq
+// CHECK-LABEL: @niche_eq
 #[no_mangle]
 pub fn niche_eq(l: Option<EnumWithNiche>, r: Option<EnumWithNiche>) -> bool {
     // CHECK: start:
@@ -64,7 +64,7 @@
 }
 
 // FIXME: This should work too
-// // FIXME-CHECK-lABEL: @bool_eq
+// // FIXME-CHECK-LABEL: @bool_eq
 // #[no_mangle]
 // pub fn bool_eq(l: Option<bool>, r: Option<bool>) -> bool {
 //     // FIXME-CHECK: start:
diff --git a/tests/codegen/sanitizer/no-sanitize-inlining.rs b/tests/codegen/sanitizer/no-sanitize-inlining.rs
index 623bfa6..d5e4788 100644
--- a/tests/codegen/sanitizer/no-sanitize-inlining.rs
+++ b/tests/codegen/sanitizer/no-sanitize-inlining.rs
@@ -16,7 +16,7 @@
 // ASAN:       }
 //
 // LSAN-LABEL: define void @test
-// LSAN-NO:      call
+// LSAN-NOT:     call
 // LSAN:       }
 #[no_mangle]
 pub fn test(n: &mut u32) {
diff --git a/tests/codegen/simd/issue-120720-reduce-nan.rs b/tests/codegen/simd/issue-120720-reduce-nan.rs
index 2c6098c..0372582 100644
--- a/tests/codegen/simd/issue-120720-reduce-nan.rs
+++ b/tests/codegen/simd/issue-120720-reduce-nan.rs
@@ -8,7 +8,7 @@
 #![feature(stdarch_x86_avx512, avx512_target_feature)]
 use std::arch::x86_64::*;
 
-// CHECK-label: @demo(
+// CHECK-LABEL: @demo(
 #[no_mangle]
 #[target_feature(enable = "avx512f")] // Function-level target feature mismatches inhibit inlining
 pub unsafe fn demo() -> bool {
diff --git a/tests/run-make/rustdoc-shared-flags/Makefile b/tests/run-make/rustdoc-shared-flags/Makefile
deleted file mode 100644
index a2a7d7b..0000000
--- a/tests/run-make/rustdoc-shared-flags/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-include ../tools.mk
-
-all: z_help c_help list_passes
-
-c_help:
-	$(RUSTC) -C help > $(TMPDIR)/rustc.c_help.txt
-	$(RUSTDOC) -C help > $(TMPDIR)/rustdoc.c_help.txt
-	$(DIFF) $(TMPDIR)/rustc.c_help.txt $(TMPDIR)/rustdoc.c_help.txt
-
-z_help:
-	$(RUSTC) -Z help > $(TMPDIR)/rustc.z_help.txt
-	$(RUSTDOC) -Z help > $(TMPDIR)/rustdoc.z_help.txt
-	$(DIFF) $(TMPDIR)/rustc.z_help.txt $(TMPDIR)/rustdoc.z_help.txt
-
-list_passes:
-	$(RUSTC) -C passes=list > $(TMPDIR)/rustc.passes.txt
-	$(RUSTDOC) -C passes=list > $(TMPDIR)/rustdoc.passes.txt
-	$(DIFF) $(TMPDIR)/rustc.passes.txt $(TMPDIR)/rustdoc.passes.txt
diff --git a/tests/run-make/rustdoc-shared-flags/rmake.rs b/tests/run-make/rustdoc-shared-flags/rmake.rs
new file mode 100644
index 0000000..2db613f
--- /dev/null
+++ b/tests/run-make/rustdoc-shared-flags/rmake.rs
@@ -0,0 +1,14 @@
+use run_make_support::{rustc, rustdoc, Diff};
+
+fn compare_outputs(args: &[&str]) {
+    let rustc_output = String::from_utf8(rustc().args(args).command_output().stdout).unwrap();
+    let rustdoc_output = String::from_utf8(rustdoc().args(args).command_output().stdout).unwrap();
+
+    Diff::new().expected_text("rustc", rustc_output).actual_text("rustdoc", rustdoc_output).run();
+}
+
+fn main() {
+    compare_outputs(&["-C", "help"]);
+    compare_outputs(&["-Z", "help"]);
+    compare_outputs(&["-C", "passes=list"]);
+}
diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout
index bbea7e5..cc3c838 100644
--- a/tests/rustdoc-ui/issues/issue-91713.stdout
+++ b/tests/rustdoc-ui/issues/issue-91713.stdout
@@ -1,6 +1,7 @@
 Available passes for running rustdoc:
 check-custom-code-classes - check for custom code classes without the feature-gate enabled
 check_doc_test_visibility - run various visibility-related lints on doctests
+strip-aliased-non-local - strips all non-local private aliased items from the output
         strip-hidden - strips all `#[doc(hidden)]` items from the output
        strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
   strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
@@ -14,6 +15,7 @@
 check-custom-code-classes
  collect-trait-impls
 check_doc_test_visibility
+strip-aliased-non-local
         strip-hidden  (when not --document-hidden-items)
        strip-private  (when not --document-private-items)
   strip-priv-imports  (when --document-private-items)
diff --git a/tests/rustdoc/private-non-local-fields-2.rs b/tests/rustdoc/private-non-local-fields-2.rs
new file mode 100644
index 0000000..615b957
--- /dev/null
+++ b/tests/rustdoc/private-non-local-fields-2.rs
@@ -0,0 +1,11 @@
+//! This test makes sure that with never show the inner fields in the
+//! aliased type view of type alias.
+
+//@ compile-flags: -Z unstable-options --document-private-items
+
+#![crate_name = "foo"]
+
+use std::collections::BTreeMap;
+
+// @has 'foo/type.FooBar.html' '//*[@class="rust item-decl"]/code' 'struct FooBar { /* private fields */ }'
+pub type FooBar = BTreeMap<u32, String>;
diff --git a/tests/rustdoc/private-non-local-fields.rs b/tests/rustdoc/private-non-local-fields.rs
new file mode 100644
index 0000000..7922ce0
--- /dev/null
+++ b/tests/rustdoc/private-non-local-fields.rs
@@ -0,0 +1,9 @@
+//! This test makes sure that with never show the inner fields in the
+//! aliased type view of type alias.
+
+#![crate_name = "foo"]
+
+use std::collections::BTreeMap;
+
+// @has 'foo/type.FooBar.html' '//*[@class="rust item-decl"]/code' 'struct FooBar { /* private fields */ }'
+pub type FooBar = BTreeMap<u32, String>;
diff --git a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
index 9c61518..6b21877 100644
--- a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
+++ b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
@@ -1,28 +1,21 @@
 #![crate_name = "externcallback"]
 #![crate_type = "lib"]
-#![feature(rustc_private)]
 
-extern crate libc;
-
-pub mod rustrt {
-    extern crate libc;
-
-    #[link(name = "rust_test_helpers", kind = "static")]
-    extern "C" {
-        pub fn rust_dbg_call(
-            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
-            data: libc::uintptr_t,
-        ) -> libc::uintptr_t;
-    }
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_call(
+        cb: extern "C" fn(u64) -> u64,
+        data: u64,
+    ) -> u64;
 }
 
-pub fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+pub fn fact(n: u64) -> u64 {
     unsafe {
         println!("n = {}", n);
-        rustrt::rust_dbg_call(cb, n)
+        rust_dbg_call(cb, n)
     }
 }
 
-pub extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+pub extern "C" fn cb(data: u64) -> u64 {
     if data == 1 { data } else { fact(data - 1) * data }
 }
diff --git a/tests/ui/abi/extern/extern-call-deep.rs b/tests/ui/abi/extern/extern-call-deep.rs
index 062e70b..40457ae 100644
--- a/tests/ui/abi/extern/extern-call-deep.rs
+++ b/tests/ui/abi/extern/extern-call-deep.rs
@@ -1,35 +1,27 @@
 //@ run-pass
 //@ ignore-emscripten blows the JS stack
 
-#![feature(rustc_private)]
-
-extern crate libc;
-
-mod rustrt {
-    extern crate libc;
-
-    #[link(name = "rust_test_helpers", kind = "static")]
-    extern "C" {
-        pub fn rust_dbg_call(
-            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
-            data: libc::uintptr_t,
-        ) -> libc::uintptr_t;
-    }
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_call(
+        cb: extern "C" fn(u64) -> u64,
+        data: u64,
+    ) -> u64;
 }
 
-extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+extern "C" fn cb(data: u64) -> u64 {
     if data == 1 { data } else { count(data - 1) + 1 }
 }
 
-fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+fn count(n: u64) -> u64 {
     unsafe {
         println!("n = {}", n);
-        rustrt::rust_dbg_call(cb, n)
+        rust_dbg_call(cb, n)
     }
 }
 
 pub fn main() {
     let result = count(1000);
-    println!("result = {}", result);
+    println!("result = {:?}", result);
     assert_eq!(result, 1000);
 }
diff --git a/tests/ui/abi/extern/extern-call-deep2.rs b/tests/ui/abi/extern/extern-call-deep2.rs
index c021bc2..91ca28d 100644
--- a/tests/ui/abi/extern/extern-call-deep2.rs
+++ b/tests/ui/abi/extern/extern-call-deep2.rs
@@ -1,31 +1,24 @@
 //@ run-pass
-#![allow(unused_must_use)]
 //@ needs-threads
-#![feature(rustc_private)]
 
-extern crate libc;
 use std::thread;
 
-mod rustrt {
-    extern crate libc;
-
-    #[link(name = "rust_test_helpers", kind = "static")]
-    extern "C" {
-        pub fn rust_dbg_call(
-            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
-            data: libc::uintptr_t,
-        ) -> libc::uintptr_t;
-    }
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_call(
+        cb: extern "C" fn(u64) -> u64,
+        data: u64,
+    ) -> u64;
 }
 
-extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
-    if data == 1 { data } else { count(data - 1) + 1 }
+extern "C" fn cb(data: u64) -> u64 {
+    if data == 1 { data } else { count(data - 1 ) + 1 }
 }
 
-fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+fn count(n: u64) -> u64 {
     unsafe {
         println!("n = {}", n);
-        rustrt::rust_dbg_call(cb, n)
+        rust_dbg_call(cb, n)
     }
 }
 
@@ -37,5 +30,5 @@
         println!("result = {}", result);
         assert_eq!(result, 1000);
     })
-    .join();
+    .join().unwrap();
 }
diff --git a/tests/ui/abi/extern/extern-call-indirect.rs b/tests/ui/abi/extern/extern-call-indirect.rs
index 18fb07d..ef1e8ae 100644
--- a/tests/ui/abi/extern/extern-call-indirect.rs
+++ b/tests/ui/abi/extern/extern-call-indirect.rs
@@ -1,29 +1,21 @@
 //@ run-pass
 
-#![feature(rustc_private)]
-
-extern crate libc;
-
-mod rustrt {
-    extern crate libc;
-
-    #[link(name = "rust_test_helpers", kind = "static")]
-    extern "C" {
-        pub fn rust_dbg_call(
-            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
-            data: libc::uintptr_t,
-        ) -> libc::uintptr_t;
-    }
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_call(
+        cb: extern "C" fn(u64) -> u64,
+        data: u64,
+    ) -> u64;
 }
 
-extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+extern "C" fn cb(data: u64) -> u64 {
     if data == 1 { data } else { fact(data - 1) * data }
 }
 
-fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+fn fact(n: u64) -> u64 {
     unsafe {
         println!("n = {}", n);
-        rustrt::rust_dbg_call(cb, n)
+        rust_dbg_call(cb, n)
     }
 }
 
diff --git a/tests/ui/abi/extern/extern-call-scrub.rs b/tests/ui/abi/extern/extern-call-scrub.rs
index 7edf897..7df3a8f 100644
--- a/tests/ui/abi/extern/extern-call-scrub.rs
+++ b/tests/ui/abi/extern/extern-call-scrub.rs
@@ -1,35 +1,27 @@
 //@ run-pass
-#![allow(unused_must_use)]
+//@ needs-threads
 // This time we're testing repeatedly going up and down both stacks to
 // make sure the stack pointers are maintained properly in both
 // directions
 
-//@ needs-threads
-#![feature(rustc_private)]
-
-extern crate libc;
 use std::thread;
 
-mod rustrt {
-    extern crate libc;
-
-    #[link(name = "rust_test_helpers", kind = "static")]
-    extern "C" {
-        pub fn rust_dbg_call(
-            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
-            data: libc::uintptr_t,
-        ) -> libc::uintptr_t;
-    }
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_call(
+        cb: extern "C" fn(u64) -> u64,
+        data: u64,
+    ) -> u64;
 }
 
-extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+extern "C" fn cb(data: u64) -> u64 {
     if data == 1 { data } else { count(data - 1) + count(data - 1) }
 }
 
-fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+fn count(n: u64) -> u64 {
     unsafe {
         println!("n = {}", n);
-        rustrt::rust_dbg_call(cb, n)
+        rust_dbg_call(cb, n)
     }
 }
 
@@ -41,5 +33,5 @@
         println!("result = {}", result);
         assert_eq!(result, 2048);
     })
-    .join();
+    .join().unwrap();
 }
diff --git a/tests/ui/abi/extern/extern-crosscrate.rs b/tests/ui/abi/extern/extern-crosscrate.rs
index c283cbe..b467d99 100644
--- a/tests/ui/abi/extern/extern-crosscrate.rs
+++ b/tests/ui/abi/extern/extern-crosscrate.rs
@@ -1,15 +1,12 @@
 //@ run-pass
 //@ aux-build:extern-crosscrate-source.rs
 
-#![feature(rustc_private)]
-
 extern crate externcallback;
-extern crate libc;
 
-fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+fn fact(n: u64) -> u64 {
     unsafe {
-        println!("n = {}", n);
-        externcallback::rustrt::rust_dbg_call(externcallback::cb, n)
+        println!("n = {:?}", n);
+        externcallback::rust_dbg_call(externcallback::cb, n)
     }
 }
 
diff --git a/tests/ui/abi/foreign/foreign-call-no-runtime.rs b/tests/ui/abi/foreign/foreign-call-no-runtime.rs
deleted file mode 100644
index fccd62b..0000000
--- a/tests/ui/abi/foreign/foreign-call-no-runtime.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-//@ run-pass
-//@ needs-threads
-
-#![feature(rustc_private)]
-
-extern crate libc;
-
-use std::mem;
-use std::thread;
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern "C" {
-    fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t), data: libc::uintptr_t) -> libc::uintptr_t;
-}
-
-pub fn main() {
-    unsafe {
-        thread::spawn(move || {
-            let i: isize = 100;
-            rust_dbg_call(callback_isize, mem::transmute(&i));
-        })
-        .join()
-        .unwrap();
-
-        thread::spawn(move || {
-            let i: i32 = 100;
-            rust_dbg_call(callback_i32, mem::transmute(&i));
-        })
-        .join()
-        .unwrap();
-
-        thread::spawn(move || {
-            let i: i64 = 100;
-            rust_dbg_call(callback_i64, mem::transmute(&i));
-        })
-        .join()
-        .unwrap();
-    }
-}
-
-extern "C" fn callback_isize(data: libc::uintptr_t) {
-    unsafe {
-        let data = data as *const isize;
-        assert_eq!(*data, 100);
-    }
-}
-
-extern "C" fn callback_i64(data: libc::uintptr_t) {
-    unsafe {
-        let data = data as *const i64;
-        assert_eq!(*data, 100);
-    }
-}
-
-extern "C" fn callback_i32(data: libc::uintptr_t) {
-    unsafe {
-        let data = data as *const i32;
-        assert_eq!(*data, 100);
-    }
-}
diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs
index 87a6822..87fa42f 100644
--- a/tests/ui/lint/reference_casting.rs
+++ b/tests/ui/lint/reference_casting.rs
@@ -261,6 +261,13 @@
         let ptr = r as *mut i32 as *mut Vec3<i32>;
         unsafe { *ptr = Vec3(0, 0, 0) }
     }
+
+    unsafe fn deref(v: &mut Vec3<i32>) {
+        let r = &mut v.0;
+        let r = &mut *r;
+        let ptr = &mut *(r as *mut i32 as *mut Vec3<i32>);
+        unsafe { *ptr = Vec3(0, 0, 0) }
+    }
 }
 
 const RAW_PTR: *mut u8 = 1 as *mut u8;
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
index f1ac3e3..62e4f82 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
@@ -53,6 +53,12 @@
     if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) {
         let _: u32 = x;
     }
+    if let Some(&Some(&x)) = Some(&Some(&mut 0)) {
+        let _: u32 = x;
+    }
+    if let Some(&Some(x)) = &mut Some(Some(0)) {
+        let _: u32 = x;
+    }
 
     let &mut x = &&mut 0;
     let _: &u32 = x;
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
index ec091bb..96b4ff7 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
@@ -14,18 +14,20 @@
         let _: &mut u32 = x;
         //~^ ERROR: mismatched types
     }
-    if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
-        //~^ ERROR: mismatched types
-    }
     if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
         //~^ ERROR: mismatched types
     }
     if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
         //~^ ERROR: mismatched types
     }
+    if let Some(&mut Some(x)) = &Some(Some(0)) {
+        //~^ ERROR: mismatched types
+    }
+    if let Some(&mut Some(x)) = &Some(Some(0)) {
+        //~^ ERROR: mismatched types
+    }
 
-
-    let &mut _= &&0;
+    let &mut _ = &&0;
     //~^ ERROR: mismatched types
 
     let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
index be71ee6..e06a645 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
@@ -34,17 +34,6 @@
 error[E0308]: mismatched types
   --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23
    |
-LL |     if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
-   |                       ^^     ------------------- this expression has type `Option<&Option<&mut {integer}>>`
-   |                       |
-   |                       types differ in mutability
-   |
-   = note: expected mutable reference `&mut {integer}`
-                      found reference `&_`
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23
-   |
 LL |     if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
    |                       ^^^^^^     ------------------- this expression has type `&mut Option<&Option<{integer}>>`
    |                       |
@@ -54,7 +43,7 @@
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:29
    |
 LL |     if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
    |                             ^^^^^^       ------------------------- this expression has type `&Option<Option<&mut Option<{integer}>>>`
@@ -65,10 +54,32 @@
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:17
    |
-LL |     let &mut _= &&0;
-   |         ^^^^^^  --- this expression has type `&&{integer}`
+LL |     if let Some(&mut Some(x)) = &Some(Some(0)) {
+   |                 ^^^^^^^^^^^^    -------------- this expression has type `&Option<Option<{integer}>>`
+   |                 |
+   |                 expected `Option<{integer}>`, found `&mut _`
+   |
+   = note:           expected enum `Option<{integer}>`
+           found mutable reference `&mut _`
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17
+   |
+LL |     if let Some(&mut Some(x)) = &Some(Some(0)) {
+   |                 ^^^^^^^^^^^^    -------------- this expression has type `&Option<Option<{integer}>>`
+   |                 |
+   |                 expected `Option<{integer}>`, found `&mut _`
+   |
+   = note:           expected enum `Option<{integer}>`
+           found mutable reference `&mut _`
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:30:9
+   |
+LL |     let &mut _ = &&0;
+   |         ^^^^^^   --- this expression has type `&&{integer}`
    |         |
    |         expected integer, found `&mut _`
    |
@@ -76,7 +87,7 @@
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:33:9
    |
 LL |     let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
    |         ^^^^^^   ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}`
@@ -86,6 +97,6 @@
    = note:           expected type `{integer}`
            found mutable reference `&mut _`
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs
index 3645548..3cdf47c 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs
@@ -8,4 +8,7 @@
         //~^ ERROR: cannot move out of a shared reference [E0507]
         let _: &u32 = x;
     }
+
+    let &ref mut x = &0;
+    //~^ cannot borrow data in a `&` reference as mutable [E0596]
 }
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr
index ccfb5c7..8b86fa6 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr
@@ -12,6 +12,13 @@
 LL |     if let Some(&Some(ref x)) = Some(&Some(&mut 0)) {
    |                       +++
 
-error: aborting due to 1 previous error
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:12:10
+   |
+LL |     let &ref mut x = &0;
+   |          ^^^^^^^^^ cannot borrow as mutable
 
-For more information about this error, try `rustc --explain E0507`.
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0507, E0596.
+For more information about an error, try `rustc --explain E0507`.
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed
new file mode 100644
index 0000000..bc7a58a
--- /dev/null
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed
@@ -0,0 +1,30 @@
+//@ edition: 2024
+//@ compile-flags: -Zunstable-options
+//@ run-rustfix
+#![allow(incomplete_features)]
+#![feature(ref_pat_eat_one_layer_2024)]
+
+pub fn main() {
+    if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+        let _: &mut u8 = x;
+    }
+
+    if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+        let _: &mut u8 = x;
+    }
+
+    macro_rules! pat {
+        ($var:ident) => { ref mut $var };
+    }
+    let &mut pat!(x) = &mut 0;
+    //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+    let _: &mut u8 = x;
+
+    let &mut (ref mut a, ref mut b) = &mut (true, false);
+    //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+    //~| ERROR: cannot borrow as mutable inside an `&` pattern
+    let _: &mut bool = a;
+    let _: &mut bool = b;
+}
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs
new file mode 100644
index 0000000..c6d72b0
--- /dev/null
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs
@@ -0,0 +1,30 @@
+//@ edition: 2024
+//@ compile-flags: -Zunstable-options
+//@ run-rustfix
+#![allow(incomplete_features)]
+#![feature(ref_pat_eat_one_layer_2024)]
+
+pub fn main() {
+    if let Some(&Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+        let _: &mut u8 = x;
+    }
+
+    if let &Some(Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+        let _: &mut u8 = x;
+    }
+
+    macro_rules! pat {
+        ($var:ident) => { ref mut $var };
+    }
+    let &pat!(x) = &mut 0;
+    //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+    let _: &mut u8 = x;
+
+    let &(ref mut a, ref mut b) = &mut (true, false);
+    //~^ ERROR: cannot borrow as mutable inside an `&` pattern
+    //~| ERROR: cannot borrow as mutable inside an `&` pattern
+    let _: &mut bool = a;
+    let _: &mut bool = b;
+}
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr
new file mode 100644
index 0000000..964e9f3
--- /dev/null
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr
@@ -0,0 +1,43 @@
+error[E0596]: cannot borrow as mutable inside an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:8:31
+   |
+LL |     if let Some(&Some(ref mut x)) = &mut Some(Some(0)) {
+   |                 -             ^
+   |                 |
+   |                 help: replace this `&` with `&mut`: `&mut`
+
+error[E0596]: cannot borrow as mutable inside an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:13:31
+   |
+LL |     if let &Some(Some(ref mut x)) = &mut Some(Some(0)) {
+   |            -                  ^
+   |            |
+   |            help: replace this `&` with `&mut`: `&mut`
+
+error[E0596]: cannot borrow as mutable inside an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:21:15
+   |
+LL |     let &pat!(x) = &mut 0;
+   |         -     ^
+   |         |
+   |         help: replace this `&` with `&mut`: `&mut`
+
+error[E0596]: cannot borrow as mutable inside an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:19
+   |
+LL |     let &(ref mut a, ref mut b) = &mut (true, false);
+   |         -         ^
+   |         |
+   |         help: replace this `&` with `&mut`: `&mut`
+
+error[E0596]: cannot borrow as mutable inside an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:30
+   |
+LL |     let &(ref mut a, ref mut b) = &mut (true, false);
+   |         -                    ^
+   |         |
+   |         help: replace this `&` with `&mut`: `&mut`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-fail.rs
similarity index 63%
rename from tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs
rename to tests/ui/match/ref_pat_everywhere-fail.rs
index 9dd7a78..d1b1c04 100644
--- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs
+++ b/tests/ui/match/ref_pat_everywhere-fail.rs
@@ -5,11 +5,7 @@
         //~^ ERROR: mismatched types [E0308]
         let _: u32 = x;
     }
-    if let &Some(x) = &mut Some(0) {
-        //~^ ERROR: mismatched types [E0308]
-        let _: u32 = x;
-    }
-    if let Some(&x) = &mut Some(0) {
+    if let Some(&mut x) = Some(&0) {
         //~^ ERROR: mismatched types [E0308]
         let _: u32 = x;
     }
diff --git a/tests/ui/match/ref_pat_everywhere-fail.stderr b/tests/ui/match/ref_pat_everywhere-fail.stderr
new file mode 100644
index 0000000..25a0112
--- /dev/null
+++ b/tests/ui/match/ref_pat_everywhere-fail.stderr
@@ -0,0 +1,38 @@
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_everywhere-fail.rs:4:17
+   |
+LL |     if let Some(&x) = Some(0) {
+   |                 ^^    ------- this expression has type `Option<{integer}>`
+   |                 |
+   |                 expected integer, found `&_`
+   |
+   = note:   expected type `{integer}`
+           found reference `&_`
+help: consider removing `&` from the pattern
+   |
+LL |     if let Some(x) = Some(0) {
+   |                 ~
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_everywhere-fail.rs:8:17
+   |
+LL |     if let Some(&mut x) = Some(&0) {
+   |                 ^^^^^^    -------- this expression has type `Option<&{integer}>`
+   |                 |
+   |                 types differ in mutability
+   |
+   = note:      expected reference `&{integer}`
+           found mutable reference `&mut _`
+note: to declare a mutable binding use: `mut x`
+  --> $DIR/ref_pat_everywhere-fail.rs:8:17
+   |
+LL |     if let Some(&mut x) = Some(&0) {
+   |                 ^^^^^^
+help: consider removing `&mut` from the pattern
+   |
+LL |     if let Some(x) = Some(&0) {
+   |                 ~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr
deleted file mode 100644
index d512ea5..0000000
--- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr
+++ /dev/null
@@ -1,44 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:4:17
-   |
-LL |     if let Some(&x) = Some(0) {
-   |                 ^^    ------- this expression has type `Option<{integer}>`
-   |                 |
-   |                 expected integer, found `&_`
-   |
-   = note:   expected type `{integer}`
-           found reference `&_`
-help: consider removing `&` from the pattern
-   |
-LL |     if let Some(x) = Some(0) {
-   |                 ~
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:8:12
-   |
-LL |     if let &Some(x) = &mut Some(0) {
-   |            ^^^^^^^^   ------------ this expression has type `&mut Option<{integer}>`
-   |            |
-   |            types differ in mutability
-   |
-   = note: expected mutable reference `&mut Option<{integer}>`
-                      found reference `&_`
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:12:17
-   |
-LL |     if let Some(&x) = &mut Some(0) {
-   |                 ^^    ------------ this expression has type `&mut Option<{integer}>`
-   |                 |
-   |                 expected integer, found `&_`
-   |
-   = note:   expected type `{integer}`
-           found reference `&_`
-help: consider removing `&` from the pattern
-   |
-LL |     if let Some(x) = &mut Some(0) {
-   |                 ~
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs
index b3daca4..9a79c54 100644
--- a/tests/ui/match/ref_pat_everywhere.rs
+++ b/tests/ui/match/ref_pat_everywhere.rs
@@ -15,4 +15,10 @@
     if let Some(Some(&x)) = &Some(&mut Some(0)) {
         let _: u32 = x;
     }
+    if let &Some(x) = &mut Some(0) {
+        let _: u32 = x;
+    }
+    if let Some(&x) = &mut Some(0) {
+        let _: u32 = x;
+    }
 }