diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8c52ed6ed1234..44bfc746a741f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1976,9 +1976,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], ) { - if symbols.is_empty() { + if linked_symbols.is_empty() && exported_symbols.is_empty() { return; } @@ -2015,7 +2017,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, @@ -2073,6 +2075,38 @@ 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| format!(" /EXPORT:\"{sym}\"")) + .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 { @@ -2247,7 +2281,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 8fc83908efbcc..eb421e7e45ec5 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1086,47 +1086,8 @@ 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]) { - // 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 in symbols { - debug!(" _{symbol}"); - writeln!(f, " {symbol}")?; - } - }; - 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); + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) { + // 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;