about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2024-08-07 15:59:35 +0200
committerGitHub <noreply@github.com>2024-08-07 15:59:35 +0200
commit355eb9c79fed8895c7f96cb401e254696375955e (patch)
tree8349313e035b5de282fc0aeaa06590efa52e785a /compiler
parent9bad7ba324099d124c77c5b06aebf68e11763f7b (diff)
parentf58e737554bebd34ac794d02b6e018bf20857788 (diff)
downloadrust-355eb9c79fed8895c7f96cb401e254696375955e.tar.gz
rust-355eb9c79fed8895c7f96cb401e254696375955e.zip
Rollup merge of #128206 - bjorn3:import_lib_writing_refactor, r=jieyouxu
Make create_dll_import_lib easier to implement

This will make it easier to implement raw-dylib support in cg_clif and cg_gcc. This PR doesn't yet include an create_dll_import_lib implementation for cg_clif as I need to correctly implement dllimport in cg_clif first before raw-dylib can work at all with cg_clif.

Required for https://github.com/rust-lang/rustc_codegen_cranelift/issues/1345
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_cranelift/src/archive.rs9
-rw-r--r--compiler/rustc_codegen_gcc/src/archive.rs10
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl10
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs173
-rw-r--r--compiler/rustc_codegen_llvm/src/callee.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs66
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs23
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl11
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs135
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs98
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs65
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs22
14 files changed, 329 insertions, 305 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs
index 1935005a08c..084654af09d 100644
--- a/compiler/rustc_codegen_cranelift/src/archive.rs
+++ b/compiler/rustc_codegen_cranelift/src/archive.rs
@@ -1,4 +1,4 @@
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 use rustc_codegen_ssa::back::archive::{
     ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
@@ -16,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
         &self,
         sess: &Session,
         _lib_name: &str,
-        _dll_imports: &[rustc_session::cstore::DllImport],
-        _tmpdir: &Path,
-        _is_direct_dependency: bool,
-    ) -> PathBuf {
+        _import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
+        _output_path: &Path,
+    ) {
         sess.dcx().fatal("raw-dylib is not yet supported by rustc_codegen_cranelift");
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs
index 36f5e0f8094..0cee05f1cea 100644
--- a/compiler/rustc_codegen_gcc/src/archive.rs
+++ b/compiler/rustc_codegen_gcc/src/archive.rs
@@ -1,9 +1,8 @@
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 use rustc_codegen_ssa::back::archive::{
     ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
 };
-use rustc_session::cstore::DllImport;
 use rustc_session::Session;
 
 pub(crate) struct ArArchiveBuilderBuilder;
@@ -17,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
         &self,
         _sess: &Session,
         _lib_name: &str,
-        _dll_imports: &[DllImport],
-        _tmpdir: &Path,
-        _is_direct_dependency: bool,
-    ) -> PathBuf {
+        _import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
+        _output_path: &Path,
+    ) {
         unimplemented!("creating dll imports is not yet supported");
     }
 }
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 1c126e79762..267da9325c3 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -1,23 +1,13 @@
 codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
 
-codegen_llvm_dlltool_fail_import_library =
-    Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
-    {$stdout}
-    {$stderr}
-
 codegen_llvm_dynamic_linking_with_lto =
     cannot prefer dynamic linking when performing LTO
     .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
 
-codegen_llvm_error_calling_dlltool =
-    Error calling dlltool '{$dlltool_path}': {$error}
 
 codegen_llvm_error_creating_import_library =
     Error creating import library for {$lib_name}: {$error}
 
-codegen_llvm_error_writing_def_file =
-    Error writing .DEF file: {$error}
-
 codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
 
 codegen_llvm_from_llvm_diag = {$message}
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 2f5a864d70e..a2ab19ac800 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -1,21 +1,19 @@
 //! A helper class for dealing with static archives
 
-use std::ffi::{c_char, c_void, CStr, CString, OsString};
+use std::ffi::{c_char, c_void, CStr, CString};
 use std::path::{Path, PathBuf};
-use std::{env, io, mem, ptr, str};
+use std::{io, mem, ptr, str};
 
 use rustc_codegen_ssa::back::archive::{
-    try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder,
-    ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER,
+    create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder,
+    ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind,
+    DEFAULT_OBJECT_READER,
 };
