Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 355eb9c

Browse files
authoredAug 7, 2024
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 rust-lang/rustc_codegen_cranelift#1345
2 parents 9bad7ba + f58e737 commit 355eb9c

File tree

15 files changed

+332
-317
lines changed

15 files changed

+332
-317
lines changed
 

‎Cargo.lock

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,11 @@ dependencies = [
205205

206206
[[package]]
207207
name = "ar_archive_writer"
208-
version = "0.3.0"
208+
version = "0.3.3"
209209
source = "registry+https://github.com/rust-lang/crates.io-index"
210-
checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733"
210+
checksum = "3f2bcb7cf51decfbbfc7ef476e28b0775b13e5eb1190f8b7df145cd53d4f4374"
211211
dependencies = [
212-
"object 0.35.0",
212+
"object 0.36.2",
213213
]
214214

215215
[[package]]
@@ -2454,15 +2454,6 @@ dependencies = [
24542454
"ruzstd 0.5.0",
24552455
]
24562456

2457-
[[package]]
2458-
name = "object"
2459-
version = "0.35.0"
2460-
source = "registry+https://github.com/rust-lang/crates.io-index"
2461-
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
2462-
dependencies = [
2463-
"memchr",
2464-
]
2465-
24662457
[[package]]
24672458
name = "object"
24682459
version = "0.36.2"

‎compiler/rustc_codegen_cranelift/src/archive.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::path::{Path, PathBuf};
1+
use std::path::Path;
22

33
use rustc_codegen_ssa::back::archive::{
44
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
@@ -16,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
1616
&self,
1717
sess: &Session,
1818
_lib_name: &str,
19-
_dll_imports: &[rustc_session::cstore::DllImport],
20-
_tmpdir: &Path,
21-
_is_direct_dependency: bool,
22-
) -> PathBuf {
19+
_import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
20+
_output_path: &Path,
21+
) {
2322
sess.dcx().fatal("raw-dylib is not yet supported by rustc_codegen_cranelift");
2423
}
2524
}
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use std::path::{Path, PathBuf};
1+
use std::path::Path;
22

33
use rustc_codegen_ssa::back::archive::{
44
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
55
};
6-
use rustc_session::cstore::DllImport;
76
use rustc_session::Session;
87

98
pub(crate) struct ArArchiveBuilderBuilder;
@@ -17,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
1716
&self,
1817
_sess: &Session,
1918
_lib_name: &str,
20-
_dll_imports: &[DllImport],
21-
_tmpdir: &Path,
22-
_is_direct_dependency: bool,
23-
) -> PathBuf {
19+
_import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
20+
_output_path: &Path,
21+
) {
2422
unimplemented!("creating dll imports is not yet supported");
2523
}
2624
}

‎compiler/rustc_codegen_llvm/messages.ftl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
22
3-
codegen_llvm_dlltool_fail_import_library =
4-
Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
5-
{$stdout}
6-
{$stderr}
7-
83
codegen_llvm_dynamic_linking_with_lto =
94
cannot prefer dynamic linking when performing LTO
105
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
116
12-
codegen_llvm_error_calling_dlltool =
13-
Error calling dlltool '{$dlltool_path}': {$error}
147
158
codegen_llvm_error_creating_import_library =
169
Error creating import library for {$lib_name}: {$error}
1710
18-
codegen_llvm_error_writing_def_file =
19-
Error writing .DEF file: {$error}
20-
2111
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
2212
2313
codegen_llvm_from_llvm_diag = {$message}

‎compiler/rustc_codegen_llvm/src/back/archive.rs

Lines changed: 19 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
//! A helper class for dealing with static archives
22
3-
use std::ffi::{c_char, c_void, CStr, CString, OsString};
3+
use std::ffi::{c_char, c_void, CStr, CString};
44
use std::path::{Path, PathBuf};
5-
use std::{env, io, mem, ptr, str};
5+
use std::{io, mem, ptr, str};
66

