diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8882ba359b774..ee77e9e191396 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1957,9 +1957,11 @@ fn add_linked_symbol_object( cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, - symbols: &[(String, SymbolExportKind)], + crate_type: CrateType, + linked_symbols: &[(String, SymbolExportKind)], + exported_symbols: &[(String, SymbolExportKind)], ) { - if symbols.is_empty() { + if linked_symbols.is_empty() && exported_symbols.is_empty() { return; } @@ -1996,7 +1998,7 @@ fn add_linked_symbol_object( None }; - for (sym, kind) in symbols.iter() { + for (sym, kind) in linked_symbols.iter() { let symbol = file.add_symbol(object::write::Symbol { name: sym.clone().into(), value: 0, @@ -2054,6 +2056,41 @@ fn add_linked_symbol_object( } } + if sess.target.is_like_msvc { + // Symbol visibility takes care of this for executables typically + let should_filter_symbols = if crate_type == CrateType::Executable { + sess.opts.unstable_opts.export_executable_symbols + } else { + true + }; + if should_filter_symbols { + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this adds a + // `.drectve` section which lists all the symbols using /EXPORT arguments. + // + // The linker will read these arguments from the `.drectve` section and + // export all the symbols from the dynamic library. Note that this is not + // as simple as just exporting all the symbols in the current crate (as + // specified by `codegen.reachable`) but rather we also need to possibly + // export the symbols of upstream crates. Upstream rlibs may be linked + // statically to this dynamic library, in which case they may continue to + // transitively be used and hence need their symbols exported. + let drectve = exported_symbols + .into_iter() + .map(|(sym, kind)| match kind { + SymbolExportKind::Text | SymbolExportKind::Tls => format!(" /EXPORT:\"{sym}\""), + SymbolExportKind::Data => format!(" /EXPORT:\"{sym}\",DATA"), + }) + .collect::>() + .join(""); + + let section = + file.add_section(vec![], b".drectve".to_vec(), object::SectionKind::Linker); + file.append_section_data(section, drectve.as_bytes(), 1); + } + } + let path = tmpdir.join("symbols.o"); let result = std::fs::write(&path, file.write().unwrap()); if let Err(error) = result { @@ -2228,7 +2265,9 @@ fn linker_with_args( cmd, sess, tmpdir, + crate_type, &codegen_results.crate_info.linked_symbols[&crate_type], + &codegen_results.crate_info.exported_symbols[&crate_type], ); // Sanitizer libraries. diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index b6892bb63e856..ff81e6ed48c79 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1097,53 +1097,13 @@ impl<'a> Linker for MsvcLinker<'a> { } } - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `codegen.reachable`) - // but rather we also need to possibly export the symbols of upstream - // crates. Upstream rlibs may be linked statically to this dynamic library, - // in which case they may continue to transitively be used and hence need - // their symbols exported. fn export_symbols( &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], + _tmpdir: &Path, + _crate_type: CrateType, + _symbols: &[(String, SymbolExportKind)], ) { - // Symbol visibility takes care of this typically - if crate_type == CrateType::Executable { - let should_export_executable_symbols = - self.sess.opts.unstable_opts.export_executable_symbols; - if !should_export_executable_symbols { - return; - } - } - - let path = tmpdir.join("lib.def"); - let res: io::Result<()> = try { - let mut f = File::create_buffered(&path)?; - - // Start off with the standard module name header and then go - // straight to exports. - writeln!(f, "LIBRARY")?; - writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; - debug!(" _{symbol}"); - writeln!(f, " {symbol}{kind_marker}")?; - } - }; - if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); - } - let mut arg = OsString::from("/DEF:"); - arg.push(path); - self.link_arg(&arg); + // We already add /EXPORT arguments to the .drectve section of symbols.o. } fn subsystem(&mut self, subsystem: &str) { diff --git a/tests/ui/linking/dll-weak-definition.rs b/tests/ui/linking/dll-weak-definition.rs new file mode 100644 index 0000000000000..c64bd11f84796 --- /dev/null +++ b/tests/ui/linking/dll-weak-definition.rs @@ -0,0 +1,20 @@ +// Regression test for MSVC link.exe failing to export weak definitions from dlls. +// See https://github.com/rust-lang/rust/pull/142568 + +//@ build-pass +//@ only-msvc +//@ revisions: link_exe lld +//@[lld] needs-rust-lld +//@[link_exe] compile-flags: -Zunstable-options -Clink-self-contained=-linker -Zlinker-features=-lld +//@[lld] compile-flags: -Zunstable-options -Clink-self-contained=+linker -Zlinker-features=+lld + +#![feature(linkage)] +#![crate_type = "cdylib"] + +#[linkage = "weak"] +#[no_mangle] +pub fn weak_function() {} + +#[linkage = "weak"] +#[no_mangle] +pub static WEAK_STATIC: u8 = 42;