-use rustc_session::cstore::DllImport;
+use rustc_codegen_ssa::common;
 use rustc_session::Session;
 use tracing::trace;
 
-use crate::common;
-use crate::errors::{
-    DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile,
-};
+use crate::errors::ErrorCreatingImportLibrary;
 use crate::llvm::archive_ro::{ArchiveRO, Child};
 use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
 
@@ -121,116 +119,21 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
         &self,
         sess: &Session,
         lib_name: &str,
-        dll_imports: &[DllImport],
-        tmpdir: &Path,
-        is_direct_dependency: bool,
-    ) -> PathBuf {
-        let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
-        let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib"));
-
-        let target = &sess.target;
-        let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target);
-
-        let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
-            .iter()
-            .map(|import: &DllImport| {
-                if sess.target.arch == "x86" {
-                    (
-                        common::i686_decorated_name(import, mingw_gnu_toolchain, false),
-                        import.ordinal(),
-                    )
-                } else {
-                    (import.name.to_string(), import.ordinal())
-                }
-            })
-            .collect();
-
-        if mingw_gnu_toolchain {
+        import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
+        output_path: &Path,
+    ) {
+        if common::is_mingw_gnu_toolchain(&sess.target) {
             // The binutils linker used on -windows-gnu targets cannot read the import
             // libraries generated by LLVM: in our attempts, the linker produced an .EXE
             // that loaded but crashed with an AV upon calling one of the imported
             // functions. Therefore, use binutils to create the import library instead,
             // by writing a .DEF file to the temp dir and calling binutils's dlltool.
-            let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def"));
-
-            let def_file_content = format!(
-                "EXPORTS\n{}",
-                import_name_and_ordinal_vector
-                    .into_iter()
-                    .map(|(name, ordinal)| {
-                        match ordinal {
-                            Some(n) => format!("{name} @{n} NONAME"),
-                            None => name,
-                        }
-                    })
-                    .collect::<Vec<String>>()
-                    .join("\n")
+            create_mingw_dll_import_lib(
+                sess,
+                lib_name,
+                import_name_and_ordinal_vector,
+                output_path,
             );
-
-            match std::fs::write(&def_file_path, def_file_content) {
-                Ok(_) => {}
-                Err(e) => {
-                    sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
-                }
-            };
-
-            // --no-leading-underscore: For the `import_name_type` feature to work, we need to be
-            // able to control the *exact* spelling of each of the symbols that are being imported:
-            // hence we don't want `dlltool` adding leading underscores automatically.
-            let dlltool = find_binutils_dlltool(sess);
-            let temp_prefix = {
-                let mut path = PathBuf::from(&output_path);
-                path.pop();
-                path.push(lib_name);
-                path
-            };
-            // dlltool target architecture args from:
-            // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
-            let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() {
-                "x86_64" => ("i386:x86-64", "--64"),
-                "x86" => ("i386", "--32"),
-                "aarch64" => ("arm64", "--64"),
-                "arm" => ("arm", "--32"),
-                _ => panic!("unsupported arch {}", sess.target.arch),
-            };
-            let mut dlltool_cmd = std::process::Command::new(&dlltool);
-            dlltool_cmd
-                .arg("-d")
-                .arg(def_file_path)
-                .arg("-D")
-                .arg(lib_name)
-                .arg("-l")
-                .arg(&output_path)
-                .arg("-m")
-                .arg(dlltool_target_arch)
-                .arg("-f")
-                .arg(dlltool_target_bitness)
-                .arg("--no-leading-underscore")
-                .arg("--temp-prefix")
-                .arg(temp_prefix);
-
-            match dlltool_cmd.output() {
-                Err(e) => {
-                    sess.dcx().emit_fatal(ErrorCallingDllTool {
-                        dlltool_path: dlltool.to_string_lossy(),
-                        error: e,
-                    });
-                }
-                // dlltool returns '0' on failure, so check for error output instead.
-                Ok(output) if !output.stderr.is_empty() => {
-                    sess.dcx().emit_fatal(DlltoolFailImportLibrary {
-                        dlltool_path: dlltool.to_string_lossy(),
-                        dlltool_args: dlltool_cmd
-                            .get_args()
-                            .map(|arg| arg.to_string_lossy())
-                            .collect::<Vec<_>>()
-                            .join(" "),
-                        stdout: String::from_utf8_lossy(&output.stdout),
-                        stderr: String::from_utf8_lossy(&output.stderr),
-                    })
-                }
-                _ => {}
-            }
         } else {
             // we've checked for \0 characters in the library name already
             let dll_name_z = CString::new(lib_name).unwrap();
@@ -242,9 +145,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
             trace!("  output_path {}", output_path.display());
             trace!(
                 "  import names: {}",
-                dll_imports
+                import_name_and_ordinal_vector
                     .iter()
-                    .map(|import| import.name.to_string())
+                    .map(|(name, _ordinal)| name.clone())
                     .collect::<Vec<_>>()
                     .join(", "),
             );
@@ -281,9 +184,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
                     error: llvm::last_error().unwrap_or("unknown LLVM error".to_string()),
                 });
             }