77
use rustc_codegen_ssa::back::archive::{
8-
try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder,
9-
ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER,
8+
create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder,
9+
ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind,
10+
DEFAULT_OBJECT_READER,
1011
};
11-
use rustc_session::cstore::DllImport;
12+
use rustc_codegen_ssa::common;
1213
use rustc_session::Session;
1314
use tracing::trace;
1415

15-
use crate::common;
16-
use crate::errors::{
17-
DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile,
18-
};
16+
use crate::errors::ErrorCreatingImportLibrary;
1917
use crate::llvm::archive_ro::{ArchiveRO, Child};
2018
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
2119

@@ -121,116 +119,21 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
121119
&self,
122120
sess: &Session,
123121
lib_name: &str,
124-
dll_imports: &[DllImport],
125-
tmpdir: &Path,
126-
is_direct_dependency: bool,
127-
) -> PathBuf {
128-
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
129-
let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib"));
130-
131-
let target = &sess.target;
132-
let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target);
133-
134-
let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
135-
.iter()
136-
.map(|import: &DllImport| {
137-
if sess.target.arch == "x86" {
138-
(
139-
common::i686_decorated_name(import, mingw_gnu_toolchain, false),
140-
import.ordinal(),
141-
)
142-
} else {
143-
(import.name.to_string(), import.ordinal())
144-
}
145-
})
146-
.collect();
147-
148-
if mingw_gnu_toolchain {
122+
import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
123+
output_path: &Path,
124+
) {
125+
if common::is_mingw_gnu_toolchain(&sess.target) {
149126
// The binutils linker used on -windows-gnu targets cannot read the import
150127
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
151128
// that loaded but crashed with an AV upon calling one of the imported
152129
// functions. Therefore, use binutils to create the import library instead,
153130
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
154-
let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def"));
155-
156-
let def_file_content = format!(
157-
"EXPORTS\n{}",
158-
import_name_and_ordinal_vector
159-
.into_iter()
160-
.map(|(name, ordinal)| {
161-
match ordinal {
162-
Some(n) => format!("{name} @{n} NONAME"),
163-
None => name,
164-
}
165-
})
166-
.collect::<Vec<String>>()
167-
.join("\n")
131+
create_mingw_dll_import_lib(
132+
sess,
133+
lib_name,
134+
import_name_and_ordinal_vector,
135+
output_path,
168136
);
169-
170-
match std::fs::write(&def_file_path, def_file_content) {
171-
Ok(_) => {}
172-
Err(e) => {
173-
sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
174-
}
175-
};
176-
177-
// --no-leading-underscore: For the `import_name_type` feature to work, we need to be
178-
// able to control the *exact* spelling of each of the symbols that are being imported:
179-
// hence we don't want `dlltool` adding leading underscores automatically.
180-
let dlltool = find_binutils_dlltool(sess);
181-
let temp_prefix = {
182-
let mut path = PathBuf::from(&output_path);
183-
path.pop();
184-
path.push(lib_name);
185-
path
186-
};
187-
// dlltool target architecture args from:
188-
// https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
189-
let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() {
190-
"x86_64" => ("i386:x86-64", "--64"),
191-
"x86" => ("i386", "--32"),
192-
"aarch64" => ("arm64", "--64"),
193-
"arm" => ("arm", "--32"),
194-
_ => panic!("unsupported arch {}", sess.target.arch),
195-
};
196-
let mut dlltool_cmd = std::process::Command::new(&dlltool);
197-
dlltool_cmd
198-
.arg("-d")
199-
.arg(def_file_path)
200-
.arg("-D")
201-
.arg(lib_name)
202-
.arg("-l")
203-
.arg(&output_path)
204-
.arg("-m")
205-
.arg(dlltool_target_arch)
206-
.arg("-f")
207-
.arg(dlltool_target_bitness)
208-
.arg("--no-leading-underscore")
209-
.arg("--temp-prefix")
210-
.arg(temp_prefix);
211-
212-
match dlltool_cmd.output() {
213-
Err(e) => {
214-
sess.dcx().emit_fatal(ErrorCallingDllTool {
215-
dlltool_path: dlltool.to_string_lossy(),
216-
error: e,
217-
});
218-
}
219-
// dlltool returns '0' on failure, so check for error output instead.
220-
Ok(output) if !output.stderr.is_empty() => {
221-
sess.dcx().emit_fatal(DlltoolFailImportLibrary {
222-
dlltool_path: dlltool.to_string_lossy(),
223-
dlltool_args: dlltool_cmd
224-
.get_args()
225-
.map(|arg| arg.to_string_lossy())
226-
.collect::<Vec<_>>()
227-
.join(" "),
228-
stdout: String::from_utf8_lossy(&output.stdout),
229-
stderr: String::from_utf8_lossy(&output.stderr),
230-
})
231-
}
232-
_ => {}
233-
}
234137
} else {
235138
// we've checked for \0 characters in the library name already
236139
let dll_name_z = CString::new(lib_name).unwrap();
@@ -242,9 +145,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
242145
trace!(" output_path {}", output_path.display());
243146
trace!(
244147
" import names: {}",
245-
dll_imports
148+
import_name_and_ordinal_vector
246149
.iter()
247-
.map(|import| import.name.to_string())
150+
.map(|(name, _ordinal)| name.clone())
248151
.collect::<Vec<_>>()
249152
.join(", "),
250153
);
@@ -281,9 +184,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
281184
error: llvm::last_error().unwrap_or("unknown LLVM error".to_string()),
282185
});
283186
}
284-
};
285-
286-
output_path
187+
}
287188
}
288189
}
289190

