Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 5f77dba

Browse files
committed
Link object files that use #[used]
1 parent 9482bda commit 5f77dba

File tree

5 files changed

+163
-29
lines changed

5 files changed

+163
-29
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{env, fmt, fs, io, mem, str};
99

1010
use cc::windows_registry;
1111
use itertools::Itertools;
12+
use object::read::archive::{ArchiveFile, ArchiveOffset};
1213
use regex::Regex;
1314
use rustc_arena::TypedArena;
1415
use rustc_ast::CRATE_NODE_ID;
@@ -78,6 +79,7 @@ pub fn link_binary(
7879
let _timer = sess.timer("link_binary");
7980
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
8081
let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
82+
let mut tempfiles_for_linked_objects: Vec<PathBuf> = Vec::new();
8183
for &crate_type in &codegen_results.crate_info.crate_types {
8284
// Ignore executable crates if we have -Z no-codegen, as they will error.
8385
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
@@ -142,6 +144,8 @@ pub fn link_binary(
142144
&out_filename,
143145
&codegen_results,
144146
path.as_ref(),
147+
&mut tempfiles_for_linked_objects,
148+
outputs,
145149
);
146150
}
147151
}
@@ -214,6 +218,10 @@ pub fn link_binary(
214218
ensure_removed(sess.dcx(), &temp);
215219
}
216220

221+
for temp in tempfiles_for_linked_objects {
222+
ensure_removed(sess.dcx(), &temp);
223+
}
224+
217225
// If no requested outputs require linking, then the object temporaries should
218226
// be kept.
219227
if !sess.opts.output_types.should_link() {
@@ -771,6 +779,8 @@ fn link_natively(
771779
out_filename: &Path,
772780
codegen_results: &CodegenResults,
773781
tmpdir: &Path,
782+
tempfiles_for_linked_objects: &mut Vec<PathBuf>,
783+
outputs: &OutputFilenames,
774784
) {
775785
info!("preparing {:?} to {:?}", crate_type, out_filename);
776786
let (linker_path, flavor) = linker_and_flavor(sess);
@@ -795,6 +805,8 @@ fn link_natively(
795805
temp_filename,
796806
codegen_results,
797807
self_contained_components,
808+
tempfiles_for_linked_objects,
809+
outputs,
798810
);
799811

800812
linker::disable_localization(&mut cmd);
@@ -2254,6 +2266,8 @@ fn linker_with_args(
22542266
out_filename: &Path,
22552267
codegen_results: &CodegenResults,
22562268
self_contained_components: LinkSelfContainedComponents,
2269+
tempfiles_for_linked_objects: &mut Vec<PathBuf>,
2270+
outputs: &OutputFilenames,
22572271
) -> Command {
22582272
let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
22592273
let cmd = &mut *super::linker::get_linker(
@@ -2329,6 +2343,13 @@ fn linker_with_args(
23292343
add_local_crate_regular_objects(cmd, codegen_results);
23302344
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
23312345
add_local_crate_allocator_objects(cmd, codegen_results);
2346+
add_local_crate_linked_objects(
2347+
cmd,
2348+
codegen_results,
2349+
crate_type,
2350+
tempfiles_for_linked_objects,
2351+
outputs,
2352+
);
23322353

23332354
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols
23342355
// at the point at which they are specified on the command line.
@@ -2925,6 +2946,32 @@ fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf {
29252946
}
29262947
}
29272948

2949+
fn add_local_crate_linked_objects(
2950+
cmd: &mut dyn Linker,
2951+
codegen_results: &CodegenResults,
2952+
crate_type: CrateType,
2953+
tempfiles_for_linked_objects: &mut Vec<PathBuf>,
2954+
outputs: &OutputFilenames,
2955+
) {
2956+
for (cnum, offsets) in &codegen_results.crate_info.linked_objects[&crate_type] {
2957+
let src = &codegen_results.crate_info.used_crate_source[cnum];
2958+
let cratepath = &src.rlib.as_ref().unwrap().0;
2959+
let archive_map = unsafe { Mmap::map(File::open(cratepath).unwrap()).unwrap() };
2960+
let archive = ArchiveFile::parse(&*archive_map)
2961+
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
2962+
.unwrap();
2963+
for &offset in offsets {
2964+
let member = archive.member(ArchiveOffset(offset)).unwrap();
2965+
let name = std::str::from_utf8(member.name()).unwrap();
2966+
let data = member.data(&*archive_map).unwrap();
2967+
let obj = outputs.temp_path(OutputType::Object, Some(&format!("{name}.linked_object")));
2968+
fs::write(&obj, data).unwrap();
2969+
cmd.add_object(&obj);
2970+
tempfiles_for_linked_objects.push(obj);
2971+
}
2972+
}
2973+
}
2974+
29282975
// Adds the static "rlib" versions of all crates to the command line.
29292976
// There's a bit of magic which happens here specifically related to LTO,
29302977
// namely that we remove upstream object files.

compiler/rustc_codegen_ssa/src/back/linker.rs

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ use std::path::{Path, PathBuf};
55
use std::{env, io, iter, mem, str};
66

77
use cc::windows_registry;
8+
use object::read::archive::ArchiveFile;
9+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
10+
use rustc_data_structures::memmap::Mmap;
811
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
912
use rustc_metadata::{
1013
find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,
1114
};
1215
use rustc_middle::bug;
1316
use rustc_middle::middle::dependency_format::Linkage;
14-
use rustc_middle::middle::exported_symbols;
15-
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
17+
use rustc_middle::middle::exported_symbols::{
18+
self, ExportedSymbol, SymbolExportInfo, SymbolExportKind,
19+
};
1620
use rustc_middle::ty::TyCtxt;
1721
use rustc_session::Session;
1822
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
@@ -21,6 +25,7 @@ use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};
2125
use tracing::{debug, warn};
2226

2327
use super::command::Command;
28+
use super::link::are_upstream_rust_objects_already_included;
2429
use super::symbol_export;
2530
use crate::errors;
2631

@@ -1753,17 +1758,15 @@ impl<'a> Linker for AixLinker<'a> {
17531758
fn for_each_exported_symbols_include_dep<'tcx>(
17541759
tcx: TyCtxt<'tcx>,
17551760
crate_type: CrateType,
1756-
mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum),
1761+
mut callback: impl FnMut(&'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)], CrateNum),
17571762
) {
17581763
let formats = tcx.dependency_formats(());
17591764
let deps = &formats[&crate_type];
17601765

17611766
for (cnum, dep_format) in deps.iter_enumerated() {
17621767
// For each dependency that we are linking to statically ...
17631768
if *dep_format == Linkage::Static {
1764-
for &(symbol, info) in tcx.exported_symbols(cnum).iter() {
1765-
callback(symbol, info, cnum);
1766-
}
1769+
callback(tcx.exported_symbols(cnum), cnum);
17671770
}
17681771
}
17691772
}
@@ -1783,12 +1786,14 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
17831786
fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
17841787
let mut symbols = Vec::new();
17851788
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
1786-
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
1787-
if info.level.is_below_threshold(export_threshold) {
1788-
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
1789-
tcx, symbol, cnum,
1790-
));
1791-
symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);
1789+
for_each_exported_symbols_include_dep(tcx, crate_type, |exported_symbols, cnum| {
1790+
for &(symbol, info) in exported_symbols {
1791+
if info.level.is_below_threshold(export_threshold) {
1792+
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
1793+
tcx, symbol, cnum,
1794+
));
1795+
symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);
1796+
}
17921797
}
17931798
});
17941799