-        };
-
-        output_path
+        }
     }
 }
 
@@ -457,39 +358,3 @@ impl<'a> LlvmArchiveBuilder<'a> {
 fn string_to_io_error(s: String) -> io::Error {
     io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
 }
-
-fn find_binutils_dlltool(sess: &Session) -> OsString {
-    assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
-    if let Some(dlltool_path) = &sess.opts.cg.dlltool {
-        return dlltool_path.clone().into_os_string();
-    }
-
-    let tool_name: OsString = if sess.host.options.is_like_windows {
-        // If we're compiling on Windows, always use "dlltool.exe".
-        "dlltool.exe"
-    } else {
-        // On other platforms, use the architecture-specific name.
-        match sess.target.arch.as_ref() {
-            "x86_64" => "x86_64-w64-mingw32-dlltool",
-            "x86" => "i686-w64-mingw32-dlltool",
-            "aarch64" => "aarch64-w64-mingw32-dlltool",
-
-            // For non-standard architectures (e.g., aarch32) fallback to "dlltool".
-            _ => "dlltool",
-        }
-    }
-    .into();
-
-    // NOTE: it's not clear how useful it is to explicitly search PATH.
-    for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
-        let full_path = dir.join(&tool_name);
-        if full_path.is_file() {
-            return full_path.into_os_string();
-        }
-    }
-
-    // The user didn't specify the location of the dlltool binary, and we weren't able
-    // to find the appropriate one on the PATH. Just return the name of the tool
-    // and let the invocation fail with a hopefully useful error message.
-    tool_name
-}
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index c913bdebaaa..9030b3d848f 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -4,13 +4,14 @@
 //! and methods are represented as just a fn ptr and not a full
 //! closure.
 
+use rustc_codegen_ssa::common;
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
 use rustc_middle::ty::{self, Instance, TypeVisitableExt};
 use tracing::debug;
 
 use crate::context::CodegenCx;
 use crate::value::Value;
-use crate::{attributes, common, llvm};
+use crate::{attributes, llvm};
 
 /// Codegens a reference to a fn/method item, monomorphizing and
 /// inlining as it goes.
@@ -46,7 +47,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
     } else {
         let instance_def_id = instance.def_id();
         let llfn = if tcx.sess.target.arch == "x86"
-            && let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym)
+            && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym)
         {
             // Fix for https://github.com/rust-lang/rust/issues/104453
             // On x86 Windows, LLVM uses 'L' as the prefix for any private
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 197bbb9ddbf..65f974c5689 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -1,7 +1,5 @@
 //! Code that is useful in various codegen modules.
 
-use std::fmt::Write;
-
 use libc::{c_char, c_uint};
 use rustc_ast::Mutability;
 use rustc_codegen_ssa::traits::*;
@@ -10,9 +8,8 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
 use rustc_middle::ty::TyCtxt;
-use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
+use rustc_session::cstore::DllImport;
 use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
-use rustc_target::spec::Target;
 use tracing::debug;
 
 use crate::consts::const_alloc_to_llvm;
@@ -379,64 +376,3 @@ pub(crate) fn get_dllimport<'tcx>(
     tcx.native_library(id)
         .and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name))
 }
