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.

Copy link
Contributor Author

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.

My understanding is that if I don't add the symbols __rustc_proc_macro_decls_* and rust_metadata_downstream_cdylib_* to the local list, then they should be global (which is what you call the allowlist)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another problem is that I tried printing out the symbols I added to the local list, but I didn't find the leaked Rust ABI symbols from my test symbol table, even for the symbol _ZN3std3sys9backtrace26__rust_end_short_backtrace17h7499229d1271bddcE. I found one symbol in the local symbol list that was identical except for the last hash value: _ZN3std3sys9backtrace26__rust_end_short_backtrace17h125dd998f2b2d6c4E. I don't understand why this is happening.

Copy link
Member

Choose a reason for hiding this comment

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

My understanding is that if I don't add the symbols rustc_proc_macro_decls* and rust_metadata_downstream_cdylib* to the local list, then they should be global (which is what you call the allowlist)?

A proc-macro should only export those symbols, even with cstyle-export-rules enabled. So for a proc macro you should generate roughly { global: __rustc_proc_macro_decls_*; rust_metadata_downstream_cdylib_*; local: * } just like before this PR. A proc-macro also doesn't export #[no_mangle] symbols either.

Another problem is that I tried printing out the symbols I added to the local list, but I didn't find the leaked Rust ABI symbols from my test symbol table

No idea what the issue is there.

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