diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 5d150eca00e..1f4092ad738 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -296,7 +296,6 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
 
 pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
 pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
-pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
 
 // Equivalent to the `NT_SUCCESS` C preprocessor macro.
 // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
@@ -1282,6 +1281,46 @@ extern "system" {
     ) -> NTSTATUS;
 }
 
+#[link(name = "ntdll")]
+extern "system" {
+    pub fn NtCreateFile(
+        FileHandle: *mut HANDLE,
+        DesiredAccess: ACCESS_MASK,
+        ObjectAttributes: *const OBJECT_ATTRIBUTES,
+        IoStatusBlock: *mut IO_STATUS_BLOCK,
+        AllocationSize: *mut i64,
+        FileAttributes: ULONG,
+        ShareAccess: ULONG,
+        CreateDisposition: ULONG,
+        CreateOptions: ULONG,
+        EaBuffer: *mut c_void,
+        EaLength: ULONG,
+    ) -> NTSTATUS;
+    pub fn NtReadFile(
+        FileHandle: BorrowedHandle<'_>,
+        Event: HANDLE,
+        ApcRoutine: Option<IO_APC_ROUTINE>,
+        ApcContext: *mut c_void,
+        IoStatusBlock: &mut IO_STATUS_BLOCK,
+        Buffer: *mut crate::mem::MaybeUninit<u8>,
+        Length: ULONG,
+        ByteOffset: Option<&LARGE_INTEGER>,
+        Key: Option<&ULONG>,
+    ) -> NTSTATUS;
+    pub fn NtWriteFile(
+        FileHandle: BorrowedHandle<'_>,
+        Event: HANDLE,
+        ApcRoutine: Option<IO_APC_ROUTINE>,
+        ApcContext: *mut c_void,
+        IoStatusBlock: &mut IO_STATUS_BLOCK,
+        Buffer: *const u8,
+        Length: ULONG,
+        ByteOffset: Option<&LARGE_INTEGER>,
+        Key: Option<&ULONG>,
+    ) -> NTSTATUS;
+    pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> ULONG;
+}
+
 // Functions that aren't available on every version of Windows that we support,
 // but we still use them and just provide some form of a fallback implementation.
 compat_fn_with_fallback! {
@@ -1322,52 +1361,6 @@ compat_fn_optional! {
 compat_fn_with_fallback! {
     pub static NTDLL: &CStr = ansi_str!("ntdll");
 
-    pub fn NtCreateFile(
-        FileHandle: *mut HANDLE,
-        DesiredAccess: ACCESS_MASK,
-        ObjectAttributes: *const OBJECT_ATTRIBUTES,
-        IoStatusBlock: *mut IO_STATUS_BLOCK,
-        AllocationSize: *mut i64,
-        FileAttributes: ULONG,
-        ShareAccess: ULONG,
-        CreateDisposition: ULONG,
-        CreateOptions: ULONG,
-        EaBuffer: *mut c_void,
-        EaLength: ULONG
-    ) -> NTSTATUS {
-        STATUS_NOT_IMPLEMENTED
-    }
-    pub fn NtReadFile(
-        FileHandle: BorrowedHandle<'_>,
-        Event: HANDLE,
-        ApcRoutine: Option<IO_APC_ROUTINE>,
-        ApcContext: *mut c_void,
-        IoStatusBlock: &mut IO_STATUS_BLOCK,
-        Buffer: *mut crate::mem::MaybeUninit<u8>,
-        Length: ULONG,
-        ByteOffset: Option<&LARGE_INTEGER>,
-        Key: Option<&ULONG>
-    ) -> NTSTATUS {
-        STATUS_NOT_IMPLEMENTED
-    }
-    pub fn NtWriteFile(
-        FileHandle: BorrowedHandle<'_>,
-        Event: HANDLE,
-        ApcRoutine: Option<IO_APC_ROUTINE>,
-        ApcContext: *mut c_void,
-        IoStatusBlock: &mut IO_STATUS_BLOCK,
-        Buffer: *const u8,
-        Length: ULONG,
-        ByteOffset: Option<&LARGE_INTEGER>,
-        Key: Option<&ULONG>
-    ) -> NTSTATUS {
-        STATUS_NOT_IMPLEMENTED
-    }
-    pub fn RtlNtStatusToDosError(
-        Status: NTSTATUS
-    ) -> ULONG {
-        Status as ULONG
-    }
     pub fn NtCreateKeyedEvent(
         KeyedEventHandle: LPHANDLE,
         DesiredAccess: ACCESS_MASK,
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index a3d9cb3e10c..dceb4bd1b89 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -210,6 +210,8 @@ fn make_win_dist(
         rustc_dlls.push("libgcc_s_seh-1.dll");
     }
 
+    // Libraries necessary to link the windows-gnu toolchains.
+    // System libraries will be preferred if they are available (see #67429).
     let target_libs = [
         //MinGW libs
         "libgcc.a",
@@ -223,6 +225,7 @@ fn make_win_dist(
         "libmoldname.a",
         "libpthread.a",
         //Windows import libs
+        //This should contain only the set of libraries necessary to link the standard library.
         "libadvapi32.a",
         "libbcrypt.a",
         "libcomctl32.a",
@@ -236,6 +239,7 @@ fn make_win_dist(
         "libkernel32.a",
         "libmsimg32.a",
         "libmsvcrt.a",
+        "libntdll.a",
         "libodbc32.a",
         "libole32.a",
         "liboleaut32.a",
diff --git a/src/tools/miri/src/shims/windows/dlsym.rs b/src/tools/miri/src/shims/windows/dlsym.rs
index 60dd299c438..7e2051fc98a 100644
--- a/src/tools/miri/src/shims/windows/dlsym.rs
+++ b/src/tools/miri/src/shims/windows/dlsym.rs
@@ -1,5 +1,4 @@
 use rustc_middle::mir;
-use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 
 use log::trace;
@@ -11,7 +10,6 @@ use crate::*;
 
 #[derive(Debug, Copy, Clone)]
 pub enum Dlsym {
-    NtWriteFile,
     SetThreadDescription,
     WaitOnAddress,
     WakeByAddressSingle,
@@ -23,7 +21,6 @@ impl Dlsym {
     pub fn from_str<'tcx>(name: &str) -> InterpResult<'tcx, Option<Dlsym>> {
         Ok(match name {
             "GetSystemTimePreciseAsFileTime" => None,
-            "NtWriteFile" => Some(Dlsym::NtWriteFile),
             "SetThreadDescription" => Some(Dlsym::SetThreadDescription),
             "WaitOnAddress" => Some(Dlsym::WaitOnAddress),
             "WakeByAddressSingle" => Some(Dlsym::WakeByAddressSingle),
@@ -49,72 +46,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         this.check_abi(abi, Abi::System { unwind: false })?;
 
         match dlsym {
-            Dlsym::NtWriteFile => {
-                if !this.frame_in_std() {
-                    throw_unsup_format!(
-                        "`NtWriteFile` support is crude and just enough for stdout to work"
-                    );
-                }
-
-                let [
-                    handle,
-                    _event,
-                    _apc_routine,
-                    _apc_context,
-                    io_status_block,
-                    buf,
-                    n,
-                    byte_offset,
-                    _key,
-                ] = check_arg_count(args)?;
-                let handle = this.read_target_isize(handle)?;
-                let buf = this.read_pointer(buf)?;
-                let n = this.read_scalar(n)?.to_u32()?;
-                let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer
-                let io_status_block = this.deref_operand(io_status_block)?;
-
-                if byte_offset != 0 {
-                    throw_unsup_format!(
-                        "`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
-                    );
-                }
-
-                let written = if handle == -11 || handle == -12 {
-                    // stdout/stderr
-                    use std::io::{self, Write};
-
-                    let buf_cont =
-                        this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
-                    let res = if this.machine.mute_stdout_stderr {
-                        Ok(buf_cont.len())
-                    } else if handle == -11 {
-                        io::stdout().write(buf_cont)
-                    } else {
-                        io::stderr().write(buf_cont)
-                    };
-                    // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
-                    res.ok().map(|n| u32::try_from(n).unwrap())
-                } else {
-                    throw_unsup_format!(
-                        "on Windows, writing to anything except stdout/stderr is not supported"
-                    )
-                };
-                // We have to put the result into io_status_block.
-                if let Some(n) = written {
-                    let io_status_information =
-                        this.mplace_field_named(&io_status_block, "Information")?;
-                    this.write_scalar(
-                        Scalar::from_target_usize(n.into(), this),
-                        &io_status_information.into(),
-                    )?;
-                }
-                // Return whether this was a success. >= 0 is success.
-                // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
-                this.write_scalar(
-                    Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
-                    dest,
-                )?;
-            }
             Dlsym::SetThreadDescription => {
                 let [handle, name] = check_arg_count(args)?;
 
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index a3d7176a976..665c7ed438f 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -69,6 +69,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.write_scalar(result, dest)?;
             }
 
+            // File related shims
+            "NtWriteFile" => {
+                if !this.frame_in_std() {
+                    throw_unsup_format!(
+                        "`NtWriteFile` support is crude and just enough for stdout to work"
+                    );
+                }
+
+                let [
+                    handle,
+                    _event,
+                    _apc_routine,
+                    _apc_context,
+                    io_status_block,
+                    buf,
+                    n,
+                    byte_offset,
+                    _key,
+                ] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let handle = this.read_target_isize(handle)?;
+                let buf = this.read_pointer(buf)?;
+                let n = this.read_scalar(n)?.to_u32()?;
+                let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer
+                let io_status_block = this.deref_operand(io_status_block)?;
+
+                if byte_offset != 0 {
+                    throw_unsup_format!(
+                        "`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
+                    );
+                }
+
+                let written = if handle == -11 || handle == -12 {
+                    // stdout/stderr
+                    use std::io::{self, Write};
+
+                    let buf_cont =
+                        this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
+                    let res = if this.machine.mute_stdout_stderr {
+                        Ok(buf_cont.len())
+                    } else if handle == -11 {
+                        io::stdout().write(buf_cont)
+                    } else {
+                        io::stderr().write(buf_cont)
+                    };
+                    // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
+                    res.ok().map(|n| u32::try_from(n).unwrap())
+                } else {
+                    throw_unsup_format!(
+                        "on Windows, writing to anything except stdout/stderr is not supported"
+                    )
+                };
+                // We have to put the result into io_status_block.
+                if let Some(n) = written {
+                    let io_status_information =
+                        this.mplace_field_named(&io_status_block, "Information")?;
+                    this.write_scalar(
+                        Scalar::from_target_usize(n.into(), this),
+                        &io_status_information.into(),
+                    )?;
+                }
+                // Return whether this was a success. >= 0 is success.
+                // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
+                this.write_scalar(
+                    Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
+                    dest,
+                )?;
+            }
+
             // Allocation
             "HeapAlloc" => {
                 let [handle, flags, size] =
diff --git a/tests/run-make-fulldeps/tools.mk b/tests/run-make-fulldeps/tools.mk
index 0f5425daa16..ea06b620c4c 100644
--- a/tests/run-make-fulldeps/tools.mk
+++ b/tests/run-make-fulldeps/tools.mk
@@ -112,9 +112,9 @@ endif
 # Extra flags needed to compile a working executable with the standard library
 ifdef IS_WINDOWS
 ifdef IS_MSVC
-	EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib
+	EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib
 else
-	EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt
+	EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll
 	EXTRACXXFLAGS := -lstdc++
 	# So this is a bit hacky: we can't use the DLL version of libstdc++ because
 	# it pulls in the DLL version of libgcc, which means that we end up with 2