Skip to content

Commit 022cd10

Browse files
Rollup merge of rust-lang#156950 - cezarbbb:staticlib-rename-internal-symbols, r=petrochenkov
Staticlib rename internal symbols Follow-up to rust-lang#155338. `-Zstaticlib-rename-internal-symbols` appends a crate-specific suffix (`_rs{StableCrateId}`) to non-exported symbols, resolving duplicate symbol conflicts when linking multiple Rust staticlibs into the same binary. The implementation collects all defined `GLOBAL/WEAK` symbol names not in the exported set across all .o files, then renames them by extending the strtab and patching symbol name offsets. When combined with `-Zstaticlib-hide-internal-symbols`, the renamed symbols also receive `STV_HIDDEN` visibility. Supported on ELF targets (Linux, BSD, etc.) and Apple targets (macOS, iOS, etc.). On unsupported targets (Windows), a warning is emitted and the flag has no effect. r?@bjorn3 @petrochenkov
2 parents bdc5805 + 796bc58 commit 022cd10

17 files changed

Lines changed: 904 additions & 92 deletions

File tree

compiler/rustc_codegen_ssa/src/back/archive.rs

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::env;
23
use std::error::Error;
34
use std::ffi::OsString;
@@ -22,7 +23,7 @@ use tracing::trace;
2223

2324
use super::metadata::{create_compressed_metadata_file, search_for_section};
2425
use super::rmeta_link;
25-
use super::symbol_edit::apply_hide;
26+
use super::symbol_edit::{apply_edits, collect_internal_names};
2627
use crate::common;
2728
// Public for ArchiveBuilderBuilder::extract_bundled_libs
2829
pub use crate::errors::ExtractBundledLibsError;
@@ -314,12 +315,18 @@ pub enum AddArchiveKind<'a> {
314315
Other,
315316
}
316317

