Skip to content

Commit e63c76b

Browse files
committed
Use the .drectve section for exporting symbols from dlls on Windows
While it would be reasonable to expect the Windows linker to handle linker args in the .drectve section identical to cli arguments, as it turns out exporting weak symbols only works when the /EXPORT is in the .drectve section, not when it is a linker argument or when a .DEF file is used.
1 parent 68ac5ab commit e63c76b

File tree

4 files changed

+121
-86
lines changed

4 files changed

+121
-86
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,9 +1976,10 @@ fn add_linked_symbol_object(
19761976
cmd: &mut dyn Linker,
19771977
sess: &Session,
19781978
tmpdir: &Path,
1979-
symbols: &[(String, SymbolExportKind)],
1979+
linked_symbols: &[(String, SymbolExportKind)],
1980+
exported_symbols: &[(String, SymbolExportKind)],
19801981
) {
1981-
if symbols.is_empty() {
1982+
if linked_symbols.is_empty() {
19821983
return;
19831984
}
19841985

@@ -2015,7 +2016,7 @@ fn add_linked_symbol_object(
20152016
None
20162017
};
20172018

2018-
for (sym, kind) in symbols.iter() {
2019+
for (sym, kind) in linked_symbols.iter() {
20192020
let symbol = file.add_symbol(object::write::Symbol {
20202021
name: sym.clone().into(),
20212022
value: 0,
@@ -2073,6 +2074,34 @@ fn add_linked_symbol_object(
20732074
}
20742075
}
20752076

2077+
if sess.target.is_like_windows {
2078+
// Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
2079+
// export symbols from a dynamic library. When building a dynamic library,
2080+
// however, we're going to want some symbols exported, so this adds a
2081+
// `.drectve` section which lists all the symbols using /EXPORT arguments.
2082+
//
2083+
// The linker will read these arguments from the `.drectve` section and
2084+
// export all the symbols from the dynamic library. Note that this is not
2085+
// as simple as just exporting all the symbols in the current crate (as
2086+
// specified by `codegen.reachable`) but rather we also need to possibly
2087+
// export the symbols of upstream crates. Upstream rlibs may be linked
2088+
// statically to this dynamic library, in which case they may continue to
2089+
// transitively be used and hence need their symbols exported.
2090+
let drectve = exported_symbols
2091+
.into_iter()
2092+
.map(|(sym, kind)| match (sess.target.is_like_msvc, *kind == SymbolExportKind::Text) {
2093+
(true, true) => format!(" /EXPORT:\"{sym}\""),
2094+
(true, false) => format!(" /EXPORT:\"{sym}\",DATA"),
2095+
(false, true) => format!(" -export:\"{sym}\""),
2096+
(false, false) => format!(" -export:\"{sym}\",data"),
2097+
})
2098+
.collect::<Vec<_>>()
2099+
.join("");
2100+
2101+
let section = file.add_section(vec![], b".drectve".to_vec(), object::SectionKind::Linker);
2102+
file.append_section_data(section, drectve.as_bytes(), 1);
2103+
}
2104+
20762105
let path = tmpdir.join("symbols.o");
20772106
let result = std::fs::write(&path, file.write().unwrap());
20782107
if let Err(error) = result {
@@ -2248,6 +2277,7 @@ fn linker_with_args(
22482277
sess,
22492278
tmpdir,
22502279
&codegen_results.crate_info.linked_symbols[&crate_type],
2280+
&codegen_results.crate_info.exported_symbols[&crate_type],
22512281
);
22522282

22532283
// Sanitizer libraries.

compiler/rustc_codegen_ssa/src/back/linker.rs

Lines changed: 85 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,12 @@ pub(crate) trait Linker {
337337
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);
338338
fn no_crt_objects(&mut self);
339339
fn no_default_libraries(&mut self);
340-
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
340+
fn export_symbols(
341+
&mut self,
342+
tmpdir: &Path,
343+
crate_type: CrateType,
344+
symbols: &[(String, SymbolExportKind)],
345+
);
341346
fn subsystem(&mut self, subsystem: &str);
342347
fn linker_plugin_lto(&mut self);
343348
fn add_eh_frame_header(&mut self) {}
@@ -770,7 +775,12 @@ impl<'a> Linker for GccLinker<'a> {
770775
}
771776
}
772777

773-
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
778+
fn export_symbols(
779+
&mut self,
780+
tmpdir: &Path,
781+
crate_type: CrateType,
782+
symbols: &[(String, SymbolExportKind)],
783+
) {
774784
// Symbol visibility in object files typically takes care of this.
775785
if crate_type == CrateType::Executable {
776786
let should_export_executable_symbols =
@@ -799,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> {
799809
// Write a plain, newline-separated list of symbols
800810
let res: io::Result<()> = try {
801811
let mut f = File::create_buffered(&path)?;
802-
for sym in symbols {
812+
for (sym, _) in symbols {
803813
debug!(" _{sym}");
804814
writeln!(f, "_{sym}")?;
805815
}
@@ -808,30 +818,15 @@ impl<'a> Linker for GccLinker<'a> {
808818
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
809819
}
810820
} else if is_windows {
811-
let res: io::Result<()> = try {
812-
let mut f = File::create_buffered(&path)?;
813-
814-
// .def file similar to MSVC one but without LIBRARY section
815-
// because LD doesn't like when it's empty
816-
writeln!(f, "EXPORTS")?;
817-
for symbol in symbols {
818-
debug!(" _{symbol}");
819-
// Quote the name in case it's reserved by linker in some way
820-
// (this accounts for names with dots in particular).
821-
writeln!(f, " \"{symbol}\"")?;
822-
}
823-
};
824-
if let Err(error) = res {
825-
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
826-
}
821+
// We already add -export arguments to the .drectve section of symbols.o
827822
} else {
828823
// Write an LD version script
829824
let res: io::Result<()> = try {
830825
let mut f = File::create_buffered(&path)?;
831826
writeln!(f, "{{")?;
832827
if !symbols.is_empty() {
833828
writeln!(f, " global:")?;
834-
for sym in symbols {
829+
for (sym, _) in symbols {
835830
debug!(" {sym};");
836831
writeln!(f, " {sym};")?;
837832
}
@@ -1086,47 +1081,13 @@ impl<'a> Linker for MsvcLinker<'a> {
10861081
}
10871082
}
10881083

1089-
// Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
1090-
// export symbols from a dynamic library. When building a dynamic library,
1091-
// however, we're going to want some symbols exported, so this function
1092-
// generates a DEF file which lists all the symbols.
1093-
//
1094-
// The linker will read this `*.def` file and export all the symbols from
1095-
// the dynamic library. Note that this is not as simple as just exporting
1096-
// all the symbols in the current crate (as specified by `codegen.reachable`)
1097-
// but rather we also need to possibly export the symbols of upstream
1098-
// crates. Upstream rlibs may be linked statically to this dynamic library,
1099-
// in which case they may continue to transitively be used and hence need
1100-
// their symbols exported.
1101-
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
1102-
// Symbol visibility takes care of this typically
1103-
if crate_type == CrateType::Executable {
1104-
let should_export_executable_symbols =
1105-
self.sess.opts.unstable_opts.export_executable_symbols;
1106-
if !should_export_executable_symbols {
1107-
return;
1108-
}
1109-
}
1110-
1111-
let path = tmpdir.join("lib.def");
1112-
let res: io::Result<()> = try {
1113-
let mut f = File::create_buffered(&path)?;
1114-
1115-
// Start off with the standard module name header and then go
1116-
// straight to exports.
1117-
writeln!(f, "LIBRARY")?;
1118-
writeln!(f, "EXPORTS")?;
1119-
for symbol in symbols {
1120-
debug!(" _{symbol}");
1121-
writeln!(f, " {symbol}")?;
1122-
}
1123-
};
1124-
if let Err(error) = res {
1125-
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
1126-
}
1127-
let mut arg = OsString::from("/DEF:");
1128-
arg.push(path);
1129-
self.link_arg(&arg);
1084+
fn export_symbols(
1085+
&mut self,
1086+
_tmpdir: &Path,
1087+
_crate_type: CrateType,
1088+
_symbols: &[(String, SymbolExportKind)],
1089+
) {
1090+
// We already add /EXPORT arguments to the .drectve section of symbols.o
11301091
}
11311092

11321093
fn subsystem(&mut self, subsystem: &str) {
@@ -1259,14 +1220,19 @@ impl<'a> Linker for EmLinker<'a> {
12591220
self.cc_arg("-nodefaultlibs");
12601221
}
12611222

1262-
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
1223+
fn export_symbols(
1224+
&mut self,
1225+
_tmpdir: &Path,
1226+
_crate_type: CrateType,
1227+
symbols: &[(String, SymbolExportKind)],
1228+
) {
12631229
debug!("EXPORTED SYMBOLS:");
12641230

12651231
self.cc_arg("-s");
12661232

12671233
let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
12681234
let encoded = serde_json::to_string(
1269-
&symbols.iter().map(|sym| "_".to_owned() + sym).collect::<Vec<_>>(),
1235+
&symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::<Vec<_>>(),
12701236
)
12711237
.unwrap();
12721238
debug!("{encoded}");
@@ -1428,8 +1394,13 @@ impl<'a> Linker for WasmLd<'a> {
14281394

14291395
fn no_default_libraries(&mut self) {}
14301396

1431-
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
1432-
for sym in symbols {
1397+
fn export_symbols(
1398+
&mut self,
1399+
_tmpdir: &Path,
1400+
_crate_type: CrateType,
1401+
symbols: &[(String, SymbolExportKind)],
1402+
) {
1403+
for (sym, _) in symbols {
14331404
self.link_args(&["--export", sym]);
14341405
}
14351406

@@ -1563,7 +1534,7 @@ impl<'a> Linker for L4Bender<'a> {
15631534
self.cc_arg("-nostdlib");
15641535
}
15651536

1566-
fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
1537+
fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) {
15671538
// ToDo, not implemented, copy from GCC
15681539
self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented);
15691540
}
@@ -1720,12 +1691,17 @@ impl<'a> Linker for AixLinker<'a> {
17201691

17211692
fn no_default_libraries(&mut self) {}
17221693

1723-
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
1694+
fn export_symbols(
1695+
&mut self,
1696+
tmpdir: &Path,
1697+
_crate_type: CrateType,
1698+
symbols: &[(String, SymbolExportKind)],
1699+
) {
17241700
let path = tmpdir.join("list.exp");
17251701
let res: io::Result<()> = try {
17261702
let mut f = File::create_buffered(&path)?;
17271703
// FIXME: use llvm-nm to generate export list.
1728-
for symbol in symbols {
1704+
for (symbol, _) in symbols {
17291705
debug!(" _{symbol}");
17301706
writeln!(f, " {symbol}")?;
17311707
}
@@ -1769,9 +1745,15 @@ fn for_each_exported_symbols_include_dep<'tcx>(
17691745
}
17701746
}
17711747

1772-
pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
1748+
pub(crate) fn exported_symbols(
1749+
tcx: TyCtxt<'_>,
1750+
crate_type: CrateType,
1751+
) -> Vec<(String, SymbolExportKind)> {
17731752
if let Some(ref exports) = tcx.sess.target.override_export_symbols {
1774-
return exports.iter().map(ToString::to_string).collect();
1753+
return exports
1754+
.iter()
1755+
.map(|sym| (sym.to_string(), SymbolExportKind::Text /* FIXME */))
1756+
.collect();
17751757
}
17761758

17771759
if let CrateType::ProcMacro = crate_type {
@@ -1781,16 +1763,20 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
17811763
}
17821764
}
17831765

1784-
fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
1766+
fn exported_symbols_for_non_proc_macro(
1767+
tcx: TyCtxt<'_>,
1768+
crate_type: CrateType,
1769+
) -> Vec<(String, SymbolExportKind)> {
17851770
let mut symbols = Vec::new();
17861771
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
17871772
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
17881773
// Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins
17891774
// from any cdylib. The latter doesn't work anyway as we use hidden visibility for
17901775
// compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.
17911776
if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
1792-
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
1793-
tcx, symbol, cnum,
1777+
symbols.push((
1778+
symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
1779+
info.kind,
17941780
));
17951781
symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);
17961782
}
@@ -1799,7 +1785,7 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
17991785
symbols
18001786
}
18011787

1802-
fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
1788+
fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> {
18031789
// `exported_symbols` will be empty when !should_codegen.
18041790
if !tcx.sess.opts.output_types.should_codegen() {
18051791
return Vec::new();
@@ -1809,7 +1795,10 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
18091795
let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
18101796
let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
18111797

1812-
vec![proc_macro_decls_name, metadata_symbol_name]
1798+
vec![
1799+
(proc_macro_decls_name, SymbolExportKind::Data),
1800+
(metadata_symbol_name, SymbolExportKind::Data),
1801+
]
18131802
}
18141803

18151804
pub(crate) fn linked_symbols(
@@ -1906,7 +1895,13 @@ impl<'a> Linker for PtxLinker<'a> {
19061895

19071896
fn ehcont_guard(&mut self) {}
19081897

1909-
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {}
1898+
fn export_symbols(
1899+
&mut self,
1900+
_tmpdir: &Path,
1901+
_crate_type: CrateType,
1902+
_symbols: &[(String, SymbolExportKind)],
1903+
) {
1904+
}
19101905

19111906
fn subsystem(&mut self, _subsystem: &str) {}
19121907

@@ -1975,10 +1970,15 @@ impl<'a> Linker for LlbcLinker<'a> {
19751970

19761971
fn ehcont_guard(&mut self) {}
19771972

1978-
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
1973+
fn export_symbols(
1974+
&mut self,
1975+
_tmpdir: &Path,
1976+
_crate_type: CrateType,
1977+
symbols: &[(String, SymbolExportKind)],
1978+
) {
19791979
match _crate_type {
19801980
CrateType::Cdylib => {
1981-
for sym in symbols {
1981+
for (sym, _) in symbols {
19821982
self.link_args(&["--export-symbol", sym]);
19831983
}
19841984
}
@@ -2052,11 +2052,16 @@ impl<'a> Linker for BpfLinker<'a> {
20522052

20532053
fn ehcont_guard(&mut self) {}
20542054

2055-
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
2055+
fn export_symbols(
2056+
&mut self,
2057+
tmpdir: &Path,
2058+
_crate_type: CrateType,
2059+
symbols: &[(String, SymbolExportKind)],
2060+
) {
20562061
let path = tmpdir.join("symbols");
20572062
let res: io::Result<()> = try {
20582063
let mut f = File::create_buffered(&path)?;
2059-
for sym in symbols {
2064+
for (sym, _) in symbols {
20602065
writeln!(f, "{sym}")?;
20612066
}
20622067
};

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>(
753753
/// Add it to the symbols list for all kernel functions, so that it is exported in the linked
754754
/// object.
755755
pub(crate) fn extend_exported_symbols<'tcx>(
756-
symbols: &mut Vec<String>,
756+
symbols: &mut Vec<(String, SymbolExportKind)>,
757757
tcx: TyCtxt<'tcx>,
758758
symbol: ExportedSymbol<'tcx>,
759759
instantiating_crate: CrateNum,
@@ -767,7 +767,7 @@ pub(crate) fn extend_exported_symbols<'tcx>(
767767
let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
768768

769769
// Add the symbol for the kernel descriptor (with .kd suffix)
770-
symbols.push(format!("{undecorated}.kd"));
770+
symbols.push((format!("{undecorated}.kd"), SymbolExportKind::Data));
771771
}
772772

773773
fn maybe_emutls_symbol_name<'tcx>(

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ pub struct CrateInfo {
218218
pub target_cpu: String,
219219
pub target_features: Vec<String>,
220220
pub crate_types: Vec<CrateType>,
221-
pub exported_symbols: UnordMap<CrateType, Vec<String>>,
221+
pub exported_symbols: UnordMap<CrateType, Vec<(String, SymbolExportKind)>>,
222222
pub linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>>,
223223
pub local_crate_name: Symbol,
224224
pub compiler_builtins: Option<CrateNum>,

0 commit comments

Comments
 (0)