@@ -457,39 +358,3 @@ impl<'a> LlvmArchiveBuilder<'a> {
457358
fn string_to_io_error(s: String) -> io::Error {
458359
io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
459360
}
460-
461-
fn find_binutils_dlltool(sess: &Session) -> OsString {
462-
assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
463-
if let Some(dlltool_path) = &sess.opts.cg.dlltool {
464-
return dlltool_path.clone().into_os_string();
465-
}
466-
467-
let tool_name: OsString = if sess.host.options.is_like_windows {
468-
// If we're compiling on Windows, always use "dlltool.exe".
469-
"dlltool.exe"
470-
} else {
471-
// On other platforms, use the architecture-specific name.
472-
match sess.target.arch.as_ref() {
473-
"x86_64" => "x86_64-w64-mingw32-dlltool",
474-
"x86" => "i686-w64-mingw32-dlltool",
475-
"aarch64" => "aarch64-w64-mingw32-dlltool",
476-
477-
// For non-standard architectures (e.g., aarch32) fallback to "dlltool".
478-
_ => "dlltool",
479-
}
480-
}
481-
.into();
482-
483-
// NOTE: it's not clear how useful it is to explicitly search PATH.
484-
for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
485-
let full_path = dir.join(&tool_name);
486-
if full_path.is_file() {
487-
return full_path.into_os_string();
488-
}
489-
}
490-
491-
// The user didn't specify the location of the dlltool binary, and we weren't able
492-
// to find the appropriate one on the PATH. Just return the name of the tool
493-
// and let the invocation fail with a hopefully useful error message.
494-
tool_name
495-
}

‎compiler/rustc_codegen_llvm/src/callee.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
//! and methods are represented as just a fn ptr and not a full
55
//! closure.
66
7+
use rustc_codegen_ssa::common;
78
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
89
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
910
use tracing::debug;
1011

1112
use crate::context::CodegenCx;
1213
use crate::value::Value;
13-
use crate::{attributes, common, llvm};
14+
use crate::{attributes, llvm};
1415