-
-pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool {
-    target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
-}
-
-pub(crate) fn i686_decorated_name(
-    dll_import: &DllImport,
-    mingw: bool,
-    disable_name_mangling: bool,
-) -> String {
-    let name = dll_import.name.as_str();
-
-    let (add_prefix, add_suffix) = match dll_import.import_name_type {
-        Some(PeImportNameType::NoPrefix) => (false, true),
-        Some(PeImportNameType::Undecorated) => (false, false),
-        _ => (true, true),
-    };
-
-    // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__).
-    let mut decorated_name = String::with_capacity(name.len() + 6);
-
-    if disable_name_mangling {
-        // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled.
-        decorated_name.push('\x01');
-    }
-
-    let prefix = if add_prefix && dll_import.is_fn {
-        match dll_import.calling_convention {
-            DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
-            DllCallingConvention::Stdcall(_) => (!mingw
-                || dll_import.import_name_type == Some(PeImportNameType::Decorated))
-            .then_some('_'),
-            DllCallingConvention::Fastcall(_) => Some('@'),
-        }
-    } else if !dll_import.is_fn && !mingw {
-        // For static variables, prefix with '_' on MSVC.
-        Some('_')
-    } else {
-        None
-    };
-    if let Some(prefix) = prefix {
-        decorated_name.push(prefix);
-    }
-
-    decorated_name.push_str(name);
-
-    if add_suffix && dll_import.is_fn {
-        match dll_import.calling_convention {
-            DllCallingConvention::C => {}
-            DllCallingConvention::Stdcall(arg_list_size)
-            | DllCallingConvention::Fastcall(arg_list_size) => {
-                write!(&mut decorated_name, "@{arg_list_size}").unwrap();
-            }
-            DllCallingConvention::Vectorcall(arg_list_size) => {
-                write!(&mut decorated_name, "@@{arg_list_size}").unwrap();
-            }
-        }
-    }
-
-    decorated_name
-}
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index c3ea4a18a71..75b298f14ca 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -1,5 +1,6 @@
 use std::ops::Range;
 
+use rustc_codegen_ssa::common;
 use rustc_codegen_ssa::traits::*;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -18,7 +19,7 @@ use rustc_target::abi::{
 };
 use tracing::{debug, instrument, trace};
 
-use crate::common::{self, CodegenCx};
+use crate::common::CodegenCx;
 use crate::errors::{
     InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined,
 };
@@ -195,7 +196,7 @@ fn check_and_apply_linkage<'ll, 'tcx>(
             g2
         }
     } else if cx.tcx.sess.target.arch == "x86"
-        && let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym)
+        && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
     {
         cx.declare_global(
             &common::i686_decorated_name(
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index a3957bc52a5..7e53d32ce8c 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -1,4 +1,3 @@
-use std::borrow::Cow;
 use std::ffi::CString;
 use std::path::Path;
 
@@ -72,28 +71,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge {
 pub(crate) struct SanitizerMemtagRequiresMte;
 
 #[derive(Diagnostic)]
-#[diag(codegen_llvm_error_writing_def_file)]
-pub(crate) struct ErrorWritingDEFFile {
-    pub error: std::io::Error,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_error_calling_dlltool)]
-pub(crate) struct ErrorCallingDllTool<'a> {
-    pub dlltool_path: Cow<'a, str>,
-    pub error: std::io::Error,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_dlltool_fail_import_library)]
-pub(crate) struct DlltoolFailImportLibrary<'a> {
-    pub dlltool_path: Cow<'a, str>,
-    pub dlltool_args: String,
-    pub stdout: Cow<'a, str>,
-    pub stderr: Cow<'a, str>,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_llvm_dynamic_linking_with_lto)]
 #[note]
 pub(crate) struct DynamicLinkingWithLTO;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 2767ad5ec9c..6e1c323cbd0 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-ar_archive_writer = "0.3.0"
+ar_archive_writer = "0.3.3"
 arrayvec = { version = "0.7", default-features = false }
 bitflags = "2.4.1"
 cc = "1.0.90"
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 57d789aef80..80f25d42a08 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -24,8 +24,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e
 
 codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
 
+codegen_ssa_dlltool_fail_import_library =
+    Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
+    {$stdout}
+    {$stderr}
+
+codegen_ssa_error_calling_dlltool =
+    Error calling dlltool '{$dlltool_path}': {$error}
+
 codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
 
+codegen_ssa_error_writing_def_file =
+    Error writing .DEF file: {$error}
+
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 4f93b7b23c6..ce55d99f506 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -1,4 +1,6 @@
+use std::env;
 use std::error::Error;
+use std::ffi::OsString;
 use std::fs::{self, File};
 use std::io::{self, Write};
 use std::path::{Path, PathBuf};
@@ -9,7 +11,6 @@ use object::read::archive::ArchiveFile;
 use object::read::macho::FatArch;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::memmap::Mmap;
-use rustc_session::cstore::DllImport;
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 use tempfile::Builder as TempFileBuilder;
@@ -17,6 +18,7 @@ use tempfile::Builder as TempFileBuilder;
 use super::metadata::search_for_section;
 // Re-exporting for rustc_codegen_llvm::back::archive
 pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
+use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile};
 
 pub trait ArchiveBuilderBuilder {
     fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
@@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder {
         &self,
         sess: &Session,
         lib_name: &str,
-        dll_imports: &[DllImport],
-        tmpdir: &Path,
-        is_direct_dependency: bool,
-    ) -> PathBuf;
+        import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
+        output_path: &Path,
+    );
 
     fn extract_bundled_libs<'a>(
         &'a self,
