Skip to content

Commit 4fa01bb

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 32cfe53 commit 4fa01bb

25 files changed

Lines changed: 59 additions & 473 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};
@@ -23,13 +21,9 @@ use tracing::trace;
2321
use super::metadata::{create_compressed_metadata_file, search_for_section};
2422
use super::rmeta_link;
2523
use super::symbol_edit::apply_hide;
26-
use crate::common;
2724
// Public for ArchiveBuilderBuilder::extract_bundled_libs
2825
pub use crate::errors::ExtractBundledLibsError;
29-
use crate::errors::{
30-
ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
31-
ErrorWritingDEFFile, UnknownArchiveKind,
32-
};
26+
use crate::errors::{ArchiveBuildFailure, ErrorCreatingImportLibrary, UnknownArchiveKind};
3327

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

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

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

@@ -186,129 +171,6 @@ pub trait ArchiveBuilderBuilder {
186171
}
187172
}
188173

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

compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,32 +1111,6 @@ pub struct FailedToGetLayout<'tcx> {
11111111
pub err: LayoutError<'tcx>,
11121112
}
11131113

1114-
#[derive(Diagnostic)]
1115-
#[diag(
1116-
"dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
1117-
{$stdout}
1118-
{$stderr}"
1119-
)]
1120-
pub(crate) struct DlltoolFailImportLibrary<'a> {
1121-
pub dlltool_path: Cow<'a, str>,
1122-
pub dlltool_args: String,
1123-
pub stdout: Cow<'a, str>,
1124-
pub stderr: Cow<'a, str>,
1125-
}
1126-
1127-
#[derive(Diagnostic)]
1128-
#[diag("error writing .DEF file: {$error}")]
1129-
pub(crate) struct ErrorWritingDEFFile {
1130-
pub error: std::io::Error,
1131-
}
1132-
1133-
#[derive(Diagnostic)]
1134-
#[diag("error calling dlltool '{$dlltool_path}': {$error}")]
1135-
pub(crate) struct ErrorCallingDllTool<'a> {
1136-
pub dlltool_path: Cow<'a, str>,
1137-
pub error: std::io::Error,
1138-
}
1139-
11401114
#[derive(Diagnostic)]
11411115
#[diag("failed to create remark directory: {$error}")]
11421116
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;
@@ -442,7 +442,7 @@ impl CodegenBackend for DummyCodegenBackend {
442442

443443
link_binary(
444444
sess,
445-
&DummyArchiveBuilderBuilder,
445+
&ArArchiveBuilderBuilder,
446446
compiled_modules,
447447
crate_info,
448448
metadata,
@@ -452,28 +452,6 @@ impl CodegenBackend for DummyCodegenBackend {
452452
}
453453
}
454454

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