1516
/// Codegens a reference to a fn/method item, monomorphizing and
1617
/// inlining as it goes.
@@ -46,7 +47,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
4647
} else {
4748
let instance_def_id = instance.def_id();
4849
let llfn = if tcx.sess.target.arch == "x86"
49-
&& let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym)
50+
&& let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym)
5051
{
5152
// Fix for https://github.com/rust-lang/rust/issues/104453
5253
// On x86 Windows, LLVM uses 'L' as the prefix for any private

‎compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! Code that is useful in various codegen modules.
22
3-
use std::fmt::Write;
4-
53
use libc::{c_char, c_uint};
64
use rustc_ast::Mutability;
75
use rustc_codegen_ssa::traits::*;
@@ -10,9 +8,8 @@ use rustc_hir::def_id::DefId;
108
use rustc_middle::bug;
119
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
1210
use rustc_middle::ty::TyCtxt;
13-
use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
11+
use rustc_session::cstore::DllImport;
1412
use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
15-
use rustc_target::spec::Target;
1613
use tracing::debug;
1714

1815
use crate::consts::const_alloc_to_llvm;
@@ -379,64 +376,3 @@ pub(crate) fn get_dllimport<'tcx>(
379376
tcx.native_library(id)
380377
.and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name))
381378
}
382-
383-
pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool {
384-
target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
385-
}
386-
387-
pub(crate) fn i686_decorated_name(
388-
dll_import: &DllImport,
389-
mingw: bool,
390-
disable_name_mangling: bool,
391-
) -> String {
392-
let name = dll_import.name.as_str();
393-
394-
let (add_prefix, add_suffix) = match dll_import.import_name_type {
395-
Some(PeImportNameType::NoPrefix) => (false, true),
396-
Some(PeImportNameType::Undecorated) => (false, false),
397-
_ => (true, true),
398-
};
399-
400-
// Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__).
401-
let mut decorated_name = String::with_capacity(name.len() + 6);
402-
403-
if disable_name_mangling {
404-
// LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled.
405-
decorated_name.push('\x01');
406-
}
407-
408-
let prefix = if add_prefix && dll_import.is_fn {
409-
match dll_import.calling_convention {
410-
DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
411-
DllCallingConvention::Stdcall(_) => (!mingw
412-
|| dll_import.import_name_type == Some(PeImportNameType::Decorated))
413-
.then_some('_'),
414-
DllCallingConvention::Fastcall(_) => Some('@'),
415-
}
416-
} else if !dll_import.is_fn && !mingw {
417-
// For static variables, prefix with '_' on MSVC.
418-
Some('_')
419-
} else {
420-
None
421-
};
422-
if let Some(prefix) = prefix {
423-
decorated_name.push(prefix);
424-
}
425-
426-
decorated_name.push_str(name);
427-
428-
if add_suffix && dll_import.is_fn {
429-
match dll_import.calling_convention {
430-
DllCallingConvention::C => {}
431-
DllCallingConvention::Stdcall(arg_list_size)
432-
| DllCallingConvention::Fastcall(arg_list_size) => {
433-
write!(&mut decorated_name, "@{arg_list_size}").unwrap();
434-
}
435-
DllCallingConvention::Vectorcall(arg_list_size) => {
436-
write!(&mut decorated_name, "@@{arg_list_size}").unwrap();
437-
}
438-
}
439-
}
440-
441-
decorated_name
442-
}

‎compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::ops::Range;
22

3+
use rustc_codegen_ssa::common;
34
use rustc_codegen_ssa::traits::*;
45
use rustc_hir::def::DefKind;
56
use rustc_hir::def_id::DefId;
@@ -18,7 +19,7 @@ use rustc_target::abi::{
1819
};
1920
use tracing::{debug, instrument, trace};
2021

