about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-09 09:24:11 +0000
committerbors <bors@rust-lang.org>2024-09-09 09:24:11 +0000
commit1f44f0a66fa0892ca680a26e271d22993d53de5c (patch)
treec709fabb56f7dc7a6fe0e3ffd2aa1f8cb346c669 /src
parent263a3aeeb8f2d0e9cc85eee61774d1f5f23dc3f5 (diff)
parent3e01a5da6e966f595c8343c72f4c53abb7998798 (diff)
Auto merge of #130133 - workingjubilee:rollup-t5o827k, r=workingjubilee
Rollup of 14 pull requests

Successful merges:

 - #119229 (Update mingw-w64 + GNU toolchain)
 - #128345 (added support for GNU/Hurd on x86_64)
 - #128667 (rustdoc: normalise type/field names)
 - #129876 (Use sysroot crates maximally in `rustc_codegen_gcc`.)
 - #130034 ( Fix enabling wasm-component-ld to match other tools )
 - #130048 (run-make-support: Add llvm-pdbutil)
 - #130068 (Test codegen when setting deployment target)
 - #130070 (Rename variant `AddrOfRegion` of `RegionVariableOrigin` to `BorrowRegion`)
 - #130087 (remove 'const' from 'Option::iter')
 - #130090 (make Result::copied unstably const)
 - #130092 (Fixes typo in wasm32-wasip2 doc comment)
 - #130107 (const: make ptr.is_null() stop execution on ambiguity)
 - #130115 (Remove needless returns detected by clippy in libraries)
 - #130130 (Miri subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs38