@@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder {
     }
 }
 
+pub fn create_mingw_dll_import_lib(
+    sess: &Session,
+    lib_name: &str,
+    import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
+    output_path: &Path,
+) {
+    let def_file_path = output_path.with_extension("def");
+
+    let def_file_content = format!(
+        "EXPORTS\n{}",
+        import_name_and_ordinal_vector
+            .into_iter()
+            .map(|(name, ordinal)| {
+                match ordinal {
+                    Some(n) => format!("{name} @{n} NONAME"),
+                    None => name,
+                }
+            })
+            .collect::<Vec<String>>()
+            .join("\n")
+    );
+
+    match std::fs::write(&def_file_path, def_file_content) {
+        Ok(_) => {}
+        Err(e) => {
+            sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
+        }
+    };
+
+    // --no-leading-underscore: For the `import_name_type` feature to work, we need to be
+    // able to control the *exact* spelling of each of the symbols that are being imported:
+    // hence we don't want `dlltool` adding leading underscores automatically.
+    let dlltool = find_binutils_dlltool(sess);
+    let temp_prefix = {
+        let mut path = PathBuf::from(&output_path);
+        path.pop();
+        path.push(lib_name);
+        path
+    };
+    // dlltool target architecture args from:
+    // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
+    let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() {
+        "x86_64" => ("i386:x86-64", "--64"),
+        "x86" => ("i386", "--32"),
+        "aarch64" => ("arm64", "--64"),
+        "arm" => ("arm", "--32"),
+        _ => panic!("unsupported arch {}", sess.target.arch),
+    };
+    let mut dlltool_cmd = std::process::Command::new(&dlltool);
+    dlltool_cmd
+        .arg("-d")
+        .arg(def_file_path)
+        .arg("-D")
+        .arg(lib_name)
+        .arg("-l")
+        .arg(&output_path)
+        .arg("-m")
+        .arg(dlltool_target_arch)
+        .arg("-f")
+        .arg(dlltool_target_bitness)
+        .arg("--no-leading-underscore")
+        .arg("--temp-prefix")
+        .arg(temp_prefix);
+
+    match dlltool_cmd.output() {
+        Err(e) => {
+            sess.dcx().emit_fatal(ErrorCallingDllTool {
+                dlltool_path: dlltool.to_string_lossy(),
+                error: e,
+            });
+        }
+        // dlltool returns '0' on failure, so check for error output instead.
+        Ok(output) if !output.stderr.is_empty() => {
+            sess.dcx().emit_fatal(DlltoolFailImportLibrary {
+                dlltool_path: dlltool.to_string_lossy(),
+                dlltool_args: dlltool_cmd
+                    .get_args()
+                    .map(|arg| arg.to_string_lossy())
+                    .collect::<Vec<_>>()
+                    .join(" "),
+                stdout: String::from_utf8_lossy(&output.stdout),
+                stderr: String::from_utf8_lossy(&output.stderr),
+            })
+        }
+        _ => {}
+    }
+}
+
+fn find_binutils_dlltool(sess: &Session) -> OsString {
+    assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
+    if let Some(dlltool_path) = &sess.opts.cg.dlltool {
+        return dlltool_path.clone().into_os_string();
+    }
+
+    let tool_name: OsString = if sess.host.options.is_like_windows {
+        // If we're compiling on Windows, always use "dlltool.exe".
+        "dlltool.exe"
+    } else {
+        // On other platforms, use the architecture-specific name.
+        match sess.target.arch.as_ref() {
+            "x86_64" => "x86_64-w64-mingw32-dlltool",
+            "x86" => "i686-w64-mingw32-dlltool",
+            "aarch64" => "aarch64-w64-mingw32-dlltool",
+
+            // For non-standard architectures (e.g., aarch32) fallback to "dlltool".
+            _ => "dlltool",
+        }
+    }
+    .into();
+
+    // NOTE: it's not clear how useful it is to explicitly search PATH.
+    for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
+        let full_path = dir.join(&tool_name);
+        if full_path.is_file() {
+            return full_path.into_os_string();
+        }
+    }
+
+    // The user didn't specify the location of the dlltool binary, and we weren't able
+    // to find the appropriate one on the PATH. Just return the name of the tool
+    // and let the invocation fail with a hopefully useful error message.
+    tool_name
+}
+
 pub trait ArchiveBuilder {
     fn add_file(&mut self, path: &Path);
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 653895380be..45a92fd03d5 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -51,7 +51,8 @@ use super::linker::{self, Linker};
 use super::metadata::{create_wrapper_file, MetadataPosition};
 use super::rpath::{self, RPathConfig};
 use crate::{
-    errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
+    common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo,
+    NativeLib,
 };
 
 pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@@ -390,17 +391,13 @@ fn link_rlib<'a>(
         }
     }
 