21-
use crate::common::{self, CodegenCx};
22+
use crate::common::CodegenCx;
2223
use crate::errors::{
2324
InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined,
2425
};
@@ -195,7 +196,7 @@ fn check_and_apply_linkage<'ll, 'tcx>(
195196
g2
196197
}
197198
} else if cx.tcx.sess.target.arch == "x86"
198-
&& let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym)
199+
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
199200
{
200201
cx.declare_global(
201202
&common::i686_decorated_name(

‎compiler/rustc_codegen_llvm/src/errors.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::borrow::Cow;
21
use std::ffi::CString;
32
use std::path::Path;
43

@@ -71,28 +70,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge {
7170
#[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
7271
pub(crate) struct SanitizerMemtagRequiresMte;
7372

74-
#[derive(Diagnostic)]
75-
#[diag(codegen_llvm_error_writing_def_file)]
76-
pub(crate) struct ErrorWritingDEFFile {
77-
pub error: std::io::Error,
78-
}
79-
80-
#[derive(Diagnostic)]
81-
#[diag(codegen_llvm_error_calling_dlltool)]
82-
pub(crate) struct ErrorCallingDllTool<'a> {
83-
pub dlltool_path: Cow<'a, str>,
84-
pub error: std::io::Error,
85-
}
86-
87-
#[derive(Diagnostic)]
88-
#[diag(codegen_llvm_dlltool_fail_import_library)]
89-
pub(crate) struct DlltoolFailImportLibrary<'a> {
90-
pub dlltool_path: Cow<'a, str>,
91-
pub dlltool_args: String,
92-
pub stdout: Cow<'a, str>,
93-
pub stderr: Cow<'a, str>,
94-
}
95-
9673
#[derive(Diagnostic)]
9774
#[diag(codegen_llvm_dynamic_linking_with_lto)]
9875
#[note]

‎compiler/rustc_codegen_ssa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
# tidy-alphabetical-start
8-
ar_archive_writer = "0.3.0"
8+
ar_archive_writer = "0.3.3"
99
arrayvec = { version = "0.7", default-features = false }
1010
bitflags = "2.4.1"
1111
cc = "1.0.90"

‎compiler/rustc_codegen_ssa/messages.ftl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e
2424
2525
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
2626
27+
codegen_ssa_dlltool_fail_import_library =
28+
Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
29+
{$stdout}
30+
{$stderr}
31+
32+
codegen_ssa_error_calling_dlltool =
33+
Error calling dlltool '{$dlltool_path}': {$error}
34+
2735
codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
2836
37+
codegen_ssa_error_writing_def_file =
38+
Error writing .DEF file: {$error}
39+
2940
codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
3041
3142
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

‎compiler/rustc_codegen_ssa/src/back/archive.rs

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use std::env;
12
use std::error::Error;
3+
use std::ffi::OsString;
24
use std::fs::{self, File};
35
use std::io::{self, Write};
46
use std::path::{Path, PathBuf};
@@ -9,14 +11,14 @@ use object::read::archive::ArchiveFile;
911
use object::read::macho::FatArch;
1012
use rustc_data_structures::fx::FxIndexSet;
1113
use rustc_data_structures::memmap::Mmap;
12-
use rustc_session::cstore::DllImport;
1314
use rustc_session::Session;
1415
use rustc_span::symbol::Symbol;
1516
use tempfile::Builder as TempFileBuilder;
1617

1718
use super::metadata::search_for_section;
1819
// Re-exporting for rustc_codegen_llvm::back::archive
1920
pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
21+
use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile};
2022

2123
pub trait ArchiveBuilderBuilder {
2224
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
@@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder {
3032
&self,
3133
sess: &Session,
3234
lib_name: &str,
33-
dll_imports: &[DllImport],
34-
tmpdir: &Path,
35-
is_direct_dependency: bool,
36-
) -> PathBuf;
35+
import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
36+
output_path: &Path,
37+
);
3738

3839
fn extract_bundled_libs<'a>(
3940
&'a self,
@@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder {
7273
}
7374
}
7475