@@ -1808,30 +1813,97 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
18081813
vec![proc_macro_decls_name, metadata_symbol_name]
18091814
}
18101815

1811-
pub(crate) fn linked_symbols(
1816+
fn add_linked_objects(
1817+
archive_path: &Path,
1818+
linked_symbols: &mut FxHashSet<String>,
1819+
) -> Option<FxIndexSet<u64>> {
1820+
let archive_map = unsafe { Mmap::map(File::open(&archive_path).unwrap()).unwrap() };
1821+
let archive = ArchiveFile::parse(&*archive_map)
1822+
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
1823+
.unwrap();
1824+
let Some(archive_symbols) = archive.symbols().unwrap() else {
1825+
return None;
1826+
};
1827+
let mut offsets = FxIndexSet::default();
1828+
for symbol in archive_symbols {
1829+
let symbol = symbol.unwrap();
1830+
let name = std::str::from_utf8(symbol.name()).unwrap();
1831+
if linked_symbols.remove(name) {
1832+
offsets.insert(symbol.offset().0);
1833+
}
1834+
}
1835+
Some(offsets)
1836+
}
1837+
1838+
pub(crate) fn linked_objects(
18121839
tcx: TyCtxt<'_>,
18131840
crate_type: CrateType,
1814-
) -> Vec<(String, SymbolExportKind)> {
1841+
linked_symbols: &mut Vec<(String, SymbolExportKind)>,
1842+
) -> FxIndexMap<CrateNum, FxIndexSet<u64>> {
18151843
match crate_type {
18161844
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (),
18171845
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
1818-
return Vec::new();
1846+
return FxIndexMap::default();
18191847
}
18201848
}
18211849

1822-
let mut symbols = Vec::new();
1823-
1850+
let mut objects = FxIndexMap::default();
1851+
let upstream_rust_objects_already_included =
1852+
are_upstream_rust_objects_already_included(tcx.sess);
18241853
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
1825-
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
1826-
if info.level.is_below_threshold(export_threshold) || info.used {
1827-
symbols.push((
1828-
symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
1829-
info.kind,
1830-
));
1854+
for_each_exported_symbols_include_dep(tcx, crate_type, |exported_symbols, cnum| {
1855+
if cnum == LOCAL_CRATE {
1856+
// We don't know here if the symbols are undefined, so we add them all.
1857+
// Since the local crate is always linked directly to object files, `#[used]` works as expected.
1858+
linked_symbols.extend(
1859+
exported_symbols
1860+
.iter()
1861+
.filter(|(_, info)| {
1862+
info.level.is_below_threshold(export_threshold) || info.used
1863+
})
1864+
.map(|&(symbol, info)| {
1865+
(
1866+
symbol_export::linking_symbol_name_for_instance_in_crate(
1867+
tcx, symbol, cnum,
1868+
),
1869+
info.kind,
1870+
)
1871+
}),
1872+
);
1873+
return;
1874+
}
1875+
// TODO: let lto = upstream_rust_objects_already_included && !ignored_for_lto(tcx.sess, &codegen_results.crate_info, cnum);
1876+
let lto = upstream_rust_objects_already_included;
1877+
if lto {
1878+
return;
1879+
}
1880+
let symbols: Vec<_> = exported_symbols
1881+
.iter()
1882+
.filter(|(_, info)| info.level.is_below_threshold(export_threshold) || info.used)
1883+
.map(|&(symbol, info)| {
1884+
(
1885+
symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
1886+
info.kind,
1887+
)
1888+
})
1889+
.collect();
1890+
if symbols.is_empty() {
1891+
return;
18311892
}
1893+
let used_crate_source = tcx.used_crate_source(cnum);
1894+
let cratepath = &used_crate_source.rlib.as_ref().unwrap().0;
1895+
let mut crate_linked_symbols: FxHashSet<_> =
1896+
symbols.iter().map(|(symbol, _)| symbol.to_string()).collect();
1897+
if let Some(archive_offsets) = add_linked_objects(cratepath, &mut crate_linked_symbols) {
1898+
objects.insert(cnum, archive_offsets);
1899+
}
1900+
// Unresolved symbols may come from external libraries.
1901+
linked_symbols.extend(
1902+
symbols.into_iter().filter(|(symbol, _)| crate_linked_symbols.contains(symbol)),
1903+
);
18321904
});
18331905

1834-
symbols
1906+
objects
18351907
}
18361908

18371909
/// Much simplified and explicit CLI for the NVPTX linker. The linker operates

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::time::{Duration, Instant};
66
use itertools::Itertools;
77
use rustc_abi::FIRST_VARIANT;
88
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name};
9-
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
9+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
1010
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
1111
use rustc_data_structures::sync::par_map;
1212
use rustc_data_structures::unord::UnordMap;
@@ -872,8 +872,21 @@ impl CrateInfo {
872872
.iter()
873873
.map(|&c| (c, crate::back::linker::exported_symbols(tcx, c)))
874874
.collect();
875-
let linked_symbols =
876-
crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect();
875+
let mut linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>> =
876+
crate_types.iter().map(|&c| (c, Vec::new())).collect();
877+
let linked_objects = crate_types
878+
.iter()
879+
.map(|&c| {
880+
(
881+
c,
882+
crate::back::linker::linked_objects(
883+
tcx,
884+
c,
885+
linked_symbols.get_mut(&c).unwrap(),
886+
),
887+
)
888+
})
889+
.collect();
877890
let local_crate_name = tcx.crate_name(LOCAL_CRATE);
878891
let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
879892
let subsystem =
@@ -919,6 +932,7 @@ impl CrateInfo {
919932
crate_types,
920933
exported_symbols,
921934
linked_symbols,
935+
linked_objects,
922936
local_crate_name,
923937
compiler_builtins,
924938
profiler_runtime: None,

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::path::{Path, PathBuf};
2727
use std::sync::Arc;
2828

2929
use rustc_ast as ast;
30-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
30+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
3131
use rustc_data_structures::unord::UnordMap;
3232
use rustc_hir::CRATE_HIR_ID;
3333
use rustc_hir::def_id::CrateNum;
@@ -194,6 +194,7 @@ pub struct CrateInfo {
194194
pub crate_types: Vec<CrateType>,
195195
pub exported_symbols: UnordMap<CrateType, Vec<String>>,
196196
pub linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>>,
197+
pub linked_objects: FxIndexMap<CrateType, FxIndexMap<CrateNum, FxIndexSet<u64>>>,
197198
pub local_crate_name: Symbol,
198199
pub compiler_builtins: Option<CrateNum>,
199200
pub profiler_runtime: Option<CrateNum>,

tests/run-make/linker-warning/short-error.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
error: linking with `./fake-linker` failed: exit status: 1
22
|
3-
= note: "./fake-linker" "-m64" "/tmp/rustc/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "<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-*,liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/build-root/test/run-make/linker-warning/rmake_out" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "main" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "run_make_error"
3+
= note: "./fake-linker" "-m64" "/tmp/rustc/symbols.o" "<5 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "<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-*,liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/build-root/test/run-make/linker-warning/rmake_out" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "main" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "run_make_error"
44
= note: some arguments are omitted. use `--verbose` to show all linker arguments
55
= note: error: baz
66

0 commit comments

Comments
 (0)