Skip to content

Commit 064b8e5

Browse files
committed
Stop using dlltool for generating import libraries on MinGW
Older versions of ld.bfd didn't support short import libraries like the builtin implementation of rustc generates. So we worked around this by using binutils dlltool, which produces the old import library format. We have now bumped the minimum supported ld.bfd version to one which does support them, so we can drop the dlltool usage. This makes cross-compilation a bit easier and removes a bunch of code in rustc.
1 parent 9ae765d commit 064b8e5

23 files changed

Lines changed: 59 additions & 463 deletions

File tree

compiler/rustc_codegen_ssa/src/back/archive.rs

Lines changed: 50 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use std::env;
21
use std::error::Error;
3-
use std::ffi::OsString;
42
use std::fs::{self, File};
53
use std::io::{self, BufWriter, Write};
64
use std::path::{Path, PathBuf};
@@ -22,13 +20,9 @@ use tracing::trace;
2220

2321
use super::metadata::{create_compressed_metadata_file, search_for_section};
2422
use super::rmeta_link;
25-
use crate::common;
2623
// Public for ArchiveBuilderBuilder::extract_bundled_libs
2724
pub use crate::errors::ExtractBundledLibsError;
28-
use crate::errors::{
29-
ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
30-
ErrorWritingDEFFile, UnknownArchiveKind,
31-
};
25+
use crate::errors::{ArchiveBuildFailure, ErrorCreatingImportLibrary, UnknownArchiveKind};
3226

3327
/// An item to be included in an import library.
3428
/// This is a slimmed down version of `COFFShortExport` from `ar-archive-writer`.
@@ -85,66 +79,57 @@ pub trait ArchiveBuilderBuilder {
8579
items: Vec<ImportLibraryItem>,
8680
output_path: &Path,
8781
) {
88-
if common::is_mingw_gnu_toolchain(&sess.target) {
89-
// The binutils linker used on -windows-gnu targets cannot read the import
90-
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
91-
// that loaded but crashed with an AV upon calling one of the imported
92-
// functions. Therefore, use binutils to create the import library instead,
93-
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
94-
create_mingw_dll_import_lib(sess, lib_name, items, output_path);
95-
} else {
96-
trace!("creating import library");
97-
trace!(" dll_name {:#?}", lib_name);
98-
trace!(" output_path {}", output_path.display());
99-
trace!(
100-
" import names: {}",
101-
items
102-
.iter()
103-
.map(|ImportLibraryItem { name, .. }| name.clone())
104-
.collect::<Vec<_>>()
105-
.join(", "),
106-
);
107-
108-
// All import names are Rust identifiers and therefore cannot contain \0 characters.
109-
// FIXME: when support for #[link_name] is implemented, ensure that the import names
110-
// still don't contain any \0 characters. Also need to check that the names don't
111-
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
112-
// in definition files.
113-
114-
let mut file = match fs::File::create_new(&output_path) {
115-
Ok(file) => file,
116-
Err(error) => sess
117-
.dcx()
118-
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
119-
};
82+
trace!("creating import library");
83+
trace!(" dll_name {:#?}", lib_name);
84+
trace!(" output_path {}", output_path.display());
85+
trace!(
86+
" import names: {}",
87+
items
88+
.iter()
89+
.map(|ImportLibraryItem { name, .. }| name.clone())
90+
.collect::<Vec<_>>()
91+
.join(", "),
92+
);
93+
94+
// All import names are Rust identifiers and therefore cannot contain \0 characters.
95+
// FIXME: when support for #[link_name] is implemented, ensure that the import names
96+
// still don't contain any \0 characters. Also need to check that the names don't
97+
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
98+
// in definition files.
99+
100+
let mut file = match fs::File::create_new(&output_path) {
101+
Ok(file) => file,
102+
Err(error) => sess
103+
.dcx()
104+
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
105+
};
120106

121-
let exports =
122-
items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
123-
let machine = match &sess.target.arch {
124-
Arch::X86_64 => MachineTypes::AMD64,
125-
Arch::X86 => MachineTypes::I386,
126-
Arch::AArch64 => MachineTypes::ARM64,
127-
Arch::Arm64EC => MachineTypes::ARM64EC,
128-
Arch::Arm => MachineTypes::ARMNT,
129-
cpu => panic!("unsupported cpu type {cpu}"),
130-
};
107+
let exports =
108+
items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
109+
let machine = match &sess.target.arch {
110+
Arch::X86_64 => MachineTypes::AMD64,
111+
Arch::X86 => MachineTypes::I386,
112+
Arch::AArch64 => MachineTypes::ARM64,
113+
Arch::Arm64EC => MachineTypes::ARM64EC,
114+
Arch::Arm => MachineTypes::ARMNT,
115+
cpu => panic!("unsupported cpu type {cpu}"),
116+
};
131117

132-
if let Err(error) = ar_archive_writer::write_import_library(
133-
&mut file,
134-
lib_name,
135-
&exports,
136-
machine,
137-
!sess.target.is_like_msvc,
138-
// Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
139-
// Without this flag a duplicate symbol error would be emitted
140-
// when linking a rust staticlib using `/WHOLEARCHIVE`.
141-
// See #129020
142-
true,
143-
&[],
144-
) {
145-
sess.dcx()
146-
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
147-
}
118+
if let Err(error) = ar_archive_writer::write_import_library(
119+
&mut file,
120+
lib_name,
121+
&exports,
122+
machine,
123+
!sess.target.is_like_msvc,
124+
// Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
125+
// Without this flag a duplicate symbol error would be emitted
126+
// when linking a rust staticlib using `/WHOLEARCHIVE`.
127+
// See #129020
128+
true,
129+
&[],
130+
) {
131+
sess.dcx()
132+
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
148133
}
149134
}
150135

@@ -185,129 +170,6 @@ pub trait ArchiveBuilderBuilder {
185170
}
186171
}
187172