76+
pub fn create_mingw_dll_import_lib(
77+
sess: &Session,
78+
lib_name: &str,
79+
import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
80+
output_path: &Path,
81+
) {
82+
let def_file_path = output_path.with_extension("def");
83+
84+
let def_file_content = format!(
85+
"EXPORTS\n{}",
86+
import_name_and_ordinal_vector
87+
.into_iter()
88+
.map(|(name, ordinal)| {
89+
match ordinal {
90+
Some(n) => format!("{name} @{n} NONAME"),
91+
None => name,
92+
}
93+
})
94+
.collect::<Vec<String>>()
95+
.join("\n")
96+
);
97+
98+
match std::fs::write(&def_file_path, def_file_content) {
99+
Ok(_) => {}
100+
Err(e) => {
101+
sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
102+
}
103+
};
104+
105+
// --no-leading-underscore: For the `import_name_type` feature to work, we need to be
106+
// able to control the *exact* spelling of each of the symbols that are being imported:
107+
// hence we don't want `dlltool` adding leading underscores automatically.
108+
let dlltool = find_binutils_dlltool(sess);
109+
let temp_prefix = {
110+
let mut path = PathBuf::from(&output_path);
111+
path.pop();
112+
path.push(lib_name);
113+
path
114+
};
115+
// dlltool target architecture args from:
116+
// https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
117+
let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() {
118+
"x86_64" => ("i386:x86-64", "--64"),
119+
"x86" => ("i386", "--32"),
120+
"aarch64" => ("arm64", "--64"),
121+
"arm" => ("arm", "--32"),
122+
_ => panic!("unsupported arch {}", sess.target.arch),
123+
};
124+
let mut dlltool_cmd = std::process::Command::new(&dlltool);
125+
dlltool_cmd
126+
.arg("-d")
127+
.arg(def_file_path)
128+
.arg("-D")
129+
.arg(lib_name)
130+
.arg("-l")
131+
.arg(&output_path)
132+
.arg("-m")
133+
.arg(dlltool_target_arch)
134+
.arg("-f")
135+
.arg(dlltool_target_bitness)
136+
.arg("--no-leading-underscore")
137+
.arg("--temp-prefix")
138+
.arg(temp_prefix);
139+
140+
match dlltool_cmd.output() {
141+
Err(e) => {
142+
sess.dcx().emit_fatal(ErrorCallingDllTool {
143+
dlltool_path: dlltool.to_string_lossy(),
144+
error: e,
145+
});
146+
}
147+
// dlltool returns '0' on failure, so check for error output instead.
148+
Ok(output) if !output.stderr.is_empty() => {
149+
sess.dcx().emit_fatal(DlltoolFailImportLibrary {
150+
dlltool_path: dlltool.to_string_lossy(),
151+
dlltool_args: dlltool_cmd
152+
.get_args()
153+
.map(|arg| arg.to_string_lossy())
154+
.collect::<Vec<_>>()
155+
.join(" "),
156+
stdout: String::from_utf8_lossy(&output.stdout),
157+
stderr: String::from_utf8_lossy(&output.stderr),
158+
})
159+
}
160+
_ => {}
161+
}
162+
}
163+
164+
fn find_binutils_dlltool(sess: &Session) -> OsString {
165+
assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
166+
if let Some(dlltool_path) = &sess.opts.cg.dlltool {
167+
return dlltool_path.clone().into_os_string();
168+
}
169+
170+
let tool_name: OsString = if sess.host.options.is_like_windows {
171+
// If we're compiling on Windows, always use "dlltool.exe".
172+
"dlltool.exe"
173+
} else {
174+
// On other platforms, use the architecture-specific name.
175+
match sess.target.arch.as_ref() {
176+
"x86_64" => "x86_64-w64-mingw32-dlltool",
177+
"x86" => "i686-w64-mingw32-dlltool",
178+
"aarch64" => "aarch64-w64-mingw32-dlltool",
179+
180+
// For non-standard architectures (e.g., aarch32) fallback to "dlltool".
181+
_ => "dlltool",
182+
}
183+
}
184+
.into();
185+
186+
// NOTE: it's not clear how useful it is to explicitly search PATH.
187+
for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
188+
let full_path = dir.join(&tool_name);
189+
if full_path.is_file() {
190+
return full_path.into_os_string();
191+
}
192+
}
193+
194+
// The user didn't specify the location of the dlltool binary, and we weren't able
195+
// to find the appropriate one on the PATH. Just return the name of the tool
196+
// and let the invocation fail with a hopefully useful error message.
197+
tool_name
198+
}
199+
75200
pub trait ArchiveBuilder {
76201
fn add_file(&mut self, path: &Path);
77202

‎compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ use super::linker::{self, Linker};
5151
use super::metadata::{create_wrapper_file, MetadataPosition};
5252
use super::rpath::{self, RPathConfig};
5353
use crate::{
54-
errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
54+
common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo,
55+
NativeLib,
5556
};
5657

5758
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@@ -390,17 +391,13 @@ fn link_rlib<'a>(
390391
}
391392
}
392393

