diff --git a/src/tools/run-make-support/src/artifact_names.rs b/src/tools/run-make-support/src/artifact_names.rs
index bc6ec7566e5..0d7b5cb9838 100644
--- a/src/tools/run-make-support/src/artifact_names.rs
+++ b/src/tools/run-make-support/src/artifact_names.rs
@@ -38,6 +38,17 @@ pub fn dynamic_lib_name(name: &str) -> String {
     format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
 }
 
+/// Construct the name of the import library for the dynamic library, exclusive to MSVC and
+/// accepted by link.exe.
+#[track_caller]
+#[must_use]
+pub fn msvc_import_dynamic_lib_name(name: &str) -> String {
+    assert!(is_msvc(), "this function is exclusive to MSVC");
+    assert!(!name.contains(char::is_whitespace), "import library name cannot contain whitespace");
+
+    format!("{name}.dll.lib")
+}
+
 /// Construct the dynamic library extension based on the target.
 #[must_use]
 pub fn dynamic_lib_extension() -> &'static str {
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 63c4c4d8863..fc7e5ceae40 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -72,13 +72,14 @@ pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, targe
 
 /// Helpers for building names of output artifacts that are potentially target-specific.
 pub use artifact_names::{
-    bin_name, dynamic_lib_extension, dynamic_lib_name, rust_lib_name, static_lib_name,
+    bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name,
+    static_lib_name,
 };
 
 /// Path-related helpers.
 pub use path_helpers::{
-    cwd, filename_not_in_denylist, has_extension, has_prefix, has_suffix, not_contains, path,
-    shallow_find_files, source_root,
+    cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix,
+    not_contains, path, shallow_find_files, source_root,
 };
 
 /// Helpers for scoped test execution where certain properties are attempted to be maintained.
diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs
index f37ea8dfef8..b788bc6ef30 100644
--- a/src/tools/run-make-support/src/path_helpers.rs
+++ b/src/tools/run-make-support/src/path_helpers.rs
@@ -3,6 +3,7 @@
 use std::path::{Path, PathBuf};
 
 use crate::env::env_var;
+use crate::rfs;
 
 /// Return the current working directory.
 ///
@@ -40,7 +41,7 @@ pub fn shallow_find_files<P: AsRef<Path>, F: Fn(&PathBuf) -> bool>(
     filter: F,
 ) -> Vec<PathBuf> {
     let mut matching_files = Vec::new();
-    for entry in std::fs::read_dir(path).unwrap() {
+    for entry in rfs::read_dir(path) {
         let entry = entry.expect("failed to read directory entry.");
         let path = entry.path();
 
@@ -78,3 +79,8 @@ pub fn has_extension<P: AsRef<Path>>(path: P, extension: &str) -> bool {
 pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
     path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix))
 }
+
+/// Returns true if the filename at `path` contains `needle`.
+pub fn filename_contains<P: AsRef<Path>>(path: P, needle: &str) -> bool {
+    path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(needle))
+}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 38880e5e95f..a2cfdea712e 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,6 +1,5 @@
 run-make/branch-protection-check-IBT/Makefile
 run-make/cat-and-grep-sanity-check/Makefile
-run-make/cdylib-dylib-linkage/Makefile
 run-make/cross-lang-lto-upstream-rlibs/Makefile
 run-make/dep-info-doesnt-run-much/Makefile
 run-make/dep-info-spaces/Makefile
diff --git a/tests/run-make/cdylib-dylib-linkage/Makefile b/tests/run-make/cdylib-dylib-linkage/Makefile
deleted file mode 100644
index db8393d3c05..00000000000
--- a/tests/run-make/cdylib-dylib-linkage/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-# This test checks that cdylibs can link against dylibs as dependencies, after this restriction was disabled.
-# See https://github.com/rust-lang/rust/commit/72aaa3a414d17aa0c4f19feafa5bab5f84b60e63
-
-# ignore-cross-compile
-include ../tools.mk
-
-TARGET_SYSROOT := $(shell $(RUSTC) --print sysroot)/lib/rustlib/$(TARGET)/lib
-
-ifdef IS_MSVC
-LIBSTD := $(wildcard $(TARGET_SYSROOT)/libstd-*.dll.lib)
-else
-LIBSTD := $(wildcard $(TARGET_SYSROOT)/$(call DYLIB_GLOB,std))
-STD := $(basename $(patsubst lib%,%, $(notdir $(LIBSTD))))
-endif
-
-all: $(call RUN_BINFILE,foo)
-	$(call RUN,foo)
-
-ifdef IS_MSVC
-CLIBS := $(TMPDIR)/foo.dll.lib $(TMPDIR)/bar.dll.lib $(LIBSTD)
-$(call RUN_BINFILE,foo): $(call DYLIB,foo)
-	$(CC) $(CFLAGS) foo.c $(CLIBS) $(call OUT_EXE,foo)
-else
-CLIBS := -lfoo -lbar -l$(STD) -L $(TMPDIR) -L $(TARGET_SYSROOT)
-$(call RUN_BINFILE,foo): $(call DYLIB,foo)
-	$(CC) $(CFLAGS) foo.c $(CLIBS) -o $(call RUN_BINFILE,foo)
-endif
-
-$(call DYLIB,foo):
-	$(RUSTC) -C prefer-dynamic bar.rs
-	$(RUSTC) foo.rs
diff --git a/tests/run-make/cdylib-dylib-linkage/rmake.rs b/tests/run-make/cdylib-dylib-linkage/rmake.rs
new file mode 100644
index 00000000000..a8fd8e2d168
--- /dev/null
+++ b/tests/run-make/cdylib-dylib-linkage/rmake.rs
@@ -0,0 +1,41 @@
+// Previously, rustc mandated that cdylibs could only link against rlibs as dependencies,
+// making linkage between cdylibs and dylibs impossible. After this was changed in #68448,
+// this test attempts to link both `foo` (a cdylib) and `bar` (a dylib) and checks that
+// both compilation and execution are successful.
+// See https://github.com/rust-lang/rust/pull/68448
+
+//@ ignore-cross-compile
+// Reason: the compiled binary is executed
+
+use run_make_support::{
+    bin_name, cc, dynamic_lib_extension, dynamic_lib_name, filename_contains, has_extension,
+    has_prefix, has_suffix, is_msvc, msvc_import_dynamic_lib_name, path, run, rustc,
+    shallow_find_files, target,
+};
+
+fn main() {
+    rustc().arg("-Cprefer-dynamic").input("bar.rs").run();
+    rustc().input("foo.rs").run();
+    let sysroot = rustc().print("sysroot").run().stdout_utf8();
+    let sysroot = sysroot.trim();
+    let target_sysroot = path(sysroot).join("lib/rustlib").join(target()).join("lib");
+    if is_msvc() {
+        let mut libs = shallow_find_files(&target_sysroot, |path| {
+            has_prefix(path, "libstd-") && has_suffix(path, ".dll.lib")
+        });
+        libs.push(path(msvc_import_dynamic_lib_name("foo")));
+        libs.push(path(msvc_import_dynamic_lib_name("bar")));
+        cc().input("foo.c").args(&libs).out_exe("foo").run();
+    } else {
+        let stdlibs = shallow_find_files(&target_sysroot, |path| {
+            has_extension(path, dynamic_lib_extension()) && filename_contains(path, "std")
+        });
+        cc().input("foo.c")
+            .args(&[dynamic_lib_name("foo"), dynamic_lib_name("bar")])
+            .arg(stdlibs.get(0).unwrap())
+            .library_search_path(&target_sysroot)
+            .output(bin_name("foo"))
+            .run();
+    }
+    run("foo");
+}