188-
fn create_mingw_dll_import_lib(
189-
sess: &Session,
190-
lib_name: &str,
191-
items: Vec<ImportLibraryItem>,
192-
output_path: &Path,
193-
) {
194-
let def_file_path = output_path.with_extension("def");
195-
196-
let def_file_content = format!(
197-
"EXPORTS\n{}",
198-
items
199-
.into_iter()
200-
.map(|ImportLibraryItem { name, ordinal, .. }| {
201-
match ordinal {
202-
Some(n) => format!("{name} @{n} NONAME"),
203-
None => name,
204-
}
205-
})
206-
.collect::<Vec<String>>()
207-
.join("\n")
208-
);
209-
210-
match std::fs::write(&def_file_path, def_file_content) {
211-
Ok(_) => {}
212-
Err(e) => {
213-
sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
214-
}
215-
};
216-
217-
// --no-leading-underscore: For the `import_name_type` feature to work, we need to be
218-
// able to control the *exact* spelling of each of the symbols that are being imported:
219-
// hence we don't want `dlltool` adding leading underscores automatically.
220-
let dlltool = find_binutils_dlltool(sess);
221-
// temp_prefix doesn't handle paths with spaces so
222-
// use a relative path and set the current working directory
223-
let cwd = output_path.parent().unwrap_or(output_path);
224-
let temp_prefix = lib_name;
225-
// dlltool target architecture args from:
226-
// https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
227-
let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch {
228-
Arch::X86_64 => ("i386:x86-64", "--64"),
229-
Arch::X86 => ("i386", "--32"),
230-
Arch::AArch64 => ("arm64", "--64"),
231-
Arch::Arm => ("arm", "--32"),
232-
arch => panic!("unsupported arch {arch}"),
233-
};
234-
let mut dlltool_cmd = std::process::Command::new(&dlltool);
235-
dlltool_cmd
236-
.arg("-d")
237-
.arg(def_file_path)
238-
.arg("-D")
239-
.arg(lib_name)
240-
.arg("-l")
241-
.arg(&output_path)
242-
.arg("-m")
243-
.arg(dlltool_target_arch)
244-
.arg("-f")
245-
.arg(dlltool_target_bitness)
246-
.arg("--no-leading-underscore")
247-
.arg("--temp-prefix")
248-
.arg(temp_prefix)
249-
.current_dir(cwd);
250-
251-
match dlltool_cmd.output() {
252-
Err(e) => {
253-
sess.dcx().emit_fatal(ErrorCallingDllTool {
254-
dlltool_path: dlltool.to_string_lossy(),
255-
error: e,
256-
});
257-
}
258-
// dlltool returns '0' on failure, so check for error output instead.
259-
Ok(output) if !output.stderr.is_empty() => {
260-
sess.dcx().emit_fatal(DlltoolFailImportLibrary {
261-
dlltool_path: dlltool.to_string_lossy(),
262-
dlltool_args: dlltool_cmd
263-
.get_args()
264-
.map(|arg| arg.to_string_lossy())
265-
.collect::<Vec<_>>()
266-
.join(" "),
267-
stdout: String::from_utf8_lossy(&output.stdout),
268-
stderr: String::from_utf8_lossy(&output.stderr),
269-
})
270-
}
271-
_ => {}
272-
}
273-
}
274-
275-
fn find_binutils_dlltool(sess: &Session) -> OsString {
276-
assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
277-
if let Some(dlltool_path) = &sess.opts.cg.dlltool {
278-
return dlltool_path.clone().into_os_string();
279-
}
280-
281-
let tool_name: OsString = if sess.host.options.is_like_windows {
282-
// If we're compiling on Windows, always use "dlltool.exe".
283-
"dlltool.exe"
284-
} else {
285-
// On other platforms, use the architecture-specific name.
286-
match sess.target.arch {
287-
Arch::X86_64 => "x86_64-w64-mingw32-dlltool",
288-
Arch::X86 => "i686-w64-mingw32-dlltool",
289-
Arch::AArch64 => "aarch64-w64-mingw32-dlltool",
290-
291-
// For non-standard architectures (e.g., aarch32) fallback to "dlltool".
292-
_ => "dlltool",
293-
}
294-
}
295-
.into();
296-
297-
// NOTE: it's not clear how useful it is to explicitly search PATH.
298-
for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
299-
let full_path = dir.join(&tool_name);
300-
if full_path.is_file() {
301-
return full_path.into_os_string();
302-
}
303-
}
304-
305-
// The user didn't specify the location of the dlltool binary, and we weren't able
306-
// to find the appropriate one on the PATH. Just return the name of the tool
307-
// and let the invocation fail with a hopefully useful error message.
308-
tool_name
309-
}
310-
311173
pub enum AddArchiveKind<'a> {
312174
Rlib(/*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool),
313175
Other,

compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,32 +1103,6 @@ pub struct FailedToGetLayout<'tcx> {
11031103
pub err: LayoutError<'tcx>,
11041104
}
11051105

1106-
#[derive(Diagnostic)]
1107-
#[diag(
1108-
"dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
1109-
{$stdout}
1110-
{$stderr}"
1111-
)]
1112-
pub(crate) struct DlltoolFailImportLibrary<'a> {
1113-
pub dlltool_path: Cow<'a, str>,
1114-
pub dlltool_args: String,
1115-
pub stdout: Cow<'a, str>,
1116-
pub stderr: Cow<'a, str>,
1117-
}
1118-
1119-
#[derive(Diagnostic)]
1120-
#[diag("error writing .DEF file: {$error}")]
1121-
pub(crate) struct ErrorWritingDEFFile {
1122-
pub error: std::io::Error,
1123-
}
1124-
1125-
#[derive(Diagnostic)]
1126-
#[diag("error calling dlltool '{$dlltool_path}': {$error}")]
1127-
pub(crate) struct ErrorCallingDllTool<'a> {
1128-
pub dlltool_path: Cow<'a, str>,
1129-
pub error: std::io::Error,
1130-
}
1131-
11321106
#[derive(Diagnostic)]
11331107
#[diag("failed to create remark directory: {$error}")]
11341108
pub(crate) struct ErrorCreatingRemarkDir {

compiler/rustc_interface/src/tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,6 @@ fn test_codegen_options_tracking_hash() {
580580
// tidy-alphabetical-start
581581
untracked!(codegen_units, Some(42));
582582
untracked!(default_linker_libraries, true);
583-
untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
584583
untracked!(extra_filename, String::from("extra-filename"));
585584
untracked!(incremental, Some(String::from("abc")));
586585
// `link_arg` is omitted because it just forwards to `link_args`.

compiler/rustc_interface/src/util.rs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{env, thread};
88
use rand::{RngCore, rng};
99
use rustc_ast as ast;
1010
use rustc_attr_parsing::ShouldEmit;
11-
use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder};
11+
use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder;
1212
use rustc_codegen_ssa::back::link::link_binary;
1313
use rustc_codegen_ssa::target_features::cfg_target_feature;
1414
use rustc_codegen_ssa::traits::CodegenBackend;
@@ -443,7 +443,7 @@ impl CodegenBackend for DummyCodegenBackend {
443443

444444
link_binary(
445445
sess,
446-
&DummyArchiveBuilderBuilder,
446+
&ArArchiveBuilderBuilder,
447447
compiled_modules,
448448
crate_info,
449449
metadata,
@@ -453,28 +453,6 @@ impl CodegenBackend for DummyCodegenBackend {
453453
}
454454
}
455455

456-
struct DummyArchiveBuilderBuilder;
457-
458-
impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder {
459-
fn new_archive_builder<'a>(
460-
&self,
461-
sess: &'a Session,
462-
) -> Box<dyn rustc_codegen_ssa::back::archive::ArchiveBuilder + 'a> {
463-
ArArchiveBuilderBuilder.new_archive_builder(sess)
464-
}
465-
466-
fn create_dll_import_lib(
467-
&self,
468-
sess: &Session,
469-
_lib_name: &str,
470-
_items: Vec<rustc_codegen_ssa::back::archive::ImportLibraryItem>,
471-
output_path: &Path,
472-
) {
473-
// Build an empty static library to avoid calling an external dlltool on mingw
474-
ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path);
475-
}
476-
}
477-
478456
// This is used for rustdoc, but it uses similar machinery to codegen backend
479457
// loading, so we leave the code here. It is potentially useful for other tools
480458
// that want to invoke the rustc binary while linking to rustc as well.

compiler/rustc_session/src/options.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,8 +2084,9 @@ options! {
20842084
line-tables-only, limited, or full; default: 0)"),
20852085
default_linker_libraries: bool = (false, parse_bool, [UNTRACKED],
20862086
"allow the linker to link its default libraries (default: no)"),
2087-
dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
2088-
"import library generation tool (ignored except when targeting windows-gnu)"),
2087+
dlltool: () = ((), parse_ignore, [UNTRACKED],
2088+
"this option has been removed",
2089+
removed: Warn),
20892090
#[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")]
20902091
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
20912092
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),

compiler/rustc_target/src/spec/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ bitflags::bitflags! {
644644
const LIBC = 1 << 1;
645645
/// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
646646
const UNWIND = 1 << 2;
647-
/// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`)
647+
/// Linker and its necessary libraries (e.g. for `rust-lld`)
648648
const LINKER = 1 << 3;
649649
/// Sanitizer runtime libraries
650650
const SANITIZERS = 1 << 4;

src/bootstrap/src/core/build_steps/dist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ fn make_win_dist(plat_root: &Path, target: TargetSelection, builder: &Builder<'_
224224
} else {
225225
"gcc.exe"
226226
};
227-
let target_tools = [compiler, "ld.exe", "dlltool.exe", "libwinpthread-1.dll"];
227+
let target_tools = [compiler, "ld.exe", "libwinpthread-1.dll"];
228228

229229
// Libraries necessary to link the windows-gnu toolchains.
230230
// System libraries will be preferred if they are available (see #67429).

0 commit comments

Comments
 (0)