393-
for (raw_dylib_name, raw_dylib_imports) in
394-
collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())?
395-
{
396-
let output_path = archive_builder_builder.create_dll_import_lib(
397-
sess,
398-
&raw_dylib_name,
399-
&raw_dylib_imports,
400-
tmpdir.as_ref(),
401-
true,
402-
);
403-
394+
for output_path in create_dll_import_libs(
395+
sess,
396+
archive_builder_builder,
397+
codegen_results.crate_info.used_libraries.iter(),
398+
tmpdir.as_ref(),
399+
true,
400+
)? {
404401
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
405402
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
406403
});
@@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>(
488485
.collect())
489486
}
490487

488+
fn create_dll_import_libs<'a>(
489+
sess: &Session,
490+
archive_builder_builder: &dyn ArchiveBuilderBuilder,
491+
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
492+
tmpdir: &Path,
493+
is_direct_dependency: bool,
494+
) -> Result<Vec<PathBuf>, ErrorGuaranteed> {
495+
Ok(collate_raw_dylibs(sess, used_libraries)?
496+
.into_iter()
497+
.map(|(raw_dylib_name, raw_dylib_imports)| {
498+
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
499+
let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
500+
501+
let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
502+
503+
let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = raw_dylib_imports
504+
.iter()
505+
.map(|import: &DllImport| {
506+
if sess.target.arch == "x86" {
507+
(
508+
common::i686_decorated_name(import, mingw_gnu_toolchain, false),
509+
import.ordinal(),
510+
)
511+
} else {
512+
(import.name.to_string(), import.ordinal())
513+
}
514+
})
515+
.collect();
516+
517+
archive_builder_builder.create_dll_import_lib(
518+
sess,
519+
&raw_dylib_name,
520+
import_name_and_ordinal_vector,
521+
&output_path,
522+
);
523+
524+
output_path
525+
})
526+
.collect())
527+
}
528+
491529
/// Create a static archive.
492530
///
493531
/// 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(
23052343
);
23062344