318+
pub struct ArchiveSymbols {
319+
pub exported: FxHashSet<String>,
320+
pub rename_suffix: Option<String>,
321+
pub hide: bool,
322+
}
323+
317324
pub trait ArchiveBuilder {
318325
fn add_file(&mut self, path: &Path, kind: ArchiveEntryKind);
319326

320327
fn add_archive(&mut self, archive: &Path, kind: AddArchiveKind<'_>) -> io::Result<()>;
321328

322-
fn build(self: Box<Self>, output: &Path, exported_symbols: Option<FxHashSet<String>>) -> bool;
329+
fn build(self: Box<Self>, output: &Path, symbols: Option<ArchiveSymbols>) -> bool;
323330
}
324331

325332
fn target_archive_format_to_object_kind(format: &str) -> Option<ObjectArchiveKind> {
@@ -534,9 +541,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
534541

535542
/// Combine the provided files, rlibs, and native libraries into a single
536543
/// `Archive`.
537-
fn build(self: Box<Self>, output: &Path, exported_symbols: Option<FxHashSet<String>>) -> bool {
544+
fn build(self: Box<Self>, output: &Path, symbols: Option<ArchiveSymbols>) -> bool {
538545
let sess = self.sess;
539-
match self.build_inner(output, exported_symbols) {
546+
match self.build_inner(output, symbols) {
540547
Ok(any_members) => any_members,
541548
Err(error) => {
542549
sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
@@ -546,11 +553,7 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
546553
}
547554

548555
impl<'a> ArArchiveBuilder<'a> {
549-
fn build_inner(
550-
self,
551-
output: &Path,
552-
exported_symbols: Option<FxHashSet<String>>,
553-
) -> io::Result<bool> {
556+
fn build_inner(self, output: &Path, symbols: Option<ArchiveSymbols>) -> io::Result<bool> {
554557
let archive_kind = match &*self.sess.target.archive_format {
555558
"gnu" => ArchiveKind::Gnu,
556559
"bsd" => ArchiveKind::Bsd,
@@ -562,6 +565,39 @@ impl<'a> ArArchiveBuilder<'a> {
562565
}
563566
};
564567

568+
// Collect all internally-defined symbol names across every Rust object file.
569+
// This set is needed because rename must also apply to *undefined* references
570+
// (cross-object calls within the staticlib), but we cannot use `!exported.contains(name)`
571+
// alone — that would also match external C symbols like `malloc` which must not be renamed.
572+
let rename = if let Some(sym) = &symbols
573+
&& let Some(rename_suffix) = sym.rename_suffix.as_deref()
574+
{
575+
let mut names = FxHashSet::default();
576+
for (_, entry) in &self.entries {
577+
if entry.kind != ArchiveEntryKind::RustObj {
578+
continue;
579+
}
580+
match &entry.source {
581+
ArchiveEntrySource::Archive { archive_index, file_range } => {
582+
let src_archive = &self.src_archives[*archive_index];
583+
let start = file_range.0 as usize;
584+
let end = start + file_range.1 as usize;
585+
if let Some(data) = src_archive.1.get(start..end) {
586+
collect_internal_names(data, &sym.exported, &mut names);
587+
}
588+
}
589+
ArchiveEntrySource::File(file) => {
590+
if let Ok(data) = fs::read(file) {
591+
collect_internal_names(&data, &sym.exported, &mut names);
592+
}
593+
}
594+
}
595+
}
596+
Some((names, rename_suffix))
597+
} else {
598+
None
599+
};
600+
565601
let mut entries = Vec::new();
566602

567603
for (entry_name, entry) in self.entries {
@@ -588,9 +624,9 @@ impl<'a> ArArchiveBuilder<'a> {
588624
};
589625

590626
if entry.kind == ArchiveEntryKind::RustObj
591-
&& let Some(exported) = &exported_symbols
627+
&& let Some(sym) = &symbols
592628
{
593-
Box::new(apply_hide(data, exported))
629+
Box::new(apply_edits(data, &sym.exported, sym.hide, rename.as_ref()))
594630
} else {
595631
Box::new(data)
596632
}
@@ -602,9 +638,13 @@ impl<'a> ArArchiveBuilder<'a> {
602638
)
603639
.map_err(|err| io_error_context("failed to map object file", err))?;
604640
if entry.kind == ArchiveEntryKind::RustObj
605-
&& let Some(exported) = &exported_symbols
641+
&& let Some(sym) = &symbols
606642
{
607-
Box::new(apply_hide(&mmap, exported))
643+
let edited = apply_edits(&mmap, &sym.exported, sym.hide, rename.as_ref());
644+
match edited {
645+
Cow::Borrowed(_) => Box::new(mmap) as Box<dyn AsRef<[u8]>>,
646+
Cow::Owned(v) => Box::new(v),
647+
}
608648
} else {
609649
Box::new(mmap) as Box<dyn AsRef<[u8]>>
610650
}

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ use rustc_target::spec::{
5353
};
5454
use tracing::{debug, info, warn};
5555

56-
use super::archive::{AddArchiveKind, ArchiveBuilder, ArchiveBuilderBuilder, ArchiveEntryKind};
56+
use super::archive::{
57+
AddArchiveKind, ArchiveBuilder, ArchiveBuilderBuilder, ArchiveEntryKind, ArchiveSymbols,
58+
};
5759
use super::command::Command;
5860
use super::linker::{self, Linker};
5961
use super::metadata::{MetadataPosition, create_wrapper_file};
@@ -566,11 +568,21 @@ fn link_staticlib(
566568
sess.dcx().emit_fatal(e);
567569
}
568570

569-
let exported_symbols = if sess.opts.unstable_opts.staticlib_hide_internal_symbols {
571+
let hide = sess.opts.unstable_opts.staticlib_hide_internal_symbols;
572+
let rename = sess.opts.unstable_opts.staticlib_rename_internal_symbols;
573+
574+
let exported_symbols = if hide || rename {
570575
if !matches!(sess.target.binary_format, BinaryFormat::Elf | BinaryFormat::MachO) {
571-
sess.dcx().emit_warn(errors::StaticlibHideInternalSymbolsUnsupported {
572-
binary_format: sess.target.archive_format.to_string(),
573-
});
576+
if hide {
577+
sess.dcx().emit_warn(errors::StaticlibHideInternalSymbolsUnsupported {
578+
binary_format: sess.target.archive_format.to_string(),
579+
});
580+
}
581+
if rename {
582+
sess.dcx().emit_warn(errors::StaticlibRenameInternalSymbolsUnsupported {
583+
binary_format: sess.target.archive_format.to_string(),
584+
});
585+
}
574586
None
575587
} else {
576588
crate_info
@@ -581,7 +593,14 @@ fn link_staticlib(
581593
} else {
582594
None
583595
};
584-
ab.build(out_filename, exported_symbols);
596+
597+
let symbols = exported_symbols.map(|exported| ArchiveSymbols {
598+
exported,
599+
rename_suffix: rename.then(|| crate_info.symbol_rename_suffix.clone()),
600+
hide,
601+
});
602+
603+
ab.build(out_filename, symbols);
585604

586605
let crates = crate_info.used_crates.iter();
587606

0 commit comments

Comments
 (0)