From 3c3ab2523bddd67f3f60a329b2b0553d717c90bd Mon Sep 17 00:00:00 2001 From: James Farrell Date: Thu, 2 Oct 2025 17:34:48 +0000 Subject: [PATCH] Do not use dlltool to create DLL Import Libraries for Windows --- compiler/rustc_codegen_ssa/messages.ftl | 11 - .../rustc_codegen_ssa/src/back/archive.rs | 242 ++++-------------- compiler/rustc_codegen_ssa/src/errors.rs | 22 -- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_session/src/options.rs | 2 - compiler/rustc_target/src/spec/mod.rs | 2 +- 6 files changed, 53 insertions(+), 227 deletions(-) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 91c3806df4c34..648fa6c1b6a14 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -28,26 +28,15 @@ codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C 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_dynamic_linking_with_lto = cannot prefer dynamic linking when performing LTO .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO -codegen_ssa_error_calling_dlltool = - error calling dlltool '{$dlltool_path}': {$error} - codegen_ssa_error_creating_import_library = error creating import library for {$lib_name}: {$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_name_value_pair = expected name value pair 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 cfd8ceac3a604..910a74a8804a4 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,6 +1,4 @@ -use std::env; use std::error::Error; -use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; @@ -20,12 +18,9 @@ use rustc_span::Symbol; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; -use crate::common; +use crate::errors::ErrorCreatingImportLibrary; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; -use crate::errors::{ - DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, -}; /// An item to be included in an import library. /// This is a slimmed down version of `COFFShortExport` from `ar-archive-writer`. @@ -82,66 +77,57 @@ pub trait ArchiveBuilderBuilder { items: Vec, 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. - create_mingw_dll_import_lib(sess, lib_name, items, output_path); - } else { - trace!("creating import library"); - trace!(" dll_name {:#?}", lib_name); - trace!(" output_path {}", output_path.display()); - trace!( - " import names: {}", - items - .iter() - .map(|ImportLibraryItem { name, .. }| name.clone()) - .collect::>() - .join(", "), - ); - - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] is implemented, ensure that the import names - // still don't contain any \0 characters. Also need to check that the names don't - // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved - // in definition files. - - let mut file = match fs::File::create_new(&output_path) { - Ok(file) => file, - Err(error) => sess - .dcx() - .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }), - }; - - let exports = - items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::>(); - let machine = match &*sess.target.arch { - "x86_64" => MachineTypes::AMD64, - "x86" => MachineTypes::I386, - "aarch64" => MachineTypes::ARM64, - "arm64ec" => MachineTypes::ARM64EC, - "arm" => MachineTypes::ARMNT, - cpu => panic!("unsupported cpu type {cpu}"), - }; - - if let Err(error) = ar_archive_writer::write_import_library( - &mut file, - lib_name, - &exports, - machine, - !sess.target.is_like_msvc, - // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag. - // Without this flag a duplicate symbol error would be emitted - // when linking a rust staticlib using `/WHOLEARCHIVE`. - // See #129020 - true, - &[], - ) { - sess.dcx() - .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }); - } + trace!("creating import library"); + trace!(" dll_name {:#?}", lib_name); + trace!(" output_path {}", output_path.display()); + trace!( + " import names: {}", + items + .iter() + .map(|ImportLibraryItem { name, .. }| name.clone()) + .collect::>() + .join(", "), + ); + + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + + let mut file = match fs::File::create_new(&output_path) { + Ok(file) => file, + Err(error) => sess + .dcx() + .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }), + }; + + let exports = + items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::>(); + let machine = match &*sess.target.arch { + "x86_64" => MachineTypes::AMD64, + "x86" => MachineTypes::I386, + "aarch64" => MachineTypes::ARM64, + "arm64ec" => MachineTypes::ARM64EC, + "arm" => MachineTypes::ARMNT, + cpu => panic!("unsupported cpu type {cpu}"), + }; + + if let Err(error) = ar_archive_writer::write_import_library( + &mut file, + lib_name, + &exports, + machine, + !sess.target.is_like_msvc, + // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag. + // Without this flag a duplicate symbol error would be emitted + // when linking a rust staticlib using `/WHOLEARCHIVE`. + // See #129020 + true, + &[], + ) { + sess.dcx() + .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }); } } @@ -182,130 +168,6 @@ pub trait ArchiveBuilderBuilder { } } -fn create_mingw_dll_import_lib( - sess: &Session, - lib_name: &str, - items: Vec, - output_path: &Path, -) { - let def_file_path = output_path.with_extension("def"); - - let def_file_content = format!( - "EXPORTS\n{}", - items - .into_iter() - .map(|ImportLibraryItem { name, ordinal, .. }| { - match ordinal { - Some(n) => format!("{name} @{n} NONAME"), - None => name, - } - }) - .collect::>() - .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::>() - .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/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index d5c30c5c7a6b0..eb5d91acf0224 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1144,28 +1144,6 @@ pub struct FailedToGetLayout<'tcx> { pub err: LayoutError<'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(crate) struct ErrorCreatingRemarkDir { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 7e5186db4eafe..45d514128fa54 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -584,7 +584,6 @@ fn test_codegen_options_tracking_hash() { untracked!(ar, String::from("abc")); untracked!(codegen_units, Some(42)); untracked!(default_linker_libraries, true); - untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(extra_filename, String::from("extra-filename")); untracked!(incremental, Some(String::from("abc"))); untracked!(inline_threshold, Some(0xf007ba11)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6dd90546de1b0..2e3dbabea9e86 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2067,8 +2067,6 @@ options! { line-tables-only, limited, or full; default: 0)"), default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], "allow the linker to link its default libraries (default: no)"), - dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "import library generation tool (ignored except when targeting windows-gnu)"), #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] dwarf_version: Option = (None, parse_opt_number, [TRACKED], "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4a82a8bd888e6..528c0817571dd 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -659,7 +659,7 @@ bitflags::bitflags! { const LIBC = 1 << 1; /// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets) const UNWIND = 1 << 2; - /// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`) + /// Linker and its necessary libraries (e.g. on `windows-gnu` and for `rust-lld`) const LINKER = 1 << 3; /// Sanitizer runtime libraries const SANITIZERS = 1 << 4;