-    for (raw_dylib_name, raw_dylib_imports) in
-        collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())?
-    {
-        let output_path = archive_builder_builder.create_dll_import_lib(
-            sess,
-            &raw_dylib_name,
-            &raw_dylib_imports,
-            tmpdir.as_ref(),
-            true,
-        );
-
+    for output_path in create_dll_import_libs(
+        sess,
+        archive_builder_builder,
+        codegen_results.crate_info.used_libraries.iter(),
+        tmpdir.as_ref(),
+        true,
+    )? {
         ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
             sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
         });
@@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>(
         .collect())
 }
 
+fn create_dll_import_libs<'a>(
+    sess: &Session,
+    archive_builder_builder: &dyn ArchiveBuilderBuilder,
+    used_libraries: impl IntoIterator<Item = &'a NativeLib>,
+    tmpdir: &Path,
+    is_direct_dependency: bool,
+) -> Result<Vec<PathBuf>, ErrorGuaranteed> {
+    Ok(collate_raw_dylibs(sess, used_libraries)?
+        .into_iter()
+        .map(|(raw_dylib_name, raw_dylib_imports)| {
+            let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
+            let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
+
+            let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
+
+            let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = raw_dylib_imports
+                .iter()
+                .map(|import: &DllImport| {
+                    if sess.target.arch == "x86" {
+                        (
+                            common::i686_decorated_name(import, mingw_gnu_toolchain, false),
+                            import.ordinal(),
+                        )
+                    } else {
+                        (import.name.to_string(), import.ordinal())
+                    }
+                })
+                .collect();
+
+            archive_builder_builder.create_dll_import_lib(
+                sess,
+                &raw_dylib_name,
+                import_name_and_ordinal_vector,
+                &output_path,
+            );
+
+            output_path
+        })
+        .collect())
+}
+
 /// Create a static archive.
 ///
 /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
@@ -2305,16 +2343,14 @@ fn linker_with_args(
     );
 
     // Link with the import library generated for any raw-dylib functions.
