Skip to content

Rollup of 5 pull requests #96147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed
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
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_errors::{FatalError, Handler};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::middle::exported_symbols::SymbolExportLevel;
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_session::config::{self, CrateType, Lto};
use tracing::{debug, info};
Expand Down Expand Up @@ -55,8 +55,8 @@ fn prepare_lto(
Lto::No => panic!("didn't request LTO but we're doing LTO"),
};

let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| {
if level.is_below_threshold(export_threshold) {
let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
if info.level.is_below_threshold(export_threshold) {
Some(CString::new(name.as_str()).unwrap())
} else {
None
Expand Down
69 changes: 69 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_errors::{ErrorGuaranteed, Handler};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
use rustc_hir::def_id::CrateNum;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
use rustc_session::cstore::DllImport;
Expand Down Expand Up @@ -1655,6 +1656,67 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
}
}

/// Add a synthetic object file that contains reference to all symbols that we want to expose to
/// the linker.
///
/// Background: we implement rlibs as static library (archives). Linkers treat archives
/// differently from object files: all object files participate in linking, while archives will
/// only participate in linking if they can satisfy at least one undefined reference (version
/// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the
/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
/// can't keep them either. This causes #47384.
///
/// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
/// participate in linking like object files, but this proves to be expensive (#93791). Therefore
/// we instead just introduce an undefined reference to them. This could be done by `-u` command
/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
/// from removing them, and this is especially problematic for embedded programming where every
/// byte counts.
///
/// This method creates a synthetic object file, which contains undefined references to all symbols
/// that are necessary for the linking. They are only present in symbol table but not actually
/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections.
fn add_linked_symbol_object(
cmd: &mut dyn Linker,
sess: &Session,
tmpdir: &Path,
symbols: &[(String, SymbolExportKind)],
) {
if symbols.is_empty() {
return;
}

let Some(mut file) = super::metadata::create_object_file(sess) else {
return;
};

for (sym, kind) in symbols.iter() {
file.add_symbol(object::write::Symbol {
name: sym.clone().into(),
value: 0,
size: 0,
kind: match kind {
SymbolExportKind::Text => object::SymbolKind::Text,
SymbolExportKind::Data => object::SymbolKind::Data,
SymbolExportKind::Tls => object::SymbolKind::Tls,
},
scope: object::SymbolScope::Unknown,
weak: false,
section: object::write::SymbolSection::Undefined,
flags: object::SymbolFlags::None,
});
}

let path = tmpdir.join("symbols.o");
let result = std::fs::write(&path, file.write().unwrap());
if let Err(e) = result {
sess.fatal(&format!("failed to write {}: {}", path.display(), e));
}
cmd.add_object(&path);
}

/// Add object files containing code from the current crate.
fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
Expand Down Expand Up @@ -1795,6 +1857,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
// Pre-link CRT objects.
add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);

add_linked_symbol_object(
cmd,
sess,
tmpdir,
&codegen_results.crate_info.linked_symbols[&crate_type],
);

// Sanitizer libraries.
add_sanitizer_libraries(sess, crate_type, cmd);

Expand Down
72 changes: 50 additions & 22 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::{env, mem, str};

use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
use rustc_middle::ty::TyCtxt;
use rustc_serialize::{json, Encoder};
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
Expand Down Expand Up @@ -1518,6 +1519,29 @@ impl<'a> L4Bender<'a> {
}
}

fn for_each_exported_symbols_include_dep<'tcx>(
tcx: TyCtxt<'tcx>,
crate_type: CrateType,
mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum),
) {
for &(symbol, info) in tcx.exported_symbols(LOCAL_CRATE).iter() {
callback(symbol, info, LOCAL_CRATE);
}

let formats = tcx.dependency_formats(());
let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();

for (index, dep_format) in deps.iter().enumerate() {
let cnum = CrateNum::new(index + 1);
// For each dependency that we are linking to statically ...
if *dep_format == Linkage::Static {
for &(symbol, info) in tcx.exported_symbols(cnum).iter() {
callback(symbol, info, cnum);
}
}
}
}

pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
if let Some(ref exports) = tcx.sess.target.override_export_symbols {
return exports.iter().map(ToString::to_string).collect();
Expand All @@ -1526,34 +1550,38 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
let mut symbols = Vec::new();

let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() {
if level.is_below_threshold(export_threshold) {
symbols.push(symbol_export::symbol_name_for_instance_in_crate(
tcx,
symbol,
LOCAL_CRATE,
));
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) {
symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
}
}
});

let formats = tcx.dependency_formats(());
let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();

for (index, dep_format) in deps.iter().enumerate() {
let cnum = CrateNum::new(index + 1);
// For each dependency that we are linking to statically ...
if *dep_format == Linkage::Static {
// ... we add its symbol list to our export list.
for &(symbol, level) in tcx.exported_symbols(cnum).iter() {
if !level.is_below_threshold(export_threshold) {
continue;
}
symbols
}

symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
}
pub(crate) fn linked_symbols(
tcx: TyCtxt<'_>,
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
match crate_type {
CrateType::Executable | CrateType::Cdylib => (),
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib | CrateType::Dylib => {
return Vec::new();
}
}

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| {
if info.level.is_below_threshold(export_threshold) || info.used {
symbols.push((
symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum),
info.kind,
));
}
});

symbols
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn search_for_metadata<'a>(
.map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
}

fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
let endianness = match sess.target.options.endian {
Endian::Little => Endianness::Little,
Endian::Big => Endianness::Big,
Expand Down
Loading