-rw-r--r--src/bootstrap/src/lib.rs17
-rwxr-xr-xsrc/ci/scripts/install-mingw.sh4
-rw-r--r--src/doc/rustc/src/platform-support.md1
-rw-r--r--src/doc/rustc/src/platform-support/hurd.md7
-rw-r--r--src/librustdoc/json/conversions.rs102
-rw-r--r--src/librustdoc/json/mod.rs4
-rw-r--r--src/rustdoc-json-types/lib.rs86
-rw-r--r--src/rustdoc-json-types/tests.rs4
-rw-r--r--src/tools/jsondoclint/src/item_kind.rs22
-rw-r--r--src/tools/jsondoclint/src/validator.rs49
-rw-r--r--src/tools/miri/Cargo.toml2
-rw-r--r--src/tools/miri/README.md2
-rw-r--r--src/tools/miri/miri.bat4
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs213
-rw-r--r--src/tools/miri/src/concurrency/init_once.rs10
-rw-r--r--src/tools/miri/src/concurrency/sync.rs119
-rw-r--r--src/tools/miri/src/concurrency/thread.rs9
-rw-r--r--src/tools/miri/src/lib.rs5
-rw-r--r--src/tools/miri/src/machine.rs16
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/mod.rs2
-rw-r--r--src/tools/miri/src/shims/native_lib.rs7
-rw-r--r--src/tools/miri/src/shims/unix/macos/sync.rs2
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs223
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr20
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs28
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr20
-rw-r--r--src/tools/miri/tests/native-lib/fail/function_not_in_so.rs4
-rw-r--r--src/tools/miri/tests/native-lib/fail/private_function.rs15
-rw-r--r--src/tools/miri/tests/native-lib/fail/private_function.stderr15
-rw-r--r--src/tools/miri/tests/native-lib/native-lib.map20
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.rs16
-rw-r--r--src/tools/miri/tests/native-lib/pass/scalar_arguments.rs4
-rw-r--r--src/tools/miri/tests/native-lib/ptr_read_access.c11
-rw-r--r--src/tools/miri/tests/native-lib/scalar_arguments.c20
-rw-r--r--src/tools/miri/tests/ui.rs19
-rw-r--r--src/tools/run-make-support/src/external_deps/llvm.rs24
-rw-r--r--src/tools/run-make-support/src/lib.rs2
-rw-r--r--src/tools/run-make-support/src/targets.rs18
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--src/tools/wasm-component-ld/README.md4
45 files changed, 729 insertions, 468 deletions
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 1936c91ef83..102c9fd2554 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1912,7 +1912,7 @@ impl Step for Assemble {
         // delegates to the `rust-lld` binary for linking and then runs
         // logic to create the final binary. This is used by the
         // `wasm32-wasip2` target of Rust.
-        if builder.build_wasm_component_ld() {
+        if builder.tool_enabled("wasm-component-ld") {
             let wasm_component_ld_exe =
                 builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
                     compiler: build_compiler,
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 4957de2e1b7..ccb5656d671 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -473,7 +473,7 @@ impl Step for Rustc {
                     );
                 }
             }
-            if builder.build_wasm_component_ld() {
+            if builder.tool_enabled("wasm-component-ld") {
                 let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
                 let ld = exe("wasm-component-ld", compiler.host);
                 builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld));
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 3a1eb43b801..3c2d791c209 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -693,14 +693,7 @@ impl Step for Cargo {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.path("src/tools/cargo").default_condition(
-            builder.config.extended
-                && builder.config.tools.as_ref().map_or(
-                    true,
-                    // If `tools` is set, search list for this tool.
-                    |tools| tools.iter().any(|tool| tool == "cargo"),
-                ),
-        )
+        run.path("src/tools/cargo").default_condition(builder.tool_enabled("cargo"))
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -772,14 +765,7 @@ impl Step for RustAnalyzer {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.path("src/tools/rust-analyzer").default_condition(
-            builder.config.extended
-                && builder
-                    .config
-                    .tools
-                    .as_ref()
-                    .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
-        )
+        run.path("src/tools/rust-analyzer").default_condition(builder.tool_enabled("rust-analyzer"))
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -821,12 +807,8 @@ impl Step for RustAnalyzerProcMacroSrv {
         run.path("src/tools/rust-analyzer")
             .path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
             .default_condition(
-                builder.config.extended
-                    && builder.config.tools.as_ref().map_or(true, |tools| {
-                        tools.iter().any(|tool| {
-                            tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv"
-                        })
-                    }),
+                builder.tool_enabled("rust-analyzer")
+                    || builder.tool_enabled("rust-analyzer-proc-macro-srv"),
             )
     }
 
@@ -874,16 +856,8 @@ impl Step for LlvmBitcodeLinker {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.path("src/tools/llvm-bitcode-linker").default_condition(
-            builder.config.extended
-                && builder
-                    .config
-                    .tools
-                    .as_ref()
-                    .map_or(builder.build.unstable_features(), |tools| {
-                        tools.iter().any(|tool| tool == "llvm-bitcode-linker")
-                    }),
-        )
+        run.path("src/tools/llvm-bitcode-linker")
+            .default_condition(builder.tool_enabled("llvm-bitcode-linker"))
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index c76ce340956..780024e307e 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -1407,16 +1407,17 @@ Executed at: {executed_at}"#,
         None
     }
 
-    /// Returns whether it's requested that `wasm-component-ld` is built as part
-    /// of the sysroot. This is done either with the `extended` key in
-    /// `config.toml` or with the `tools` set.
-    fn build_wasm_component_ld(&self) -> bool {
-        if self.config.extended {
-            return true;
+    /// Returns whether the specified tool is configured as part of this build.
+    ///
+    /// This requires that both the `extended` key is set and the `tools` key is
+    /// either unset or specifically contains the specified tool.
+    fn tool_enabled(&self, tool: &str) -> bool {
+        if !self.config.extended {
+            return false;
         }
         match &self.config.tools {
-            Some(set) => set.contains("wasm-component-ld"),
-            None => false,
+            Some(set) => set.contains(tool),
+            None => true,
         }
     }
 
diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh
index 31aa3785bc3..91eab2e7a08 100755
--- a/src/ci/scripts/install-mingw.sh
+++ b/src/ci/scripts/install-mingw.sh
@@ -6,8 +6,8 @@ IFS=$'\n\t'
 
 source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
 
-MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z"
-MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z"
+MINGW_ARCHIVE_32="i686-14.1.0-release-posix-dwarf-msvcrt-rt_v12-rev0.7z"
+MINGW_ARCHIVE_64="x86_64-14.1.0-release-posix-seh-msvcrt-rt_v12-rev0.7z"
 
 if isWindows && isKnownToBeMingwBuild; then
     case "${CI_JOB_NAME}" in
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 319dc9a7c08..9a35b35af71 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -382,6 +382,7 @@ target | std | host | notes
 [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ |   | 64-bit Unikraft with musl 1.2.3
 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
 `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
+[`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd
 [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | x86_64 Hermit
 `x86_64-unknown-l4re-uclibc` | ? |  |
 [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD
diff --git a/src/doc/rustc/src/platform-support/hurd.md b/src/doc/rustc/src/platform-support/hurd.md
index ddf40213ed4..2521f79dc5e 100644
--- a/src/doc/rustc/src/platform-support/hurd.md
+++ b/src/doc/rustc/src/platform-support/hurd.md
@@ -1,4 +1,4 @@
-# `i686-unknown-hurd-gnu`
+# `i686-unknown-hurd-gnu` and `x86_64-unknown-hurd-gnu`
 
 **Tier: 3**
 
@@ -16,7 +16,8 @@ The GNU/Hurd target supports `std` and uses the standard ELF file format.
 
 ## Building the target
 
-This target can be built by adding `i686-unknown-hurd-gnu` as target in the rustc list.
+This target can be built by adding `i686-unknown-hurd-gnu` and
+`x86_64-unknown-hurd-gnu` as targets in the rustc list.
 
 ## Building Rust programs
 
@@ -32,4 +33,4 @@ Tests can be run in the same way as a regular binary.
 ## Cross-compilation toolchains and C code
 
 The target supports C code, the GNU toolchain calls the target
-`i686-unknown-gnu`.
+`i686-unknown-gnu` and `x86_64-unknown-gnu`.
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index b97d710c007..59e4b072a12 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -156,7 +156,7 @@ impl FromWithTcx<clean::GenericArgs> for GenericArgs {
         match args {
             AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
                 args: args.into_vec().into_tcx(tcx),
-                bindings: constraints.into_tcx(tcx),
+                constraints: constraints.into_tcx(tcx),
             },
             Parenthesized { inputs, output } => GenericArgs::Parenthesized {
                 inputs: inputs.into_vec().into_tcx(tcx),
@@ -198,9 +198,9 @@ impl FromWithTcx<clean::ConstantKind> for Constant {
     }
 }
 
-impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
+impl FromWithTcx<clean::AssocItemConstraint> for AssocItemConstraint {
     fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self {
-        TypeBinding {
+        AssocItemConstraint {
             name: constraint.assoc.name.to_string(),
             args: constraint.assoc.args.into_tcx(tcx),
             binding: constraint.kind.into_tcx(tcx),
@@ -208,12 +208,12 @@ impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
     }
 }
 
-impl FromWithTcx<clean::AssocItemConstraintKind> for TypeBindingKind {
+impl FromWithTcx<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
     fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self {
         use clean::AssocItemConstraintKind::*;
         match kind {
-            Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)),
-            Bound { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)),
+            Equality { term } => AssocItemConstraintKind::Equality(term.into_tcx(tcx)),
+            Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_tcx(tcx)),
         }
     }
 }
@@ -314,7 +314,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         ModuleItem(m) => {
             ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
         }
-        ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
+        ImportItem(i) => ItemEnum::Use(i.into_tcx(tcx)),
         StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
         UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
         StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
@@ -331,7 +331,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
         StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
         ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)),
-        ForeignTypeItem => ItemEnum::ForeignType,
+        ForeignTypeItem => ItemEnum::ExternType,
         TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
         // FIXME(generic_const_items): Add support for generic free consts
         ConstantItem(ci) => {
@@ -347,21 +347,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         TyAssocConstItem(_generics, ty) => {
-            ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: None }
+            ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         AssocConstItem(ci) => {
-            ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), default: Some(ci.kind.expr(tcx)) }
+            ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), value: Some(ci.kind.expr(tcx)) }
+        }
+        TyAssocTypeItem(g, b) => {
+            ItemEnum::AssocType { generics: g.into_tcx(tcx), bounds: b.into_tcx(tcx), type_: None }
         }
-        TyAssocTypeItem(g, b) => ItemEnum::AssocType {
-            generics: g.into_tcx(tcx),
-            bounds: b.into_tcx(tcx),
-            default: None,
-        },
         AssocTypeItem(t, b) => ItemEnum::AssocType {
             generics: t.generics.into_tcx(tcx),
             bounds: b.into_tcx(tcx),
-            default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
+            type_: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
         },
         // `convert_item` early returns `None` for stripped items and keywords.
         KeywordItem => unreachable!(),
@@ -385,7 +383,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
 
 impl FromWithTcx<clean::Struct> for Struct {
     fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
-        let fields_stripped = struct_.has_stripped_entries();
+        let has_stripped_fields = struct_.has_stripped_entries();
         let clean::Struct { ctor_kind, generics, fields } = struct_;
 
         let kind = match ctor_kind {
@@ -394,7 +392,7 @@ impl FromWithTcx<clean::Struct> for Struct {
                 assert!(fields.is_empty());
                 StructKind::Unit
             }
-            None => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
+            None => StructKind::Plain { fields: ids(fields, tcx), has_stripped_fields },
         };
 
         Struct {
@@ -407,22 +405,22 @@ impl FromWithTcx<clean::Struct> for Struct {
 
 impl FromWithTcx<clean::Union> for Union {
     fn from_tcx(union_: clean::Union, tcx: TyCtxt<'_>) -> Self {
-        let fields_stripped = union_.has_stripped_entries();
+        let has_stripped_fields = union_.has_stripped_entries();
         let clean::Union { generics, fields } = union_;
         Union {
             generics: generics.into_tcx(tcx),
-            fields_stripped,
+            has_stripped_fields,
             fields: ids(fields, tcx),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
-pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> Header {
-    Header {
-        async_: header.is_async(),
-        const_: header.is_const(),
-        unsafe_: header.is_unsafe(),
+pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
+    FunctionHeader {
+        is_async: header.is_async(),
+        is_const: header.is_const(),
+        is_unsafe: header.is_unsafe(),
         abi: convert_abi(header.abi),
     }
 }
@@ -474,7 +472,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
             Type { bounds, default, synthetic } => GenericParamDefKind::Type {
                 bounds: bounds.into_tcx(tcx),
                 default: default.map(|x| (*x).into_tcx(tcx)),
-                synthetic,
+                is_synthetic: synthetic,
             },
             Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
                 type_: (*ty).into_tcx(tcx),
@@ -508,7 +506,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                                         .map(|bound| bound.into_tcx(tcx))
                                         .collect(),
                                     default: default.map(|ty| (*ty).into_tcx(tcx)),
-                                    synthetic,
+                                    is_synthetic: synthetic,
                                 }
                             }
                             clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
@@ -602,12 +600,12 @@ impl FromWithTcx<clean::Type> for Type {
             ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
-                mutable: mutability == ast::Mutability::Mut,
+                is_mutable: mutability == ast::Mutability::Mut,
                 type_: Box::new((*type_).into_tcx(tcx)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
                 lifetime: lifetime.map(convert_lifetime),
-                mutable: mutability == ast::Mutability::Mut,
+                is_mutable: mutability == ast::Mutability::Mut,
                 type_: Box::new((*type_).into_tcx(tcx)),
             },
             QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath {
@@ -643,29 +641,29 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
     fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
         let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
         FunctionPointer {
-            header: Header {
-                unsafe_: matches!(safety, rustc_hir::Safety::Unsafe),
-                const_: false,
-                async_: false,
+            header: FunctionHeader {
+                is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
+                is_const: false,
+                is_async: false,
                 abi: convert_abi(abi),
             },
             generic_params: generic_params.into_tcx(tcx),
-            decl: decl.into_tcx(tcx),
+            sig: decl.into_tcx(tcx),
         }
     }
 }
 
-impl FromWithTcx<clean::FnDecl> for FnDecl {
+impl FromWithTcx<clean::FnDecl> for FunctionSignature {
     fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
         let clean::FnDecl { inputs, output, c_variadic } = decl;
-        FnDecl {
+        FunctionSignature {
             inputs: inputs
                 .values
                 .into_iter()
                 .map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx)))
                 .collect(),
             output: if output.is_unit() { None } else { Some(output.into_tcx(tcx)) },
-            c_variadic,
+            is_c_variadic: c_variadic,
         }
     }
 }
@@ -702,12 +700,12 @@ impl FromWithTcx<clean::Impl> for Impl {
         let provided_trait_methods = impl_.provided_trait_methods(tcx);
         let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
         // FIXME: use something like ImplKind in JSON?
-        let (synthetic, blanket_impl) = match kind {
+        let (is_synthetic, blanket_impl) = match kind {
             clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None),
             clean::ImplKind::Auto => (true, None),
             clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
         };
-        let negative_polarity = match polarity {
+        let is_negative = match polarity {
             ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false,
             ty::ImplPolarity::Negative => true,
         };
@@ -721,8 +719,8 @@ impl FromWithTcx<clean::Impl> for Impl {
             trait_: trait_.map(|path| path.into_tcx(tcx)),
             for_: for_.into_tcx(tcx),
             items: ids(items, tcx),
-            negative: negative_polarity,
-            synthetic,
+            is_negative,
+            is_synthetic,
             blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)),
         }
     }
@@ -736,7 +734,7 @@ pub(crate) fn from_function(
 ) -> Function {
     let clean::Function { decl, generics } = *function;
     Function {
-        decl: decl.into_tcx(tcx),
+        sig: decl.into_tcx(tcx),
         generics: generics.into_tcx(tcx),
         header: from_fn_header(&header),
         has_body,
@@ -745,11 +743,11 @@ pub(crate) fn from_function(
 
 impl FromWithTcx<clean::Enum> for Enum {
     fn from_tcx(enum_: clean::Enum, tcx: TyCtxt<'_>) -> Self {
-        let variants_stripped = enum_.has_stripped_entries();
+        let has_stripped_variants = enum_.has_stripped_entries();
         let clean::Enum { variants, generics } = enum_;
         Enum {
             generics: generics.into_tcx(tcx),
-            variants_stripped,
+            has_stripped_variants,
             variants: ids(variants, tcx),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
@@ -766,7 +764,7 @@ impl FromWithTcx<clean::Variant> for Variant {
             CLike => VariantKind::Plain,
             Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)),
             Struct(s) => VariantKind::Struct {
-                fields_stripped: s.has_stripped_entries(),
+                has_stripped_fields: s.has_stripped_entries(),
                 fields: ids(s.fields, tcx),
             },
         };
@@ -787,21 +785,21 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
     }
 }
 
-impl FromWithTcx<clean::Import> for Import {
+impl FromWithTcx<clean::Import> for Use {
     fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
         use clean::ImportKind::*;
-        let (name, glob) = match import.kind {
+        let (name, is_glob) = match import.kind {
             Simple(s) => (s.to_string(), false),
             Glob => (
                 import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
                 true,
             ),
         };
-        Import {
+        Use {
             source: import.source.path.whole_name(),
             name,
             id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)),
-            glob,
+            is_glob,
         }
     }
 }
@@ -835,7 +833,7 @@ impl FromWithTcx<clean::Static> for Static {
     fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self {
         Static {
             type_: (*stat.type_).into_tcx(tcx),
-            mutable: stat.mutability == ast::Mutability::Mut,
+            is_mutable: stat.mutability == ast::Mutability::Mut,
             expr: stat
                 .expr
                 .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
@@ -856,7 +854,7 @@ impl FromWithTcx<ItemType> for ItemKind {
         match kind {
             Module => ItemKind::Module,
             ExternCrate => ItemKind::ExternCrate,
-            Import => ItemKind::Import,
+            Import => ItemKind::Use,
             Struct => ItemKind::Struct,
             Union => ItemKind::Union,
             Enum => ItemKind::Enum,
@@ -872,7 +870,7 @@ impl FromWithTcx<ItemType> for ItemKind {
             Primitive => ItemKind::Primitive,
             AssocConst => ItemKind::AssocConst,
             AssocType => ItemKind::AssocType,
-            ForeignType => ItemKind::ForeignType,
+            ForeignType => ItemKind::ExternType,
             Keyword => ItemKind::Keyword,
             TraitAlias => ItemKind::TraitAlias,
             ProcAttribute => ItemKind::ProcAttribute,
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 1b2d61a7637..03a012864ef 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -197,7 +197,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
                 types::ItemEnum::Function(_)
                 | types::ItemEnum::Module(_)
-                | types::ItemEnum::Import(_)
+                | types::ItemEnum::Use(_)
                 | types::ItemEnum::AssocConst { .. }
                 | types::ItemEnum::AssocType { .. } => true,
                 types::ItemEnum::ExternCrate { .. }
@@ -208,7 +208,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                 | types::ItemEnum::TypeAlias(_)
                 | types::ItemEnum::Constant { .. }
                 | types::ItemEnum::Static(_)
-                | types::ItemEnum::ForeignType
+                | types::ItemEnum::ExternType
                 | types::ItemEnum::Macro(_)
                 | types::ItemEnum::ProcMacro(_) => false,
             };
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 40a90c1a565..ef21779b099 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob as [`Crate::format_version`].
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-pub const FORMAT_VERSION: u32 = 33;
+pub const FORMAT_VERSION: u32 = 34;
 
 /// The root of the emitted JSON blob.
 ///
@@ -194,7 +194,7 @@ pub enum GenericArgs {
         /// ```
         args: Vec<GenericArg>,
         /// Associated type or constant bindings (e.g. `Item=i32` or `Item: Clone`) for this type.
-        bindings: Vec<TypeBinding>,
+        constraints: Vec<AssocItemConstraint>,
     },
     /// `Fn(A, B) -> C`
     Parenthesized {
@@ -258,19 +258,19 @@ pub struct Constant {
 ///              ^^^^^^^^^^  ^^^^^^^^^^^^^^^
 /// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct TypeBinding {
+pub struct AssocItemConstraint {
     /// The name of the associated type/constant.
     pub name: String,
     /// Arguments provided to the associated type/constant.
     pub args: GenericArgs,
     /// The kind of bound applied to the associated type/constant.
-    pub binding: TypeBindingKind,
+    pub binding: AssocItemConstraintKind,
 }
 
 /// The way in which an associate type/constant is bound.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
-pub enum TypeBindingKind {
+pub enum AssocItemConstraintKind {
     /// The required value/type is specified exactly. e.g.
     /// ```text
     /// Iterator<Item = u32, IntoIter: DoubleEndedIterator>
@@ -311,7 +311,7 @@ pub enum ItemKind {
     /// A crate imported via the `extern crate` syntax.
     ExternCrate,
     /// An import of 1 or more items into scope, using the `use` keyword.
-    Import,
+    Use,
     /// A `struct` declaration.
     Struct,
     /// A field of a struct.
@@ -341,7 +341,7 @@ pub enum ItemKind {
     /// `type`s from an `extern` block.
     ///
     /// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
-    ForeignType,
+    ExternType,
     /// A macro declaration.
     ///
     /// Corresponds to either `ItemEnum::Macro(_)`
@@ -386,7 +386,7 @@ pub enum ItemEnum {
         rename: Option<String>,
     },
     /// An import of 1 or more items into scope, using the `use` keyword.
-    Import(Import),
+    Use(Use),
 
     /// A `union` declaration.
     Union(Union),
@@ -429,7 +429,7 @@ pub enum ItemEnum {
     /// `type`s from an `extern` block.
     ///
     /// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
-    ForeignType,
+    ExternType,
 
     /// A macro_rules! declarative macro. Contains a single string with the source
     /// representation of the macro with the patterns stripped.
@@ -447,12 +447,19 @@ pub enum ItemEnum {
         /// The type of the constant.
         #[serde(rename = "type")]
         type_: Type,
-        /// The stringified expression for the default value, if provided, e.g.
+        /// Inside a trait declaration, this is the default value for the associated constant,
+        /// if provided.
+        /// Inside an `impl` block, this is the value assigned to the associated constant,
+        /// and will always be present.
+        ///
+        /// The representation is implementation-defined and not guaranteed to be representative of
+        /// either the resulting value or of the source code.
+        ///
         /// ```rust
         /// const X: usize = 640 * 1024;
         /// //               ^^^^^^^^^^
         /// ```
-        default: Option<String>,
+        value: Option<String>,
     },
     /// An associated type of a trait or a type.
     AssocType {
@@ -467,12 +474,16 @@ pub enum ItemEnum {
         /// }
         /// ```
         bounds: Vec<GenericBound>,
-        /// The default for this type, if provided, e.g.
+        /// Inside a trait declaration, this is the default for the associated type, if provided.
+        /// Inside an impl block, this is the type assigned to the associated type, and will always
+        /// be present.
+        ///
         /// ```rust
         /// type X = usize;
         /// //       ^^^^^
         /// ```
-        default: Option<Type>,
+        #[serde(rename = "type")]
+        type_: Option<Type>,
     },
 }
 
@@ -497,7 +508,7 @@ pub struct Union {
     /// The generic parameters and where clauses on this union.
     pub generics: Generics,
     /// Whether any fields have been removed from the result, due to being private or hidden.
-    pub fields_stripped: bool,
+    pub has_stripped_fields: bool,
     /// The list of fields in the union.
     ///
     /// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
@@ -554,7 +565,7 @@ pub enum StructKind {
         /// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
         fields: Vec<Id>,
         /// Whether any fields have been removed from the result, due to being private or hidden.
-        fields_stripped: bool,
+        has_stripped_fields: bool,
     },
 }
 
@@ -564,7 +575,7 @@ pub struct Enum {
     /// Information about the type parameters and `where` clauses of the enum.
     pub generics: Generics,
     /// Whether any variants have been removed from the result, due to being private or hidden.
-    pub variants_stripped: bool,
+    pub has_stripped_variants: bool,
     /// The list of variants in the enum.
     ///
     /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`]
@@ -621,7 +632,7 @@ pub enum VariantKind {
         /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`].
         fields: Vec<Id>,
         /// Whether any variants have been removed from the result, due to being private or hidden.
-        fields_stripped: bool,
+        has_stripped_fields: bool,
     },
 }
 
@@ -645,16 +656,13 @@ pub struct Discriminant {
 
 /// A set of fundamental properties of a function.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct Header {
+pub struct FunctionHeader {
     /// Is this function marked as `const`?
-    #[serde(rename = "const")]
-    pub const_: bool,
+    pub is_const: bool,
     /// Is this function unsafe?
-    #[serde(rename = "unsafe")]
-    pub unsafe_: bool,
+    pub is_unsafe: bool,
     /// Is this function async?
-    #[serde(rename = "async")]
-    pub async_: bool,
+    pub is_async: bool,
     /// The ABI used by the function.
     pub abi: Abi,
 }
@@ -697,11 +705,11 @@ pub enum Abi {
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Function {
     /// Information about the function signature, or declaration.
-    pub decl: FnDecl,
+    pub sig: FunctionSignature,
     /// Information about the function’s type parameters and `where` clauses.
     pub generics: Generics,
     /// Information about core properties of the function, e.g. whether it's `const`, its ABI, etc.
-    pub header: Header,
+    pub header: FunctionHeader,
     /// Whether the function has a body, i.e. an implementation.
     pub has_body: bool,
 }
@@ -784,7 +792,7 @@ pub enum GenericParamDefKind {
         /// In this example, the generic parameter named `impl Trait` (and which
         /// is bound by `Trait`) is synthetic, because it was not originally in
         /// the Rust source text.
-        synthetic: bool,
+        is_synthetic: bool,
     },
 
     /// Denotes a constant parameter.
@@ -894,7 +902,7 @@ pub enum TraitBoundModifier {
 }
 
 /// Either a type or a constant, usually stored as the right-hand side of an equation in places like
-/// [`TypeBinding`]
+/// [`AssocItemConstraint`]
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum Term {
@@ -963,7 +971,7 @@ pub enum Type {
     /// A raw pointer type, e.g. `*mut u32`, `*const u8`, etc.
     RawPointer {
         /// This is `true` for `*mut _` and `false` for `*const _`.
-        mutable: bool,
+        is_mutable: bool,
         /// The type of the pointee.
         #[serde(rename = "type")]
         type_: Box<Type>,
@@ -973,7 +981,7 @@ pub enum Type {
         /// The name of the lifetime of the reference, if provided.
         lifetime: Option<String>,
         /// This is `true` for `&mut i32` and `false` for `&i32`
-        mutable: bool,
+        is_mutable: bool,
         /// The type of the pointee, e.g. the `i32` in `&'a mut i32`
         #[serde(rename = "type")]
         type_: Box<Type>,
@@ -1036,7 +1044,7 @@ pub struct Path {
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct FunctionPointer {
     /// The signature of the function.
-    pub decl: FnDecl,
+    pub sig: FunctionSignature,
     /// Used for Higher-Rank Trait Bounds (HRTBs)
     ///
     /// ```ignore (incomplete expression)
@@ -1045,12 +1053,12 @@ pub struct FunctionPointer {
     /// ```
     pub generic_params: Vec<GenericParamDef>,
     /// The core properties of the function, such as the ABI it conforms to, whether it's unsafe, etc.
-    pub header: Header,
+    pub header: FunctionHeader,
 }
 
 /// The signature of a function.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct FnDecl {
+pub struct FunctionSignature {
     /// List of argument names and their type.
     ///
     /// Note that not all names will be valid identifiers, as some of
@@ -1063,7 +1071,7 @@ pub struct FnDecl {
     /// ```ignore (incomplete code)
     /// fn printf(fmt: &str, ...);
     /// ```
-    pub c_variadic: bool,
+    pub is_c_variadic: bool,
 }
 
 /// A `trait` declaration.
@@ -1127,10 +1135,10 @@ pub struct Impl {
     /// The list of associated items contained in this impl block.
     pub items: Vec<Id>,
     /// Whether this is a negative impl (e.g. `!Sized` or `!Send`).
-    pub negative: bool,
+    pub is_negative: bool,
     /// Whether this is an impl that’s implied by the compiler
     /// (for autotraits, e.g. `Send` or `Sync`).
-    pub synthetic: bool,
+    pub is_synthetic: bool,
     // FIXME: document this
     pub blanket_impl: Option<Type>,
 }
@@ -1138,7 +1146,7 @@ pub struct Impl {
 /// A `use` statement.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
-pub struct Import {
+pub struct Use {
     /// The full path being imported.
     pub source: String,
     /// May be different from the last segment of `source` when renaming imports:
@@ -1150,7 +1158,7 @@ pub struct Import {
     /// ```
     pub id: Option<Id>,
     /// Whether this statement is a wildcard `use`, e.g. `use source::*;`
-    pub glob: bool,
+    pub is_glob: bool,
 }
 
 /// A procedural macro.
@@ -1205,7 +1213,7 @@ pub struct Static {
     #[serde(rename = "type")]
     pub type_: Type,
     /// This is `true` for mutable statics, declared as `static mut X: T = f();`
-    pub mutable: bool,
+    pub is_mutable: bool,
     /// The stringified expression for the initial value.
     ///
     /// It's not guaranteed that it'll match the actual source code for the initial value.
diff --git a/src/rustdoc-json-types/tests.rs b/src/rustdoc-json-types/tests.rs
index 1126d5f786f..b9363fcf1b7 100644
--- a/src/rustdoc-json-types/tests.rs
+++ b/src/rustdoc-json-types/tests.rs
@@ -4,7 +4,7 @@ use super::*;
 fn test_struct_info_roundtrip() {
     let s = ItemEnum::Struct(Struct {
         generics: Generics { params: vec![], where_predicates: vec![] },
-        kind: StructKind::Plain { fields: vec![], fields_stripped: false },
+        kind: StructKind::Plain { fields: vec![], has_stripped_fields: false },
         impls: vec![],
     });
 
@@ -23,7 +23,7 @@ fn test_struct_info_roundtrip() {
 fn test_union_info_roundtrip() {
     let u = ItemEnum::Union(Union {
         generics: Generics { params: vec![], where_predicates: vec![] },
-        fields_stripped: false,
+        has_stripped_fields: false,
         fields: vec![],
         impls: vec![],
     });
diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs
index 7d6ec475bad..51146831efa 100644
--- a/src/tools/jsondoclint/src/item_kind.rs
+++ b/src/tools/jsondoclint/src/item_kind.rs
@@ -5,7 +5,7 @@ use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary};
 pub(crate) enum Kind {
     Module,
     ExternCrate,
-    Import,
+    Use,
     Struct,
     StructField,
     Union,
@@ -18,7 +18,7 @@ pub(crate) enum Kind {
     TraitAlias,
     Impl,
     Static,
-    ForeignType,
+    ExternType,
     Macro,
     ProcAttribute,
     ProcDerive,
@@ -36,7 +36,7 @@ impl Kind {
         match self {
             Module => true,
             ExternCrate => true,
-            Import => true,
+            Use => true,
             Union => true,
             Struct => true,
             Enum => true,
@@ -50,7 +50,7 @@ impl Kind {
             Macro => true,
             ProcMacro => true,
             Primitive => true,
-            ForeignType => true,
+            ExternType => true,
 
             // FIXME(adotinthevoid): I'm not sure if these are correct
             Keyword => false,
@@ -69,7 +69,7 @@ impl Kind {
     pub fn can_appear_in_import(self) -> bool {
         match self {
             Kind::Variant => true,
-            Kind::Import => false,
+            Kind::Use => false,
             other => other.can_appear_in_mod(),
         }
     }
@@ -90,7 +90,7 @@ impl Kind {
 
             Kind::Module => false,
             Kind::ExternCrate => false,
-            Kind::Import => false,
+            Kind::Use => false,
             Kind::Struct => false,
             Kind::StructField => false,
             Kind::Union => false,
@@ -102,7 +102,7 @@ impl Kind {
             Kind::TraitAlias => false,
             Kind::Impl => false,
             Kind::Static => false,
-            Kind::ForeignType => false,
+            Kind::ExternType => false,
             Kind::Macro => false,
             Kind::ProcAttribute => false,
             Kind::ProcDerive => false,
@@ -135,7 +135,7 @@ impl Kind {
         use Kind::*;
         match i.inner {
             ItemEnum::Module(_) => Module,
-            ItemEnum::Import(_) => Import,
+            ItemEnum::Use(_) => Use,
             ItemEnum::Union(_) => Union,
             ItemEnum::Struct(_) => Struct,
             ItemEnum::StructField(_) => StructField,
@@ -151,7 +151,7 @@ impl Kind {
             ItemEnum::Macro(_) => Macro,
             ItemEnum::ProcMacro(_) => ProcMacro,
             ItemEnum::Primitive(_) => Primitive,
-            ItemEnum::ForeignType => ForeignType,
+            ItemEnum::ExternType => ExternType,
             ItemEnum::ExternCrate { .. } => ExternCrate,
             ItemEnum::AssocConst { .. } => AssocConst,
             ItemEnum::AssocType { .. } => AssocType,
@@ -166,10 +166,10 @@ impl Kind {
             ItemKind::Constant => Constant,
             ItemKind::Enum => Enum,
             ItemKind::ExternCrate => ExternCrate,
-            ItemKind::ForeignType => ForeignType,
+            ItemKind::ExternType => ExternType,
             ItemKind::Function => Function,
             ItemKind::Impl => Impl,
-            ItemKind::Import => Import,
+            ItemKind::Use => Use,
             ItemKind::Keyword => Keyword,
             ItemKind::Macro => Macro,
             ItemKind::Module => Module,
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index 0ffb96bef29..10ab62cb24a 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -2,10 +2,11 @@ use std::collections::HashSet;
 use std::hash::Hash;
 
 use rustdoc_json_types::{
-    Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs,
-    GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, Path,
-    Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeAlias,
-    TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate,
+    AssocItemConstraint, AssocItemConstraintKind, Constant, Crate, DynTrait, Enum, Function,
+    FunctionPointer, FunctionSignature, GenericArg, GenericArgs, GenericBound, GenericParamDef,
+    Generics, Id, Impl, ItemEnum, ItemSummary, Module, Path, Primitive, ProcMacro, Static, Struct,
+    StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Union, Use, Variant, VariantKind,
+    WherePredicate,
 };
 use serde_json::Value;
 
@@ -90,7 +91,7 @@ impl<'a> Validator<'a> {
             item.links.values().for_each(|id| self.add_any_id(id));
 
             match &item.inner {
-                ItemEnum::Import(x) => self.check_import(x),
+                ItemEnum::Use(x) => self.check_use(x),
                 ItemEnum::Union(x) => self.check_union(x),
                 ItemEnum::Struct(x) => self.check_struct(x),
                 ItemEnum::StructField(x) => self.check_struct_field(x),
@@ -106,18 +107,18 @@ impl<'a> Validator<'a> {
                     self.check_constant(const_);
                 }
                 ItemEnum::Static(x) => self.check_static(x),
-                ItemEnum::ForeignType => {} // nop
+                ItemEnum::ExternType => {} // nop
                 ItemEnum::Macro(x) => self.check_macro(x),
                 ItemEnum::ProcMacro(x) => self.check_proc_macro(x),
                 ItemEnum::Primitive(x) => self.check_primitive_type(x),
                 ItemEnum::Module(x) => self.check_module(x, id),
                 // FIXME: Why don't these have their own structs?
                 ItemEnum::ExternCrate { .. } => {}
-                ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_),
-                ItemEnum::AssocType { generics, bounds, default } => {
+                ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_),
+                ItemEnum::AssocType { generics, bounds, type_ } => {
                     self.check_generics(generics);
                     bounds.iter().for_each(|b| self.check_generic_bound(b));
-                    if let Some(ty) = default {
+                    if let Some(ty) = type_ {
                         self.check_type(ty);
                     }
                 }
@@ -133,8 +134,8 @@ impl<'a> Validator<'a> {
         module.items.iter().for_each(|i| self.add_mod_item_id(i));
     }
 
-    fn check_import(&mut self, x: &'a Import) {
-        if x.glob {
+    fn check_use(&mut self, x: &'a Use) {
+        if x.is_glob {
             self.add_glob_import_item_id(x.id.as_ref().unwrap());
         } else if let Some(id) = &x.id {
             self.add_import_item_id(id);
@@ -152,7 +153,7 @@ impl<'a> Validator<'a> {
         match &x.kind {
             StructKind::Unit => {}
             StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)),
-            StructKind::Plain { fields, fields_stripped: _ } => {
+            StructKind::Plain { fields, has_stripped_fields: _ } => {
                 fields.iter().for_each(|f| self.add_field_id(f))
             }
         }
@@ -187,7 +188,7 @@ impl<'a> Validator<'a> {
         match kind {
             VariantKind::Plain => {}
             VariantKind::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)),
-            VariantKind::Struct { fields, fields_stripped: _ } => {
+            VariantKind::Struct { fields, has_stripped_fields: _ } => {
                 fields.iter().for_each(|f| self.add_field_id(f))
             }
         }
@@ -195,7 +196,7 @@ impl<'a> Validator<'a> {
 
     fn check_function(&mut self, x: &'a Function) {
         self.check_generics(&x.generics);
-        self.check_fn_decl(&x.decl);
+        self.check_function_signature(&x.sig);
     }
 
     fn check_trait(&mut self, x: &'a Trait, id: &Id) {
@@ -267,8 +268,8 @@ impl<'a> Validator<'a> {
             Type::Array { type_, len: _ } => self.check_type(&**type_),
             Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)),
             Type::Infer => {}
-            Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_),
-            Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_),
+            Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
+            Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
             Type::QualifiedPath { name: _, args, self_type, trait_ } => {
                 self.check_generic_args(&**args);
                 self.check_type(&**self_type);
@@ -279,7 +280,7 @@ impl<'a> Validator<'a> {
         }
     }
 
-    fn check_fn_decl(&mut self, x: &'a FnDecl) {
+    fn check_function_signature(&mut self, x: &'a FunctionSignature) {
         x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty));
         if let Some(output) = &x.output {
             self.check_type(output);
@@ -309,9 +310,9 @@ impl<'a> Validator<'a> {
 
     fn check_generic_args(&mut self, x: &'a GenericArgs) {
         match x {
-            GenericArgs::AngleBracketed { args, bindings } => {
+            GenericArgs::AngleBracketed { args, constraints } => {
                 args.iter().for_each(|arg| self.check_generic_arg(arg));
-                bindings.iter().for_each(|bind| self.check_type_binding(bind));
+                constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
             }
             GenericArgs::Parenthesized { inputs, output } => {
                 inputs.iter().for_each(|ty| self.check_type(ty));
@@ -325,7 +326,7 @@ impl<'a> Validator<'a> {
     fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
         match &gpd.kind {
             rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}
-            rustdoc_json_types::GenericParamDefKind::Type { bounds, default, synthetic: _ } => {
+            rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {
                 bounds.iter().for_each(|b| self.check_generic_bound(b));
                 if let Some(ty) = default {
                     self.check_type(ty);
@@ -346,11 +347,11 @@ impl<'a> Validator<'a> {
         }
     }
 
-    fn check_type_binding(&mut self, bind: &'a TypeBinding) {
+    fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
         self.check_generic_args(&bind.args);
         match &bind.binding {
-            TypeBindingKind::Equality(term) => self.check_term(term),
-            TypeBindingKind::Constraint(bounds) => {
+            AssocItemConstraintKind::Equality(term) => self.check_term(term),
+            AssocItemConstraintKind::Constraint(bounds) => {
                 bounds.iter().for_each(|b| self.check_generic_bound(b))
             }
         }
@@ -388,7 +389,7 @@ impl<'a> Validator<'a> {
     }
 
     fn check_function_pointer(&mut self, fp: &'a FunctionPointer) {
-        self.check_fn_decl(&fp.decl);
+        self.check_function_signature(&fp.sig);
         fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));
     }
 
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 4b7f3483ff7..d8cfa5b886d 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -37,8 +37,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
 
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
-
-[target.'cfg(target_os = "linux")'.dependencies]
 libffi = "3.2.0"
 libloading = "0.8"
 
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 5821adb96ce..dbb0e8a2925 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -383,7 +383,7 @@ to Miri failing to detect cases of undefined behavior in a program.
   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 Linux hosts for now.
+  they will fail horribly). It also only works on Unix 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
diff --git a/src/tools/miri/miri.bat b/src/tools/miri/miri.bat
index 6f9a8f38d65..b046b6310dd 100644
--- a/src/tools/miri/miri.bat
+++ b/src/tools/miri/miri.bat
@@ -5,8 +5,8 @@ set MIRI_SCRIPT_TARGET_DIR=%0\..\miri-script\target
 
 :: If any other steps are added, the "|| exit /b" must be appended to early
 :: return from the script. If not, it will continue execution.
-cargo +stable build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
-  || (echo Failed to build miri-script. Is the 'stable' toolchain installed? & exit /b)
+cargo +nightly build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
+  || (echo Failed to build miri-script. Is the 'nightly' toolchain installed? & exit /b)
 
 :: Forwards all arguments to this file to the executable.
 :: We invoke the binary directly to avoid going through rustup, which would set some extra
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index c33d9ad8614..3fdad2a91e9 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-0d634185dfddefe09047881175f35c65d68dcff1
+54fdef7799d9ff9470bb5cabd29fde9471a99eaa
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 76c68add8cd..c19a962e652 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -5,7 +5,6 @@ mod reuse_pool;
 
 use std::cell::RefCell;
 use std::cmp::max;
-use std::collections::hash_map::Entry;
 
 use rand::Rng;
 
@@ -151,6 +150,95 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
     }
 
+    fn addr_from_alloc_id_uncached(
+        &self,
+        global_state: &mut GlobalStateInner,
+        alloc_id: AllocId,
+        memory_kind: MemoryKind,
+    ) -> InterpResult<'tcx, u64> {
+        let ecx = self.eval_context_ref();
+        let mut rng = ecx.machine.rng.borrow_mut();
+        let (size, align, kind) = ecx.get_alloc_info(alloc_id);
+        // This is either called immediately after allocation (and then cached), or when
+        // adjusting `tcx` pointers (which never get freed). So assert that we are looking
+        // at a live allocation. This also ensures that we never re-assign an address to an
+        // allocation that previously had an address, but then was freed and the address
+        // information was removed.
+        assert!(!matches!(kind, AllocKind::Dead));
+
+        // This allocation does not have a base address yet, pick or reuse one.
+        if ecx.machine.native_lib.is_some() {
+            // In native lib mode, we use the "real" address of the bytes for this allocation.
+            // This ensures the interpreted program and native code have the same view of memory.
+            let base_ptr = match kind {
+                AllocKind::LiveData => {
+                    if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
+                        // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
+                        let prepared_bytes = MiriAllocBytes::zeroed(size, align)
+                            .unwrap_or_else(|| {
+                                panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
+                            });
+                        let ptr = prepared_bytes.as_ptr();
+                        // Store prepared allocation space to be picked up for use later.
+                        global_state
+                            .prepared_alloc_bytes
+                            .try_insert(alloc_id, prepared_bytes)
+                            .unwrap();
+                        ptr
+                    } else {
+                        ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
+                    }
+                }
+                AllocKind::Function | AllocKind::VTable => {
+                    // Allocate some dummy memory to get a unique address for this function/vtable.
+                    let alloc_bytes =
+                        MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
+                    let ptr = alloc_bytes.as_ptr();
+                    // Leak the underlying memory to ensure it remains unique.
+                    std::mem::forget(alloc_bytes);
+                    ptr
+                }
+                AllocKind::Dead => unreachable!(),
+            };
+            // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
+            return Ok(base_ptr.expose_provenance().try_into().unwrap());
+        }
+        // We are not in native lib mode, so we control the addresses ourselves.
+        if let Some((reuse_addr, clock)) =
+            global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, ecx.active_thread())
+        {
+            if let Some(clock) = clock {
+                ecx.acquire_clock(&clock);
+            }
+            Ok(reuse_addr)
+        } else {
+            // We have to pick a fresh address.
+            // Leave some space to the previous allocation, to give it some chance to be less aligned.
+            // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
+            let slack = rng.gen_range(0..16);
+            // From next_base_addr + slack, round up to adjust for alignment.
+            let base_addr = global_state
+                .next_base_addr
+                .checked_add(slack)
+                .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
+            let base_addr = align_addr(base_addr, align.bytes());
+
+            // Remember next base address.  If this allocation is zero-sized, leave a gap of at
+            // least 1 to avoid two allocations having the same base address. (The logic in
+            // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
+            // need to be distinguishable!)
+            global_state.next_base_addr = base_addr
+                .checked_add(max(size.bytes(), 1))
+                .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
+            // Even if `Size` didn't overflow, we might still have filled up the address space.
+            if global_state.next_base_addr > ecx.target_usize_max() {
+                throw_exhaust!(AddressSpaceFull);
+            }
+
+            Ok(base_addr)
+        }
+    }
+
     fn addr_from_alloc_id(
         &self,
         alloc_id: AllocId,
@@ -160,98 +248,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
         let global_state = &mut *global_state;
 
-        Ok(match global_state.base_addr.entry(alloc_id) {
-            Entry::Occupied(entry) => *entry.get(),
-            Entry::Vacant(entry) => {
-                let mut rng = ecx.machine.rng.borrow_mut();
-                let (size, align, kind) = ecx.get_alloc_info(alloc_id);
-                // This is either called immediately after allocation (and then cached), or when
-                // adjusting `tcx` pointers (which never get freed). So assert that we are looking
-                // at a live allocation. This also ensures that we never re-assign an address to an
-                // allocation that previously had an address, but then was freed and the address
-                // information was removed.
-                assert!(!matches!(kind, AllocKind::Dead));
-
-                // This allocation does not have a base address yet, pick or reuse one.
-                let base_addr = if ecx.machine.native_lib.is_some() {
-                    // In native lib mode, we use the "real" address of the bytes for this allocation.
-                    // This ensures the interpreted program and native code have the same view of memory.
-                    match kind {
-                        AllocKind::LiveData => {
-                            let ptr = if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
-                                // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
-                                let prepared_bytes = MiriAllocBytes::zeroed(size, align)
-                                    .unwrap_or_else(|| {
-                                        panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
-                                    });
-                                let ptr = prepared_bytes.as_ptr();
-                                    // Store prepared allocation space to be picked up for use later.
-                                    global_state.prepared_alloc_bytes.try_insert(alloc_id, prepared_bytes).unwrap();
-                                ptr
-                            } else {
-                                ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
-                            };
-                            // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
-                            ptr.expose_provenance().try_into().unwrap()
-                        }
-                        AllocKind::Function | AllocKind::VTable => {
-                            // Allocate some dummy memory to get a unique address for this function/vtable.
-                            let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
-                            // We don't need to expose these bytes as nobody is allowed to access them.
-                            let addr = alloc_bytes.as_ptr().addr().try_into().unwrap();
-                            // Leak the underlying memory to ensure it remains unique.
-                            std::mem::forget(alloc_bytes);
-                            addr
-                        }
-                        AllocKind::Dead => unreachable!()
-                    }
-                } else if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(                    
-                    &mut *rng,
-                    size,
-                    align,
-                    memory_kind,
-                    ecx.active_thread(),
-                ) {
-                    if let Some(clock) = clock {
-                        ecx.acquire_clock(&clock);
-                    }
-                    reuse_addr
-                } else {
-                    // We have to pick a fresh address.
-                    // Leave some space to the previous allocation, to give it some chance to be less aligned.
-                    // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
-                    let slack = rng.gen_range(0..16);
-                    // From next_base_addr + slack, round up to adjust for alignment.
-                    let base_addr = global_state
-                        .next_base_addr
-                        .checked_add(slack)
-                        .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
-                    let base_addr = align_addr(base_addr, align.bytes());
-
-                    // Remember next base address.  If this allocation is zero-sized, leave a gap
-                    // of at least 1 to avoid two allocations having the same base address.
-                    // (The logic in `alloc_id_from_addr` assumes unique addresses, and different
-                    // function/vtable pointers need to be distinguishable!)
-                    global_state.next_base_addr = base_addr
-                        .checked_add(max(size.bytes(), 1))
-                        .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
-                    // Even if `Size` didn't overflow, we might still have filled up the address space.
-                    if global_state.next_base_addr > ecx.target_usize_max() {
-                        throw_exhaust!(AddressSpaceFull);
-                    }
-
-                    base_addr
-                };
-                trace!(
-                    "Assigning base address {:#x} to allocation {:?} (size: {}, align: {})",
-                    base_addr,
-                    alloc_id,
-                    size.bytes(),
-                    align.bytes(),
-                );
+        match global_state.base_addr.get(&alloc_id) {
+            Some(&addr) => Ok(addr),
+            None => {
+                // First time we're looking for the absolute address of this allocation.
+                let base_addr =
+                    self.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
+                trace!("Assigning base address {:#x} to allocation {:?}", base_addr, alloc_id);
 
                 // Store address in cache.
-                entry.insert(base_addr);
+                global_state.base_addr.try_insert(alloc_id, base_addr).unwrap();
 
                 // Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
                 // We have a fast-path for the common case that this address is bigger than all previous ones.
@@ -269,9 +275,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 };
                 global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
 
-                base_addr
+                Ok(base_addr)
             }
-        })
+        }
     }
 }
 
@@ -359,16 +365,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     // This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
     // memory space in the past, or by doing the pre-allocation right upon being called.
-    fn get_global_alloc_bytes(&self, id: AllocId, kind: MemoryKind, bytes: &[u8], align: Align) -> InterpResult<'tcx, MiriAllocBytes> {
+    fn get_global_alloc_bytes(
+        &self,
+        id: AllocId,
+        kind: MemoryKind,
+        bytes: &[u8],
+        align: Align,
+    ) -> InterpResult<'tcx, MiriAllocBytes> {
         let ecx = self.eval_context_ref();
-        Ok(if ecx.machine.native_lib.is_some() {
+        if ecx.machine.native_lib.is_some() {
             // In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
-            // This additional call ensures that some `MiriAllocBytes` are always prepared.
+            // This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
+            // this function gets called before the first time `addr_from_alloc_id` gets called.
             ecx.addr_from_alloc_id(id, kind)?;
-            let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
             // The memory we need here will have already been allocated during an earlier call to
             // `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
             // fetch the previously prepared bytes from `prepared_alloc_bytes`.
+            let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
             let mut prepared_alloc_bytes = global_state
                 .prepared_alloc_bytes
                 .remove(&id)
@@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             assert_eq!(prepared_alloc_bytes.len(), bytes.len());
             // Copy allocation contents into prepared memory.
             prepared_alloc_bytes.copy_from_slice(bytes);
-            prepared_alloc_bytes
+            Ok(prepared_alloc_bytes)
         } else {
-            MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(&*bytes), align)
-        })
+            Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
+        }
     }
 
     /// When a pointer is used for a memory access, this computes where in which allocation the
diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs
index c23e5737280..d709e4e6eae 100644
--- a/src/tools/miri/src/concurrency/init_once.rs
+++ b/src/tools/miri/src/concurrency/init_once.rs
@@ -35,8 +35,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         offset: u64,
     ) -> InterpResult<'tcx, InitOnceId> {
         let this = self.eval_context_mut();
-        this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.init_onces)?
-            .ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
+        this.get_or_create_id(
+            lock_op,
+            lock_layout,
+            offset,
+            |ecx| &mut ecx.machine.sync.init_onces,
+            |_| Ok(Default::default()),
+        )?
+        .ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
     }
 
     #[inline]
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index d972831c768..97e910df6a2 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -66,6 +66,27 @@ pub(super) use declare_id;
 
 declare_id!(MutexId);
 
+/// The mutex kind.
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum MutexKind {
+    Invalid,
+    Normal,
+    Default,
+    Recursive,
+    ErrorCheck,
+}
+
+#[derive(Debug)]
+/// Additional data that may be used by shim implementations.
+pub struct AdditionalMutexData {
+    /// The mutex kind, used by some mutex implementations like pthreads mutexes.
+    pub kind: MutexKind,
+
+    /// The address of the mutex.
+    pub address: u64,
+}
+
 /// The mutex state.
 #[derive(Default, Debug)]
 struct Mutex {
@@ -77,6 +98,9 @@ struct Mutex {
     queue: VecDeque<ThreadId>,
     /// Mutex clock. This tracks the moment of the last unlock.
     clock: VClock,
+
+    /// Additional data that can be set by shim implementations.
+    data: Option<AdditionalMutexData>,
 }
 
 declare_id!(RwLockId);
@@ -168,13 +192,15 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Returns `None` if memory stores a non-zero invalid ID.
     ///
     /// `get_objs` must return the `IndexVec` that stores all the objects of this type.
+    /// `create_obj` must create the new object if initialization is needed.
     #[inline]
-    fn get_or_create_id<Id: SyncId + Idx, T: Default>(
+    fn get_or_create_id<Id: SyncId + Idx, T>(
         &mut self,
         lock_op: &OpTy<'tcx>,
         lock_layout: TyAndLayout<'tcx>,
         offset: u64,
         get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
+        create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
     ) -> InterpResult<'tcx, Option<Id>> {
         let this = self.eval_context_mut();
         let value_place =
@@ -196,7 +222,8 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
             // We set the in-memory ID to `next_index`, now also create this object in the machine
             // state.
-            let new_index = get_objs(this).push(T::default());
+            let obj = create_obj(this)?;
+            let new_index = get_objs(this).push(obj);
             assert_eq!(next_index, new_index);
             Some(new_index)
         } else {
@@ -210,6 +237,32 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         })
     }
 
+    /// Eagerly creates a Miri sync structure.
+    ///
+    /// `create_id` will store the index of the sync_structure in the memory pointed to by
+    /// `lock_op`, so that future calls to `get_or_create_id` will see it as initialized.
+    /// - `lock_op` must hold a pointer to the sync structure.
+    /// - `lock_layout` must be the memory layout of the sync structure.
+    /// - `offset` must be the offset inside the sync structure where its miri id will be stored.
+    /// - `get_objs` is described in `get_or_create_id`.
+    /// - `obj` must be the new sync object.
+    fn create_id<Id: SyncId + Idx, T>(
+        &mut self,
+        lock_op: &OpTy<'tcx>,
+        lock_layout: TyAndLayout<'tcx>,
+        offset: u64,
+        get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
+        obj: T,
+    ) -> InterpResult<'tcx, Id> {
+        let this = self.eval_context_mut();
+        let value_place =
+            this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
+
+        let new_index = get_objs(this).push(obj);
+        this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
+        Ok(new_index)
+    }
+
     fn condvar_reacquire_mutex(
         &mut self,
         mutex: MutexId,
@@ -236,15 +289,53 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 // situations.
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    /// Eagerly create and initialize a new mutex.
+    fn mutex_create(
+        &mut self,
+        lock_op: &OpTy<'tcx>,
+        lock_layout: TyAndLayout<'tcx>,
+        offset: u64,
+        data: Option<AdditionalMutexData>,
+    ) -> InterpResult<'tcx, MutexId> {
+        let this = self.eval_context_mut();
+        this.create_id(
+            lock_op,
+            lock_layout,
+            offset,
+            |ecx| &mut ecx.machine.sync.mutexes,
+            Mutex { data, ..Default::default() },
+        )
+    }
+
+    /// Lazily create a new mutex.
+    /// `initialize_data` must return any additional data that a user wants to associate with the mutex.
     fn mutex_get_or_create_id(
         &mut self,
         lock_op: &OpTy<'tcx>,
         lock_layout: TyAndLayout<'tcx>,
         offset: u64,
+        initialize_data: impl for<'a> FnOnce(
+            &'a mut MiriInterpCx<'tcx>,
+        ) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
     ) -> InterpResult<'tcx, MutexId> {
         let this = self.eval_context_mut();
-        this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.mutexes)?
-            .ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
+        this.get_or_create_id(
+            lock_op,
+            lock_layout,
+            offset,
+            |ecx| &mut ecx.machine.sync.mutexes,
+            |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
+        )?
+        .ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
+    }
+
+    /// Retrieve the additional data stored for a mutex.
+    fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData>
+    where
+        'tcx: 'a,
+    {
+        let this = self.eval_context_ref();
+        this.machine.sync.mutexes[id].data.as_ref()
     }
 
     fn rwlock_get_or_create_id(
@@ -254,8 +345,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         offset: u64,
     ) -> InterpResult<'tcx, RwLockId> {
         let this = self.eval_context_mut();
-        this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.rwlocks)?
-            .ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
+        this.get_or_create_id(
+            lock_op,
+            lock_layout,
+            offset,
+            |ecx| &mut ecx.machine.sync.rwlocks,
+            |_| Ok(Default::default()),
+        )?
+        .ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
     }
 
     fn condvar_get_or_create_id(
@@ -265,8 +362,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         offset: u64,
     ) -> InterpResult<'tcx, CondvarId> {
         let this = self.eval_context_mut();
-        this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.condvars)?
-            .ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
+        this.get_or_create_id(
+            lock_op,
+            lock_layout,
+            offset,
+            |ecx| &mut ecx.machine.sync.condvars,
+            |_| Ok(Default::default()),
+        )?
+        .ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
     }
 
     #[inline]
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 79f292f6965..306245a843b 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -888,8 +888,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
             // We make a full copy of this allocation.
-            let mut alloc =
-                alloc.inner().adjust_from_tcx(&this.tcx, |bytes, align| Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)), |ptr| this.global_root_pointer(ptr))?;
+            let mut alloc = alloc.inner().adjust_from_tcx(
+                &this.tcx,
+                |bytes, align| {
+                    Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
+                },
+                |ptr| this.global_root_pointer(ptr),
+            )?;
             // This allocation will be deallocated when the thread dies, so it is not in read-only memory.
             alloc.mutability = Mutability::Mut;
             // Create a fresh allocation with this content.
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 8a59206943d..85b4b02ff5f 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -133,7 +133,10 @@ pub use crate::concurrency::{
     cpu_affinity::MAX_CPUS,
     data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
     init_once::{EvalContextExt as _, InitOnceId},
-    sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
+    sync::{
+        AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
+        SynchronizationObjects,
+    },
     thread::{
         BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
         TimeoutAnchor, TimeoutClock, UnblockCallback,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 66c6966d1a6..2cd57e72871 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -535,9 +535,9 @@ pub struct MiriMachine<'tcx> {
     pub(crate) basic_block_count: u64,
 
     /// Handle of the optional shared object file for native functions.
-    #[cfg(target_os = "linux")]
+    #[cfg(unix)]
     pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
-    #[cfg(not(target_os = "linux"))]
+    #[cfg(not(unix))]
     pub native_lib: Option<!>,
 
     /// Run a garbage collector for BorTags every N basic blocks.
@@ -678,7 +678,7 @@ impl<'tcx> MiriMachine<'tcx> {
             report_progress: config.report_progress,
             basic_block_count: 0,
             clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
-            #[cfg(target_os = "linux")]
+            #[cfg(unix)]
             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.
@@ -700,9 +700,9 @@ impl<'tcx> MiriMachine<'tcx> {
                     lib_file_path.clone(),
                 )
             }),
-            #[cfg(not(target_os = "linux"))]
+            #[cfg(not(unix))]
             native_lib: config.native_lib.as_ref().map(|_| {
-                panic!("loading external .so files is only supported on Linux")
+                panic!("calling functions from native libraries via FFI is only supported on Unix")
             }),
             gc_interval: config.gc_interval,
             since_gc: 0,
@@ -1277,12 +1277,12 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
     {
         let kind = Self::GLOBAL_KIND.unwrap().into();
-        let alloc = alloc.adjust_from_tcx(&ecx.tcx,
+        let alloc = alloc.adjust_from_tcx(
+            &ecx.tcx,
             |bytes, align| ecx.get_global_alloc_bytes(id, kind, bytes, align),
             |ptr| ecx.global_root_pointer(ptr),
         )?;
-        let extra =
-            Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
+        let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
         Ok(Cow::Owned(alloc.with_extra(extra)))
     }
 
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index d40dbdba80f..1b45bc22038 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -226,7 +226,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // First deal with any external C functions in linked .so file.
-        #[cfg(target_os = "linux")]
+        #[cfg(unix)]
         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
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index 7d5349f26b1..618cf8cf200 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -2,7 +2,7 @@
 
 mod alloc;
 mod backtrace;
-#[cfg(target_os = "linux")]
+#[cfg(unix)]
 mod native_lib;
 mod unix;
 mod wasi;
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index 25d96fbd733..e4998c37f3f 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -240,13 +240,16 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
         ty::RawPtr(_, mutability) => {
             // Arbitrary mutable pointer accesses are not currently supported in Miri.
             if mutability.is_mut() {
-                throw_unsup_format!("unsupported mutable pointer type for native call: {}", v.layout.ty);
+                throw_unsup_format!(
+                    "unsupported mutable pointer type for native call: {}",
+                    v.layout.ty
+                );
             } else {
                 let s = v.to_scalar().to_pointer(cx)?.addr();
                 // This relies on the `expose_provenance` in `addr_from_alloc_id`.
                 CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
             }
-        },
+        }
         _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
     })
 }
diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs
index 5e5fccb587b..882c08cca15 100644
--- a/src/tools/miri/src/shims/unix/macos/sync.rs
+++ b/src/tools/miri/src/shims/unix/macos/sync.rs
@@ -19,7 +19,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // os_unfair_lock holds a 32-bit value, is initialized with zero and
         // must be assumed to be opaque. Therefore, we can just store our
         // internal mutex ID in the structure without anyone noticing.
-        this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0)
+        this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
     }
 }
 
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index 0b889b11822..57cc9cf4618 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -62,7 +62,6 @@ fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResu
 // pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
 // We ignore the platform layout and store our own fields:
 // - id: u32
-// - kind: i32
 
 fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
     let offset = match &*ecx.tcx.sess.target.os {
@@ -74,6 +73,8 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
 
     // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
     // the id must start out as 0.
+    // FIXME on some platforms (e.g linux) there are more static initializers for
+    // recursive or error checking mutexes. We should also add thme in this sanity check.
     static SANITY: AtomicBool = AtomicBool::new(false);
     if !SANITY.swap(true, Ordering::Relaxed) {
         let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
@@ -90,79 +91,92 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
     Ok(offset)
 }
 
-fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
-    // These offsets are picked for compatibility with Linux's static initializer
-    // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
-    let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
-
-    // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
-    // the kind must start out as PTHREAD_MUTEX_DEFAULT.
-    static SANITY: AtomicBool = AtomicBool::new(false);
-    if !SANITY.swap(true, Ordering::Relaxed) {
-        let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
-        let kind_field = static_initializer
-            .offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx)
-            .unwrap();
-        let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap();
-        assert_eq!(
-            kind,
-            ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
-            "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
-        );
-    }
-
-    offset
+/// Eagerly create and initialize a new mutex.
+fn mutex_create<'tcx>(
+    ecx: &mut MiriInterpCx<'tcx>,
+    mutex_op: &OpTy<'tcx>,
+    kind: i32,
+) -> InterpResult<'tcx> {
+    // FIXME: might be worth changing mutex_create to take the mplace
+    // rather than the `OpTy`.
+    let address = ecx.read_pointer(mutex_op)?.addr().bytes();
+    let kind = translate_kind(ecx, kind)?;
+    let data = Some(AdditionalMutexData { address, kind });
+    ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
+    Ok(())
 }
 
+/// Returns the `MutexId` of the mutex stored at `mutex_op`.
+///
+/// `mutex_get_id` will also check if the mutex has been moved since its first use and
+/// return an error if it has.
 fn mutex_get_id<'tcx>(
     ecx: &mut MiriInterpCx<'tcx>,
     mutex_op: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, MutexId> {
-    ecx.mutex_get_or_create_id(
+    let address = ecx.read_pointer(mutex_op)?.addr().bytes();
+
+    // FIXME: might be worth changing mutex_get_or_create_id to take the mplace
+    // rather than the `OpTy`.
+    let id = ecx.mutex_get_or_create_id(
         mutex_op,
         ecx.libc_ty_layout("pthread_mutex_t"),
         mutex_id_offset(ecx)?,
-    )
-}
+        |ecx| {
+            // This is called if a static initializer was used and the lock has not been assigned
+            // an ID yet. We have to determine the mutex kind from the static initializer.
+            let kind = kind_from_static_initializer(ecx, mutex_op)?;
+
+            Ok(Some(AdditionalMutexData { kind, address }))
+        },
+    )?;
+
+    // Check that the mutex has not been moved since last use.
+    let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
+    if data.address != address {
+        throw_ub_format!("pthread_mutex_t can't be moved after first use")
+    }
 
-fn mutex_reset_id<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
-    mutex_op: &OpTy<'tcx>,
-) -> InterpResult<'tcx, ()> {
-    ecx.deref_pointer_and_write(
-        mutex_op,
-        mutex_id_offset(ecx)?,
-        Scalar::from_u32(0),
-        ecx.libc_ty_layout("pthread_mutex_t"),
-        ecx.machine.layouts.u32,
-    )
+    Ok(id)
 }
 
-fn mutex_get_kind<'tcx>(
+/// Returns the kind of a static initializer.
+fn kind_from_static_initializer<'tcx>(
     ecx: &MiriInterpCx<'tcx>,
     mutex_op: &OpTy<'tcx>,
-) -> InterpResult<'tcx, i32> {
-    ecx.deref_pointer_and_read(
-        mutex_op,
-        mutex_kind_offset(ecx),
-        ecx.libc_ty_layout("pthread_mutex_t"),
-        ecx.machine.layouts.i32,
-    )?
-    .to_i32()
+) -> InterpResult<'tcx, MutexKind> {
+    // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
+    let kind = match &*ecx.tcx.sess.target.os {
+        "linux" => {
+            let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
+
+            ecx.deref_pointer_and_read(
+                mutex_op,
+                offset,
+                ecx.libc_ty_layout("pthread_mutex_t"),
+                ecx.machine.layouts.i32,
+            )?
+            .to_i32()?
+        }
+        | "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
+        os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
+    };
+
+    translate_kind(ecx, kind)
 }
 
-fn mutex_set_kind<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
-    mutex_op: &OpTy<'tcx>,
-    kind: i32,
-) -> InterpResult<'tcx, ()> {
-    ecx.deref_pointer_and_write(
-        mutex_op,
-        mutex_kind_offset(ecx),
-        Scalar::from_i32(kind),
-        ecx.libc_ty_layout("pthread_mutex_t"),
-        ecx.machine.layouts.i32,
-    )
+fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> {
+    Ok(if is_mutex_kind_default(ecx, kind)? {
+        MutexKind::Default
+    } else if is_mutex_kind_normal(ecx, kind)? {
+        MutexKind::Normal
+    } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
+        MutexKind::ErrorCheck
+    } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
+        MutexKind::Recursive
+    } else {
+        throw_unsup_format!("unsupported type of mutex: {kind}");
+    })
 }
 
 // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
@@ -452,10 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             mutexattr_get_kind(this, attr_op)?
         };
 
-        // Write 0 to use the same code path as the static initializers.
-        mutex_reset_id(this, mutex_op)?;
-
-        mutex_set_kind(this, mutex_op, kind)?;
+        mutex_create(this, mutex_op, kind)?;
 
         Ok(())
     }
@@ -467,8 +478,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let kind = mutex_get_kind(this, mutex_op)?;
         let id = mutex_get_id(this, mutex_op)?;
+        let kind =
+            this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
 
         let ret = if this.mutex_is_locked(id) {
             let owner_thread = this.mutex_get_owner(id);
@@ -477,19 +489,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 return Ok(());
             } else {
                 // Trying to acquire the same mutex again.
-                if is_mutex_kind_default(this, kind)? {
-                    throw_ub_format!("trying to acquire already locked default mutex");
-                } else if is_mutex_kind_normal(this, kind)? {
-                    throw_machine_stop!(TerminationInfo::Deadlock);
-                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
-                    this.eval_libc_i32("EDEADLK")
-                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
-                    this.mutex_lock(id);
-                    0
-                } else {
-                    throw_unsup_format!(
-                        "called pthread_mutex_lock on an unsupported type of mutex"
-                    );
+                match kind {
+                    MutexKind::Default =>
+                        throw_ub_format!("trying to acquire already locked default mutex"),
+                    MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
+                    MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
+                    MutexKind::Recursive => {
+                        this.mutex_lock(id);
+                        0
+                    }
+                    _ =>
+                        throw_unsup_format!(
+                            "called pthread_mutex_lock on an unsupported type of mutex"
+                        ),
                 }
             }
         } else {
@@ -504,26 +516,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let kind = mutex_get_kind(this, mutex_op)?;
         let id = mutex_get_id(this, mutex_op)?;
+        let kind =
+            this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
 
         Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
             let owner_thread = this.mutex_get_owner(id);
             if owner_thread != this.active_thread() {
                 this.eval_libc_i32("EBUSY")
             } else {
-                if is_mutex_kind_default(this, kind)?
-                    || is_mutex_kind_normal(this, kind)?
-                    || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
-                {
-                    this.eval_libc_i32("EBUSY")
-                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
-                    this.mutex_lock(id);
-                    0
-                } else {
-                    throw_unsup_format!(
-                        "called pthread_mutex_trylock on an unsupported type of mutex"
-                    );
+                match kind {
+                    MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
+                        this.eval_libc_i32("EBUSY"),
+                    MutexKind::Recursive => {
+                        this.mutex_lock(id);
+                        0
+                    }
+                    _ =>
+                        throw_unsup_format!(
+                            "called pthread_mutex_trylock on an unsupported type of mutex"
+                        ),
                 }
             }
         } else {
@@ -536,8 +548,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let kind = mutex_get_kind(this, mutex_op)?;
         let id = mutex_get_id(this, mutex_op)?;
+        let kind =
+            this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
 
         if let Some(_old_locked_count) = this.mutex_unlock(id)? {
             // The mutex was locked by the current thread.
@@ -546,20 +559,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // The mutex was locked by another thread or not locked at all. See
             // the “Unlock When Not Owner” column in
             // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
-            if is_mutex_kind_default(this, kind)? {
-                throw_ub_format!(
-                    "unlocked a default mutex that was not locked by the current thread"
-                );
-            } else if is_mutex_kind_normal(this, kind)? {
-                throw_ub_format!(
-                    "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
-                );
-            } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
-                || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
-            {
-                Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
-            } else {
-                throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
+            match kind {
+                MutexKind::Default =>
+                    throw_ub_format!(
+                        "unlocked a default mutex that was not locked by the current thread"
+                    ),
+                MutexKind::Normal =>
+                    throw_ub_format!(
+                        "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
+                    ),
+                MutexKind::ErrorCheck | MutexKind::Recursive =>
+                    Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
+                _ =>
+                    throw_unsup_format!(
+                        "called pthread_mutex_unlock on an unsupported type of mutex"
+                    ),
             }
         }
     }
@@ -574,7 +588,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
 
         // Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit.
-        mutex_get_kind(this, mutex_op)?;
         mutex_get_id(this, mutex_op)?;
 
         // This might lead to false positives, see comment in pthread_mutexattr_destroy
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr
new file mode 100644
index 00000000000..5ca6acc3fbe
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.init.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: pthread_mutex_t can't be moved after first use
+  --> $DIR/libc_pthread_mutex_move.rs:LL:CC
+   |
+LL |         libc::pthread_mutex_lock(&mut m2 as *mut _);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
+note: inside `main`
+  --> $DIR/libc_pthread_mutex_move.rs:LL:CC
+   |
+LL |     check();
+   |     ^^^^^^^
+
+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/concurrency/libc_pthread_mutex_move.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs
new file mode 100644
index 00000000000..229335c97ce
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.rs
@@ -0,0 +1,28 @@
+//@ignore-target-windows: No pthreads on Windows
+//@revisions: static_initializer init
+
+fn main() {
+    check();
+}
+
+#[cfg(init)]
+fn check() {
+    unsafe {
+        let mut m: libc::pthread_mutex_t = std::mem::zeroed();
+        assert_eq!(libc::pthread_mutex_init(&mut m as *mut _, std::ptr::null()), 0);
+
+        let mut m2 = m; // move the mutex
+        libc::pthread_mutex_lock(&mut m2 as *mut _); //~[init] ERROR: pthread_mutex_t can't be moved after first use
+    }
+}
+
+#[cfg(static_initializer)]
+fn check() {
+    unsafe {
+        let mut m: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
+        libc::pthread_mutex_lock(&mut m as *mut _);
+
+        let mut m2 = m; // move the mutex
+        libc::pthread_mutex_unlock(&mut m2 as *mut _); //~[static_initializer] ERROR: pthread_mutex_t can't be moved after first use
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr
new file mode 100644
index 00000000000..c3632eca43f
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_move.static_initializer.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: pthread_mutex_t can't be moved after first use
+  --> $DIR/libc_pthread_mutex_move.rs:LL:CC
+   |
+LL |         libc::pthread_mutex_unlock(&mut m2 as *mut _);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
+note: inside `main`
+  --> $DIR/libc_pthread_mutex_move.rs:LL:CC
+   |
+LL |     check();
+   |     ^^^^^^^
+
+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/native-lib/fail/function_not_in_so.rs b/src/tools/miri/tests/native-lib/fail/function_not_in_so.rs
index 3540c75b73a..c532d052245 100644
--- a/src/tools/miri/tests/native-lib/fail/function_not_in_so.rs
+++ b/src/tools/miri/tests/native-lib/fail/function_not_in_so.rs
@@ -1,4 +1,6 @@
-//@only-target-linux
+// Only works on Unix targets
+//@ignore-target-windows
+//@ignore-target-wasm
 //@only-on-host
 //@normalize-stderr-test: "OS `.*`" -> "$$OS"
 
diff --git a/src/tools/miri/tests/native-lib/fail/private_function.rs b/src/tools/miri/tests/native-lib/fail/private_function.rs
new file mode 100644
index 00000000000..3c6fda741dd
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/private_function.rs
@@ -0,0 +1,15 @@
+// Only works on Unix targets
+//@ignore-target-windows
+//@ignore-target-wasm
+//@only-on-host
+//@normalize-stderr-test: "OS `.*`" -> "$$OS"
+
+extern "C" {
+    fn not_exported();
+}
+
+fn main() {
+    unsafe {
+        not_exported(); //~ ERROR: unsupported operation: can't call foreign function `not_exported`
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/private_function.stderr b/src/tools/miri/tests/native-lib/fail/private_function.stderr
new file mode 100644
index 00000000000..e27a501ebb9
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/private_function.stderr
@@ -0,0 +1,15 @@
+error: unsupported operation: can't call foreign function `not_exported` on $OS
+  --> $DIR/private_function.rs:LL:CC
+   |
+LL |         not_exported();
+   |         ^^^^^^^^^^^^^^ can't call foreign function `not_exported` on $OS
+   |
+   = help: if this is a basic API commonly used on this target, please report an issue with Miri
+   = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/private_function.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/native-lib/native-lib.map b/src/tools/miri/tests/native-lib/native-lib.map
deleted file mode 100644
index 7e3bd19622a..00000000000
--- a/src/tools/miri/tests/native-lib/native-lib.map
+++ /dev/null
@@ -1,20 +0,0 @@
-CODEABI_1.0 {
-    # Define which symbols to export.
-    global:
-        # scalar_arguments.c
-        add_one_int;
-        printer;
-        test_stack_spill;
-        get_unsigned_int;
-        add_int16;
-        add_short_to_long;
-
-        # ptr_read_access.c
-        print_pointer;
-        access_simple;
-        access_nested;
-        access_static;
-
-    # The rest remains private.
-    local: *;
-};
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
index d8e6209839e..2990dfa8975 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
@@ -1,4 +1,6 @@
-//@only-target-linux
+// Only works on Unix targets
+//@ignore-target-windows
+//@ignore-target-wasm
 //@only-on-host
 
 fn main() {
@@ -26,7 +28,7 @@ fn test_pointer() {
 fn test_simple() {
     #[repr(C)]
     struct Simple {
-        field: i32
+        field: i32,
     }
 
     extern "C" {
@@ -41,7 +43,7 @@ fn test_simple() {
 // Test function that dereferences nested struct pointers and accesses fields.
 fn test_nested() {
     use std::ptr::NonNull;
-    
+
     #[derive(Debug, PartialEq, Eq)]
     #[repr(C)]
     struct Nested {
@@ -62,7 +64,6 @@ fn test_nested() {
 
 // Test function that dereferences static struct pointers and accesses fields.
 fn test_static() {
-
     #[repr(C)]
     struct Static {
         value: i32,
@@ -72,11 +73,8 @@ fn test_static() {
     extern "C" {
         fn access_static(n_ptr: *const Static) -> i32;
     }
-    
-    static STATIC: Static = Static {
-        value: 9001,
-        recurse: &STATIC,
-    };
+
+    static STATIC: Static = Static { value: 9001, recurse: &STATIC };
 
     assert_eq!(unsafe { access_static(&STATIC) }, 9001);
 }
diff --git a/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs b/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs
index 1e1d0b11e99..378baa7ce98 100644
--- a/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs
+++ b/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs
@@ -1,4 +1,6 @@
-//@only-target-linux
+// Only works on Unix targets
+//@ignore-target-windows
+//@ignore-target-wasm
 //@only-on-host
 
 extern "C" {
diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c
index 03b9189e2e8..540845d53a7 100644
--- a/src/tools/miri/tests/native-lib/ptr_read_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_read_access.c
@@ -1,8 +1,11 @@
 #include <stdio.h>
 
+// See comments in build_native_lib()
+#define EXPORT __attribute__((visibility("default")))
+
 /* Test: test_pointer */
 
-void print_pointer(const int *ptr) {
+EXPORT void print_pointer(const int *ptr) {
   printf("printing pointer dereference from C: %d\n", *ptr);
 }
 
@@ -12,7 +15,7 @@ typedef struct Simple {
   int field;
 } Simple;
 
-int access_simple(const Simple *s_ptr) {
+EXPORT int access_simple(const Simple *s_ptr) {
   return s_ptr->field;
 }
 
@@ -24,7 +27,7 @@ typedef struct Nested {
 } Nested;
 
 // Returns the innermost/last value of a Nested pointer chain.
-int access_nested(const Nested *n_ptr) {
+EXPORT int access_nested(const Nested *n_ptr) {
   // Edge case: `n_ptr == NULL` (i.e. first Nested is None).
   if (!n_ptr) { return 0; }
 
@@ -42,6 +45,6 @@ typedef struct Static {
     struct Static *recurse;
 } Static;
 
-int access_static(const Static *s_ptr) {
+EXPORT int access_static(const Static *s_ptr) {
   return s_ptr->recurse->recurse->value;
 }
diff --git a/src/tools/miri/tests/native-lib/scalar_arguments.c b/src/tools/miri/tests/native-lib/scalar_arguments.c
index 68714f1743b..6da730a4987 100644
--- a/src/tools/miri/tests/native-lib/scalar_arguments.c
+++ b/src/tools/miri/tests/native-lib/scalar_arguments.c
@@ -1,27 +1,35 @@
 #include <stdio.h>
 
-int add_one_int(int x) {
+// See comments in build_native_lib()
+#define EXPORT __attribute__((visibility("default")))
+
+EXPORT int add_one_int(int x) {
   return 2 + x;
 }
 
-void printer() {
+EXPORT void printer(void) {
   printf("printing from C\n");
 }
 
 // function with many arguments, to test functionality when some args are stored
 // on the stack
-int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
+EXPORT int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
   return a+b+c+d+e+f+g+h+i+j+k+l;
 }
 
-unsigned int get_unsigned_int() {
+EXPORT unsigned int get_unsigned_int(void) {
   return -10;
 }
 
-short add_int16(short x) {
+EXPORT short add_int16(short x) {
   return x + 3;
 }
 
-long add_short_to_long(short x, long y) {
+EXPORT long add_short_to_long(short x, long y) {
   return x + y;
 }
+
+// To test that functions not marked with EXPORT cannot be called by Miri.
+int not_exported(void) {
+  return 0;
+}
diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs
index c510ef95c30..e62394bc499 100644
--- a/src/tools/miri/tests/ui.rs
+++ b/src/tools/miri/tests/ui.rs
@@ -36,20 +36,21 @@ fn build_native_lib() -> PathBuf {
     // 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");
-    let so_file_path = so_target_dir.join("native-lib.so");
+    // We use a platform-neutral file extension to avoid having to hard-code alternatives.
+    let native_lib_path = so_target_dir.join("native-lib.module");
     let cc_output = Command::new(cc)
         .args([
             "-shared",
+            "-fPIC",
+            // We hide all symbols by default and export just the ones we need
+            // This is to future-proof against a more complex shared object which eg defines its own malloc
+            // (but we wouldn't want miri to call that, as it would if it was exported).
+            "-fvisibility=hidden",
             "-o",
-            so_file_path.to_str().unwrap(),
+            native_lib_path.to_str().unwrap(),
             // FIXME: Automate gathering of all relevant C source files in the directory.
             "tests/native-lib/scalar_arguments.c",
             "tests/native-lib/ptr_read_access.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/native-lib/native-lib.map",
             // Ensure we notice serious problems in the C code.
             "-Wall",
             "-Wextra",
@@ -64,7 +65,7 @@ fn build_native_lib() -> PathBuf {
             String::from_utf8_lossy(&cc_output.stderr),
         );
     }
-    so_file_path
+    native_lib_path
 }
 
 /// Does *not* set any args or env vars, since it is shared between the test runner and
@@ -300,7 +301,7 @@ fn main() -> Result<()> {
         WithDependencies,
         tmpdir.path(),
     )?;
-    if cfg!(target_os = "linux") {
+    if cfg!(unix) {
         ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
         ui(
             Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs
index 2522c4aeb93..16c4251998f 100644
--- a/src/tools/run-make-support/src/external_deps/llvm.rs
+++ b/src/tools/run-make-support/src/external_deps/llvm.rs
@@ -110,6 +110,13 @@ pub struct LlvmDwarfdump {
     cmd: Command,
 }
 
+/// A `llvm-pdbutil` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmPdbutil {
+    cmd: Command,
+}
+
 crate::macros::impl_common_helpers!(LlvmReadobj);
 crate::macros::impl_common_helpers!(LlvmProfdata);
 crate::macros::impl_common_helpers!(LlvmFilecheck);
@@ -118,6 +125,7 @@ crate::macros::impl_common_helpers!(LlvmAr);
 crate::macros::impl_common_helpers!(LlvmNm);
 crate::macros::impl_common_helpers!(LlvmBcanalyzer);
 crate::macros::impl_common_helpers!(LlvmDwarfdump);
+crate::macros::impl_common_helpers!(LlvmPdbutil);
 
 /// Generate the path to the bin directory of LLVM.
 #[must_use]
@@ -360,3 +368,19 @@ impl LlvmDwarfdump {
         self
     }
 }
+
+impl LlvmPdbutil {
+    /// Construct a new `llvm-pdbutil` invocation. This assumes that `llvm-pdbutil` is available
+    /// at `$LLVM_BIN_DIR/llvm-pdbutil`.
+    pub fn new() -> Self {
+        let llvm_pdbutil = llvm_bin_dir().join("llvm-pdbutil");
+        let cmd = Command::new(llvm_pdbutil);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index fefafb95b33..980bd37dca8 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -73,7 +73,7 @@ pub use env::{env_var, env_var_os, set_current_dir};
 pub use run::{cmd, run, run_fail, run_with_args};
 
 /// Helpers for checking target information.
-pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname};
+pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
 
 /// Helpers for building names of output artifacts that are potentially target-specific.
 pub use artifact_names::{
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index 5dcb0b83430..896abb73fc1 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -28,6 +28,24 @@ pub fn is_darwin() -> bool {
     target().contains("darwin")
 }
 
+/// Get the target OS on Apple operating systems.
+#[must_use]
+pub fn apple_os() -> &'static str {
+    if target().contains("darwin") {
+        "macos"
+    } else if target().contains("ios") {
+        "ios"
+    } else if target().contains("tvos") {
+        "tvos"
+    } else if target().contains("watchos") {
+        "watchos"
+    } else if target().contains("visionos") {
+        "visionos"
+    } else {
+        panic!("not an Apple OS")
+    }
+}
+
 /// Check if `component` is within `LLVM_COMPONENTS`
 #[must_use]
 pub fn llvm_components_contain(component: &str) -> bool {
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 22d1e93bf39..50d21c7ed4c 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -6,7 +6,6 @@ run-make/incr-add-rust-src-component/Makefile
 run-make/issue-84395-lto-embed-bitcode/Makefile
 run-make/jobserver-error/Makefile
 run-make/libs-through-symlinks/Makefile
-run-make/macos-deployment-target/Makefile
 run-make/split-debuginfo/Makefile
 run-make/symbol-mangling-hashed/Makefile
 run-make/translation/Makefile
diff --git a/src/tools/wasm-component-ld/README.md b/src/tools/wasm-component-ld/README.md
index 54608a2dea1..b51de9a9dce 100644
--- a/src/tools/wasm-component-ld/README.md
+++ b/src/tools/wasm-component-ld/README.md
@@ -1,7 +1,7 @@
 # `wasm-component-ld`
 
-This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate. That
-crate. That crate is itself a thin wrapper around two pieces:
+This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate.
+That crate is itself a thin wrapper around two pieces:
 
 * `wasm-ld` - the LLVM-based linker distributed as part of LLD and packaged in
   Rust as `rust-lld`.