@@ -7,6 +7,7 @@ use rustc_errors::{ErrorGuaranteed, Handler};
77use rustc_fs_util:: fix_windows_verbatim_for_gcc;
88use rustc_hir:: def_id:: CrateNum ;
99use rustc_middle:: middle:: dependency_format:: Linkage ;
10+ use rustc_middle:: middle:: exported_symbols:: SymbolExportKind ;
1011use rustc_session:: config:: { self , CFGuard , CrateType , DebugInfo , LdImpl , Strip } ;
1112use rustc_session:: config:: { OutputFilenames , OutputType , PrintRequest , SplitDwarfKind } ;
1213use rustc_session:: cstore:: DllImport ;
@@ -1655,6 +1656,73 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
16551656 }
16561657}
16571658
1659+ /// Add a synthetic object file that contains reference to all symbols that we want to expose to
1660+ /// the linker.
1661+ ///
1662+ /// Background: we implement rlibs as static library (archives). Linkers treat archives
1663+ /// differently from object files: all object files participate in linking, while archives will
1664+ /// only participate in linking if they can satisfy at least one undefined reference (version
1665+ /// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the
1666+ /// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
1667+ /// can't keep them either. This causes #47384.
1668+ ///
1669+ /// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
1670+ /// participate in linking like object files, but this proves to be expensive (#93791). Therefore
1671+ /// we instead just introduce an undefined reference to them. This could be done by `-u` command
1672+ /// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
1673+ /// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
1674+ /// from removing them, and this is especially problematic for embedded programming where every
1675+ /// byte counts.
1676+ ///
1677+ /// This method creates a synthetic object file, which contains undefined references to all symbols
1678+ /// that are necessary for the linking. They are only present in symbol table but not actually
1679+ /// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
1680+ /// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections.
1681+ fn add_linked_symbol_object (
1682+ cmd : & mut dyn Linker ,
1683+ sess : & Session ,
1684+ tmpdir : & Path ,
1685+ symbols : & [ ( String , SymbolExportKind ) ] ,
1686+ ) {
1687+ if symbols. is_empty ( ) {
1688+ return ;
1689+ }
1690+
1691+ let Some ( mut file) = super :: metadata:: create_object_file ( sess) else {
1692+ return ;
1693+ } ;
1694+
1695+ // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
1696+ // so add an empty section.
1697+ if file. format ( ) == object:: BinaryFormat :: Coff {
1698+ file. add_section ( Vec :: new ( ) , ".text" . into ( ) , object:: SectionKind :: Text ) ;
1699+ }
1700+
1701+ for ( sym, kind) in symbols. iter ( ) {
1702+ file. add_symbol ( object:: write:: Symbol {
1703+ name : sym. clone ( ) . into ( ) ,
1704+ value : 0 ,
1705+ size : 0 ,
1706+ kind : match kind {
1707+ SymbolExportKind :: Text => object:: SymbolKind :: Text ,
1708+ SymbolExportKind :: Data => object:: SymbolKind :: Data ,
1709+ SymbolExportKind :: Tls => object:: SymbolKind :: Tls ,
1710+ } ,
1711+ scope : object:: SymbolScope :: Unknown ,
1712+ weak : false ,
1713+ section : object:: write:: SymbolSection :: Undefined ,
1714+ flags : object:: SymbolFlags :: None ,
1715+ } ) ;
1716+ }
1717+
1718+ let path = tmpdir. join ( "symbols.o" ) ;
1719+ let result = std:: fs:: write ( & path, file. write ( ) . unwrap ( ) ) ;
1720+ if let Err ( e) = result {
1721+ sess. fatal ( & format ! ( "failed to write {}: {}" , path. display( ) , e) ) ;
1722+ }
1723+ cmd. add_object ( & path) ;
1724+ }
1725+
16581726/// Add object files containing code from the current crate.
16591727fn add_local_crate_regular_objects ( cmd : & mut dyn Linker , codegen_results : & CodegenResults ) {
16601728 for obj in codegen_results. modules . iter ( ) . filter_map ( |m| m. object . as_ref ( ) ) {
@@ -1795,6 +1863,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
17951863 // Pre-link CRT objects.
17961864 add_pre_link_objects ( cmd, sess, link_output_kind, crt_objects_fallback) ;
17971865
1866+ add_linked_symbol_object (
1867+ cmd,
1868+ sess,
1869+ tmpdir,
1870+ & codegen_results. crate_info . linked_symbols [ & crate_type] ,
1871+ ) ;
1872+
17981873 // Sanitizer libraries.
17991874 add_sanitizer_libraries ( sess, crate_type, cmd) ;
18001875
0 commit comments