Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 55 additions & 7 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,15 +846,31 @@ impl<'a> Linker for GccLinker<'a> {
// Write an LD version script
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
writeln!(f, "{{")?;
if !symbols.is_empty() {
writeln!(f, " global:")?;
for (sym, _) in symbols {
debug!(" {sym};");
writeln!(f, " {sym};")?;
if self.sess.opts.unstable_opts.cstyle_export_rules {
writeln!(f, "{{")?;
writeln!(f, " global:\n *;\n")?;
if !symbols.is_empty() {
writeln!(f, " local:")?;
// writeln!(f, " _ZN*;")?;
// writeln!(f, " _R*;")?;
for (sym, _) in symbols {
debug!(" {sym};");
writeln!(f, " {sym};")?;
}
}
writeln!(f, "\n}};")?;
} else {
writeln!(f, "{{")?;
if !symbols.is_empty() {
writeln!(f, " global:")?;
for (sym, _) in symbols {
debug!(" {sym};");
writeln!(f, " {sym};")?;
}
}
writeln!(f, "\n local:\n *;\n}};")?;
}
writeln!(f, "\n local:\n *;\n}};")?;

};
if let Err(error) = res {
self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error });
Expand Down Expand Up @@ -1812,6 +1828,38 @@ pub(crate) fn exported_symbols(
}
}

pub(crate) fn exported_symbols_cstyle(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For proc-macros we should keep using an allowlist containing just two symbols.

Copy link
Contributor Author

@cezarbbb cezarbbb Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same situation occurs when adding proc_macro, as can be seen in the error message above(__rustc_proc_macro_decls_908feb11bfb2de80__ and rust_metadata_downstream_cdylib_908feb11bfb2de80).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These symbols must be exported from proc-macros. If they aren't, rustc will fail to load the proc-macro. In any case it looks like you put those symbols on the denylist rather than the allowlist.

tcx: TyCtxt<'_>,
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
if let Some(ref exports) = tcx.sess.target.override_export_symbols {
return exports
.iter()
.map(|name| {
(
name.to_string(),
// FIXME use the correct export kind for this symbol. override_export_symbols
// can't directly specify the SymbolExportKind as it is defined in rustc_middle
// which rustc_target can't depend on.
SymbolExportKind::Text,
)
})
.collect();
}

let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lists all exported symbols, while you need all non-exported symbols too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the correct position to iterate through all exported symbols and invert the selection rules of the original exported_symbols?

if !info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|| is_compiler_builtins

Copy link
Contributor Author

@cezarbbb cezarbbb Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that the original criterion for selecting symbols into exported_symbols was info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum), but here we cannot perform a negation operation, i.e., info.level.is_below_threshold(export_threshold) || tcx.is_compiler_builtins(cnum), as this will result in the following error:

exit status: 1
  |
  = note:  "cc" "-Wl,--version-script=/tmp/rustcenvqxB/list" "-Wl,--no-undefined-version" "-m64" "/tmp/rustcenvqxB/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/root/cezarbbb/include-libs-test3/downstream-cdylib/target/debug/deps/libupstream-8102da413a3d22f9.rlib" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,libcfg_if-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/tmp/rustcenvqxB/raw-dylibs" "-B<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/root/cezarbbb/include-libs-test3/downstream-cdylib/target/debug/deps/libdownstream_cdylib.so" "-Wl,--gc-sections" "-shared" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: rust-lld: error: version script assignment of 'local' to symbol '__rustc_proc_macro_decls_908feb11bfb2de80__' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol 'rust_metadata_downstream_cdylib_908feb11bfb2de80' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__addtf3' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__ashlti3' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__ashrti3' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__bswapdi2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__bswapsi2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__clzdi2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__clzti2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__ctzdi2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__ctzti2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__divmodti4' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__divtf3' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__divti3' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__eqtf2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__extenddftf2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__extendhfdf2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__extendhftf2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__extendsfdf2' failed: symbol not defined
          rust-lld: error: version script assignment of 'local' to symbol '__extendsftf2' failed: symbol not defined
          rust-lld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors) collect2: error: ld returned 1 exit status

It seems linker is not selecting some of certain compilerbuiltin functions, resulting in undefined symbols?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. All compiler-builtins symbols have hidden visibility, so I guess they shouldn't be listed in the version script at all.

symbols.push((
symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
info.kind,
));
}
});
symbols
}

fn exported_symbols_for_non_proc_macro(
tcx: TyCtxt<'_>,
crate_type: CrateType,
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,11 @@ impl CrateInfo {
let crate_types = tcx.crate_types().to_vec();
let exported_symbols = crate_types
.iter()
.map(|&c| (c, crate::back::linker::exported_symbols(tcx, c)))
.map(|&c| (c, if tcx.sess.opts.unstable_opts.cstyle_export_rules {
crate::back::linker::exported_symbols_cstyle(tcx, c)
} else {
crate::back::linker::exported_symbols(tcx, c)
}))
.collect();
let linked_symbols =
crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ fn test_unstable_options_tracking_hash() {
);
tracked!(crate_attr, vec!["abc".to_string()]);
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
tracked!(cstyle_export_rules, true);
tracked!(debug_info_for_profiling, true);
tracked!(debug_info_type_line_numbers, true);
tracked!(default_visibility, Some(rustc_target::spec::SymbolVisibility::Hidden));
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2176,6 +2176,8 @@ options! {
"inject the given attribute in the crate"),
cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED],
"threshold to allow cross crate inlining of functions"),
cstyle_export_rules: bool = (false, parse_bool, [TRACKED],
"global symbols from upstream static libraries will be exported by default"),
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
"emit discriminators and other data necessary for AutoFDO"),
debug_info_type_line_numbers: bool = (false, parse_bool, [TRACKED],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `cstyle-export-rules`

This option forces rustc to export the `global` symbol from the upstream C static library.
Loading