23072345
// Link with the import library generated for any raw-dylib functions.
2308-
for (raw_dylib_name, raw_dylib_imports) in
2309-
collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())?
2310-
{
2311-
cmd.add_object(&archive_builder_builder.create_dll_import_lib(
2312-
sess,
2313-
&raw_dylib_name,
2314-
&raw_dylib_imports,
2315-
tmpdir,
2316-
true,
2317-
));
2346+
for output_path in create_dll_import_libs(
2347+
sess,
2348+
archive_builder_builder,
2349+
codegen_results.crate_info.used_libraries.iter(),
2350+
tmpdir,
2351+
true,
2352+
)? {
2353+
cmd.add_object(&output_path);
23182354
}
23192355
// As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
23202356
// they are used within inlined functions or instantiated generic functions. We do this *after*
@@ -2339,16 +2375,14 @@ fn linker_with_args(
23392375
.flatten()
23402376
.collect::<Vec<_>>();
23412377
native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
2342-
for (raw_dylib_name, raw_dylib_imports) in
2343-
collate_raw_dylibs(sess, native_libraries_from_nonstatics)?
2344-
{
2345-
cmd.add_object(&archive_builder_builder.create_dll_import_lib(
2346-
sess,
2347-
&raw_dylib_name,
2348-
&raw_dylib_imports,
2349-
tmpdir,
2350-
false,
2351-
));
2378+
for output_path in create_dll_import_libs(
2379+
sess,
2380+
archive_builder_builder,
2381+
native_libraries_from_nonstatics,
2382+
tmpdir,
2383+
false,
2384+
)? {
2385+
cmd.add_object(&output_path);
23522386
}
23532387

23542388
// Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make

‎compiler/rustc_codegen_ssa/src/common.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use rustc_hir::LangItem;
44
use rustc_middle::ty::layout::TyAndLayout;
55
use rustc_middle::ty::{self, Instance, TyCtxt};
66
use rustc_middle::{bug, mir, span_bug};
7+
use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
78
use rustc_span::Span;
9+
use rustc_target::spec::Target;
810

911
use crate::traits::*;
1012

@@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>(
176178
_ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty),
177179
}
178180
}
181+
182+
pub fn is_mingw_gnu_toolchain(target: &Target) -> bool {
183+
target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
184+
}
185+
186+
pub fn i686_decorated_name(
187+
dll_import: &DllImport,
188+
mingw: bool,
189+
disable_name_mangling: bool,
190+
) -> String {
191+
let name = dll_import.name.as_str();
192+
193+
let (add_prefix, add_suffix) = match dll_import.import_name_type {
194+
Some(PeImportNameType::NoPrefix) => (false, true),
195+
Some(PeImportNameType::Undecorated) => (false, false),
196+
_ => (true, true),
197+
};
198+
199+
// Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__).
200+
let mut decorated_name = String::with_capacity(name.len() + 6);
201+
202+
if disable_name_mangling {
203+
// LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled.
204+
decorated_name.push('\x01');
205+
}
206+
207+
let prefix = if add_prefix && dll_import.is_fn {
208+
match dll_import.calling_convention {
209+
DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
210+
DllCallingConvention::Stdcall(_) => (!mingw
211+
|| dll_import.import_name_type == Some(PeImportNameType::Decorated))
212+
.then_some('_'),
213+
DllCallingConvention::Fastcall(_) => Some('@'),
214+
}
215+
} else if !dll_import.is_fn && !mingw {
216+
// For static variables, prefix with '_' on MSVC.
217+
Some('_')
218+
} else {
219+
None
220+
};
221+
if let Some(prefix) = prefix {
222+
decorated_name.push(prefix);
223+
}
224+
225+
decorated_name.push_str(name);
226+
227+
if add_suffix && dll_import.is_fn {
228+
use std::fmt::Write;
229+
230+
match dll_import.calling_convention {
231+
DllCallingConvention::C => {}
232+
DllCallingConvention::Stdcall(arg_list_size)
233+
| DllCallingConvention::Fastcall(arg_list_size) => {
234+
write!(&mut decorated_name, "@{arg_list_size}").unwrap();
235+
}
236+
DllCallingConvention::Vectorcall(arg_list_size) => {
237+
write!(&mut decorated_name, "@@{arg_list_size}").unwrap();
238+
}
239+
}
240+
}
241+
242+
decorated_name
243+
}

‎compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,28 @@ pub struct FailedToGetLayout<'tcx> {
10261026
pub err: LayoutError<'tcx>,
10271027
}
10281028

1029+
#[derive(Diagnostic)]
1030+
#[diag(codegen_ssa_dlltool_fail_import_library)]
1031+
pub(crate) struct DlltoolFailImportLibrary<'a> {
1032+
pub dlltool_path: Cow<'a, str>,
1033+
pub dlltool_args: String,
1034+
pub stdout: Cow<'a, str>,
1035+
pub stderr: Cow<'a, str>,
1036+
}
1037+
1038+
#[derive(Diagnostic)]
1039+
#[diag(codegen_ssa_error_writing_def_file)]
1040+
pub(crate) struct ErrorWritingDEFFile {
1041+
pub error: std::io::Error,
1042+
}
1043+
1044+
#[derive(Diagnostic)]
1045+
#[diag(codegen_ssa_error_calling_dlltool)]
1046+
pub(crate) struct ErrorCallingDllTool<'a> {
1047+
pub dlltool_path: Cow<'a, str>,
1048+
pub error: std::io::Error,
1049+
}
1050+
10291051
#[derive(Diagnostic)]
10301052
#[diag(codegen_ssa_error_creating_remark_dir)]
10311053
pub struct ErrorCreatingRemarkDir {

0 commit comments

Comments
 (0)
Please sign in to comment.