-    for (raw_dylib_name, raw_dylib_imports) in
-        collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())?
-    {
-        cmd.add_object(&archive_builder_builder.create_dll_import_lib(
-            sess,
-            &raw_dylib_name,
-            &raw_dylib_imports,
-            tmpdir,
-            true,
-        ));
+    for output_path in create_dll_import_libs(
+        sess,
+        archive_builder_builder,
+        codegen_results.crate_info.used_libraries.iter(),
+        tmpdir,
+        true,
+    )? {
+        cmd.add_object(&output_path);
     }
     // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
     // they are used within inlined functions or instantiated generic functions. We do this *after*
@@ -2339,16 +2375,14 @@ fn linker_with_args(
         .flatten()
         .collect::<Vec<_>>();
     native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
-    for (raw_dylib_name, raw_dylib_imports) in
-        collate_raw_dylibs(sess, native_libraries_from_nonstatics)?
-    {
-        cmd.add_object(&archive_builder_builder.create_dll_import_lib(
-            sess,
-            &raw_dylib_name,
-            &raw_dylib_imports,
-            tmpdir,
-            false,
-        ));
+    for output_path in create_dll_import_libs(
+        sess,
+        archive_builder_builder,
+        native_libraries_from_nonstatics,
+        tmpdir,
+        false,
+    )? {
+        cmd.add_object(&output_path);
     }
 
     // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index a972c0cd99d..bfb1d217eae 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -4,7 +4,9 @@ use rustc_hir::LangItem;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{self, Instance, TyCtxt};
 use rustc_middle::{bug, mir, span_bug};
+use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
 use rustc_span::Span;
+use rustc_target::spec::Target;
 
 use crate::traits::*;
 
@@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>(
         _ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty),
     }
 }
+
+pub fn is_mingw_gnu_toolchain(target: &Target) -> bool {
+    target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
+}
+
+pub fn i686_decorated_name(
+    dll_import: &DllImport,
+    mingw: bool,
+    disable_name_mangling: bool,
+) -> String {
+    let name = dll_import.name.as_str();
+
+    let (add_prefix, add_suffix) = match dll_import.import_name_type {
+        Some(PeImportNameType::NoPrefix) => (false, true),
+        Some(PeImportNameType::Undecorated) => (false, false),
+        _ => (true, true),
+    };
+
+    // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__).
+    let mut decorated_name = String::with_capacity(name.len() + 6);
+
+    if disable_name_mangling {
+        // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled.
+        decorated_name.push('\x01');
+    }
+
+    let prefix = if add_prefix && dll_import.is_fn {
+        match dll_import.calling_convention {
+            DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
+            DllCallingConvention::Stdcall(_) => (!mingw
+                || dll_import.import_name_type == Some(PeImportNameType::Decorated))
+            .then_some('_'),
+            DllCallingConvention::Fastcall(_) => Some('@'),
+        }
+    } else if !dll_import.is_fn && !mingw {
+        // For static variables, prefix with '_' on MSVC.
+        Some('_')
+    } else {
+        None
+    };
+    if let Some(prefix) = prefix {
+        decorated_name.push(prefix);
+    }
+
+    decorated_name.push_str(name);
+
+    if add_suffix && dll_import.is_fn {
+        use std::fmt::Write;
+
+        match dll_import.calling_convention {
+            DllCallingConvention::C => {}
+            DllCallingConvention::Stdcall(arg_list_size)
+            | DllCallingConvention::Fastcall(arg_list_size) => {
+                write!(&mut decorated_name, "@{arg_list_size}").unwrap();
+            }
+            DllCallingConvention::Vectorcall(arg_list_size) => {
+                write!(&mut decorated_name, "@@{arg_list_size}").unwrap();
+            }
+        }
+    }
+
+    decorated_name
+}
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 2d1eae630cf..94bf0ab34e2 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1027,6 +1027,28 @@ pub struct FailedToGetLayout<'tcx> {
 }
 
 #[derive(Diagnostic)]
+#[diag(codegen_ssa_dlltool_fail_import_library)]
+pub(crate) struct DlltoolFailImportLibrary<'a> {
+    pub dlltool_path: Cow<'a, str>,
+    pub dlltool_args: String,
+    pub stdout: Cow<'a, str>,
+    pub stderr: Cow<'a, str>,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_error_writing_def_file)]
+pub(crate) struct ErrorWritingDEFFile {
+    pub error: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_error_calling_dlltool)]
+pub(crate) struct ErrorCallingDllTool<'a> {
+    pub dlltool_path: Cow<'a, str>,
+    pub error: std::io::Error,
+}
+
+#[derive(Diagnostic)]
 #[diag(codegen_ssa_error_creating_remark_dir)]
 pub struct ErrorCreatingRemarkDir {
     pub error: std::io::Error,