Skip to content

Commit db9a84a

Browse files
committed
MinGW: emit dllexport/dllimport by rustc
This fixes various cases where LD could not guess dllexport correctly and greatly improves compatibility with LLD which is not going to support linker scripts anytime soon
1 parent 6ee1b62 commit db9a84a

File tree

6 files changed

+47
-14
lines changed

6 files changed

+47
-14
lines changed

src/librustc_codegen_llvm/callee.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,12 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value
168168
}
169169
}
170170

171-
if cx.use_dll_storage_attrs && tcx.is_dllimport_foreign_item(instance_def_id) {
171+
// MinGW: For backward compatibility we rely on the linker to decide whether it
172+
// should use dllimport for functions.
173+
if cx.use_dll_storage_attrs
174+
&& tcx.is_dllimport_foreign_item(instance_def_id)
175+
&& tcx.sess.target.target.target_env != "gnu"
176+
{
172177
unsafe {
173178
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
174179
}

src/librustc_codegen_llvm/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ impl CodegenCx<'ll, 'tcx> {
281281
// argument validation.
282282
debug_assert!(
283283
!(self.tcx.sess.opts.cg.linker_plugin_lto.enabled()
284-
&& self.tcx.sess.target.target.options.is_like_msvc
284+
&& self.tcx.sess.target.target.options.is_like_windows
285285
&& self.tcx.sess.opts.cg.prefer_dynamic)
286286
);
287287

src/librustc_codegen_llvm/context.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,16 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
217217
// attributes in LLVM IR as well as native dependencies (in C these
218218
// correspond to `__declspec(dllimport)`).
219219
//
220-
// Whenever a dynamic library is built by MSVC it must have its public
220+
// LD (BFD) in MinGW mode can often correctly guess `dllexport` but
221+
// relying on that can result in issues like #50176.
222+
// LLD won't support that and expects symbols with proper attributes.
223+
// Because of that we make MinGW target emit dllexport just like MSVC.
224+
// When it comes to dllimport we use it for constants but for functions
225+
// rely on the linker to do the right thing. Opposed to dllexport this
226+
// task is easy for them (both LD and LLD) and allows us to easily use
227+
// symbols from static libraries in shared libraries.
228+
//
229+
// Whenever a dynamic library is built on Windows it must have its public
221230
// interface specified by functions tagged with `dllexport` or otherwise
222231
// they're not available to be linked against. This poses a few problems
223232
// for the compiler, some of which are somewhat fundamental, but we use
@@ -254,8 +263,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
254263
// this effect) by marking very little as `dllimport` and praying the
255264
// linker will take care of everything. Fixing this problem will likely
256265
// require adding a few attributes to Rust itself (feature gated at the
257-
// start) and then strongly recommending static linkage on MSVC!
258-
let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc;
266+
// start) and then strongly recommending static linkage on Windows!
267+
let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_windows;
259268

260269
let check_overflow = tcx.sess.overflow_checks();
261270

src/librustc_codegen_ssa/back/linker.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,9 @@ impl<'a> Linker for GccLinker<'a> {
523523
return;
524524
}
525525

526+
let is_windows = self.sess.target.target.options.is_like_windows;
526527
let mut arg = OsString::new();
527-
let path = tmpdir.join("list");
528+
let path = tmpdir.join(if is_windows { "list.def" } else { "list" });
528529

529530
debug!("EXPORTED SYMBOLS:");
530531

@@ -540,6 +541,21 @@ impl<'a> Linker for GccLinker<'a> {
540541
if let Err(e) = res {
541542
self.sess.fatal(&format!("failed to write lib.def file: {}", e));
542543
}
544+
} else if is_windows {
545+
let res: io::Result<()> = try {
546+
let mut f = BufWriter::new(File::create(&path)?);
547+
548+
// .def file similar to MSVC one but without LIBRARY section
549+
// because LD doesn't like when it's empty
550+
writeln!(f, "EXPORTS")?;
551+
for symbol in self.info.exports[&crate_type].iter() {
552+
debug!(" _{}", symbol);
553+
writeln!(f, " {}", symbol)?;
554+
}
555+
};
556+
if let Err(e) = res {
557+
self.sess.fatal(&format!("failed to write list.def file: {}", e));
558+
}
543559
} else {
544560
// Write an LD version script
545561
let res: io::Result<()> = try {
@@ -573,7 +589,10 @@ impl<'a> Linker for GccLinker<'a> {
573589
if !self.is_ld {
574590
arg.push("-Wl,")
575591
}
576-
arg.push("--version-script=");
592+
// Both LD and LLD accept export list in *.def file form, there are no flags required
593+
if !is_windows {
594+
arg.push("--version-script=")
595+
}
577596
}
578597

579598
arg.push(&path);

src/librustc_codegen_ssa/back/write.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1846,11 +1846,11 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
18461846
// something is wrong with commandline arg validation.
18471847
assert!(
18481848
!(tcx.sess.opts.cg.linker_plugin_lto.enabled()
1849-
&& tcx.sess.target.target.options.is_like_msvc
1849+
&& tcx.sess.target.target.options.is_like_windows
18501850
&& tcx.sess.opts.cg.prefer_dynamic)
18511851
);
18521852

1853-
tcx.sess.target.target.options.is_like_msvc &&
1853+
tcx.sess.target.target.options.is_like_windows &&
18541854
tcx.sess.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
18551855
// ThinLTO can't handle this workaround in all cases, so we don't
18561856
// emit the `__imp_` symbols. Instead we make them unnecessary by disallowing

src/librustc_session/session.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1294,19 +1294,19 @@ pub fn build_session(
12941294
// commandline argument, you can do so here.
12951295
fn validate_commandline_args_with_session_available(sess: &Session) {
12961296
// Since we don't know if code in an rlib will be linked to statically or
1297-
// dynamically downstream, rustc generates `__imp_` symbols that help the
1298-
// MSVC linker deal with this lack of knowledge (#27438). Unfortunately,
1297+
// dynamically downstream, rustc generates `__imp_` symbols that help linkers
1298+
// on Windows deal with this lack of knowledge (#27438). Unfortunately,
12991299
// these manually generated symbols confuse LLD when it tries to merge
1300-
// bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC
1300+
// bitcode during ThinLTO. Therefore we disallow dynamic linking on Windows
13011301
// when compiling for LLD ThinLTO. This way we can validly just not generate
13021302
// the `dllimport` attributes and `__imp_` symbols in that case.
13031303
if sess.opts.cg.linker_plugin_lto.enabled()
13041304
&& sess.opts.cg.prefer_dynamic
1305-
&& sess.target.target.options.is_like_msvc
1305+
&& sess.target.target.options.is_like_windows
13061306
{
13071307
sess.err(
13081308
"Linker plugin based LTO is not supported together with \
1309-
`-C prefer-dynamic` when targeting MSVC",
1309+
`-C prefer-dynamic` when targeting Windows-like targets",
13101310
);
13111311
}
13121312

0 commit comments

Comments
 (0)