@@ -11,7 +11,7 @@ use ar_archive_writer::{
1111pub use ar_archive_writer:: { DEFAULT_OBJECT_READER , ObjectReader } ;
1212use object:: read:: archive:: ArchiveFile ;
1313use object:: read:: macho:: FatArch ;
14- use rustc_data_structures:: fx:: FxIndexSet ;
14+ use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
1515use rustc_data_structures:: memmap:: Mmap ;
1616use rustc_fs_util:: TempDirBuilder ;
1717use rustc_metadata:: EncodedMetadata ;
@@ -318,6 +318,8 @@ pub trait ArchiveBuilder {
318318 ) -> io:: Result < ( ) > ;
319319
320320 fn build ( self : Box < Self > , output : & Path ) -> bool ;
321+
322+ fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) ;
321323}
322324
323325pub struct ArArchiveBuilderBuilder ;
@@ -337,6 +339,7 @@ pub struct ArArchiveBuilder<'a> {
337339 // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs
338340 // to be at the end of an archive in some cases for linkers to not get confused.
339341 entries : Vec < ( Vec < u8 > , ArchiveEntry ) > ,
342+ keep_symbols : Option < FxHashSet < String > > ,
340343}
341344
342345#[ derive( Debug ) ]
@@ -347,7 +350,17 @@ enum ArchiveEntry {
347350
348351impl < ' a > ArArchiveBuilder < ' a > {
349352 pub fn new ( sess : & ' a Session , object_reader : & ' static ObjectReader ) -> ArArchiveBuilder < ' a > {
350- ArArchiveBuilder { sess, object_reader, src_archives : vec ! [ ] , entries : vec ! [ ] }
353+ ArArchiveBuilder {
354+ sess,
355+ object_reader,
356+ src_archives : vec ! [ ] ,
357+ entries : vec ! [ ] ,
358+ keep_symbols : None ,
359+ }
360+ }
361+
362+ pub fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) {
363+ self . keep_symbols = Some ( keep) ;
351364 }
352365}
353366
@@ -460,6 +473,10 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
460473 }
461474 }
462475 }
476+
477+ fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) {
478+ self . keep_symbols = Some ( keep) ;
479+ }
463480}
464481
465482impl < ' a > ArArchiveBuilder < ' a > {
@@ -478,7 +495,7 @@ impl<'a> ArArchiveBuilder<'a> {
478495 let mut entries = Vec :: new ( ) ;
479496
480497 for ( entry_name, entry) in self . entries {
481- let data =
498+ let data: Box < dyn AsRef < [ u8 ] > > =
482499 match entry {
483500 ArchiveEntry :: FromArchive { archive_index, file_range } => {
484501 let src_archive = & self . src_archives [ archive_index] ;
@@ -498,6 +515,16 @@ impl<'a> ArArchiveBuilder<'a> {
498515 } ,
499516 } ;
500517
518+ let data: Box < dyn AsRef < [ u8 ] > > = if let Some ( ref keep) = self . keep_symbols {
519+ if let Some ( filtered) = elf_filter_global_symbols ( data. as_ref ( ) . as_ref ( ) , keep) {
520+ Box :: new ( filtered)
521+ } else {
522+ data
523+ }
524+ } else {
525+ data
526+ } ;
527+
501528 entries. push ( NewArchiveMember {
502529 buf : data,
503530 object_reader : self . object_reader ,
@@ -557,3 +584,142 @@ impl<'a> ArArchiveBuilder<'a> {
557584fn io_error_context ( context : & str , err : io:: Error ) -> io:: Error {
558585 io:: Error :: new ( io:: ErrorKind :: Other , format ! ( "{context}: {err}" ) )
559586}
587+
588+ /// For ELF object files, set `STV_HIDDEN` visibility on GLOBAL/WEAK symbols
589+ /// that are NOT in the `keep_symbols` set. Returns `Some(modified_data)` if
590+ /// any changes were made, `None` if the data is not an ELF object or no
591+ /// changes were needed.
592+ fn elf_filter_global_symbols ( data : & [ u8 ] , keep_symbols : & FxHashSet < String > ) -> Option < Vec < u8 > > {
593+ use object:: { Endianness , elf} ;
594+
595+ if data. len ( ) < 16 || & data[ 0 ..4 ] != elf:: ELFMAG {
596+ return None ;
597+ }
598+
599+ match data[ 4 ] {
600+ elf:: ELFCLASS64 => elf_filter_symbols_inner :: < elf:: FileHeader64 < Endianness > > (
601+ data,
602+ keep_symbols,
603+ /* sym_entry_size= */ 24 ,
604+ /* st_info_offset= */ 4 ,
605+ /* st_other_offset= */ 5 ,
606+ ) ,
607+ elf:: ELFCLASS32 => elf_filter_symbols_inner :: < elf:: FileHeader32 < Endianness > > (
608+ data,
609+ keep_symbols,
610+ /* sym_entry_size= */ 16 ,
611+ /* st_info_offset= */ 12 ,
612+ /* st_other_offset= */ 13 ,
613+ ) ,
614+ _ => None ,
615+ }
616+ }
617+
618+ fn elf_filter_symbols_inner < Elf : object:: read:: elf:: FileHeader < Endian = object:: Endianness > > (
619+ data : & [ u8 ] ,
620+ keep_symbols : & FxHashSet < String > ,
621+ sym_entry_size : usize ,
622+ st_info_offset : usize ,
623+ st_other_offset : usize ,
624+ ) -> Option < Vec < u8 > >
625+ where
626+ u64 : From < Elf :: Word > ,
627+ {
628+ use object:: read:: elf:: SectionHeader ;
629+ use object:: { Endianness , elf} ;
630+
631+ let endian = match Elf :: parse ( data) {
632+ Ok ( h) => match h. endian ( ) {
633+ Ok ( e) => e,
634+ Err ( _) => return None ,
635+ } ,
636+ Err ( _) => return None ,
637+ } ;
638+
639+ let header = Elf :: parse ( data) . unwrap ( ) ;
640+ let sections = match header. sections ( endian, data) {
641+ Ok ( s) => s,
642+ Err ( _) => return None ,
643+ } ;
644+
645+ let mut modified: Option < Vec < u8 > > = None ;
646+
647+ for section in sections. iter ( ) {
648+ if section. sh_type ( endian) != elf:: SHT_SYMTAB {
649+ continue ;
650+ }
651+
652+ let strtab_index = section. sh_link ( endian) as usize ;
653+ let strtab_section = match sections. section ( object:: SectionIndex ( strtab_index) ) {
654+ Ok ( s) => s,
655+ Err ( _) => continue ,
656+ } ;
657+ let strtab_data = match strtab_section. data ( endian, data) {
658+ Ok ( d) => d,
659+ Err ( _) => continue ,
660+ } ;
661+
662+ if sym_entry_size == 0 {
663+ continue ;
664+ }
665+
666+ let sym_offset = u64:: from ( section. sh_offset ( endian) ) as usize ;
667+ let sym_size = u64:: from ( section. sh_size ( endian) ) as usize ;
668+ let sym_count = sym_size / sym_entry_size;
669+
670+ let buf = modified. get_or_insert_with ( || data. to_vec ( ) ) ;
671+
672+ for i in 1 ..sym_count {
673+ let off = sym_offset + i * sym_entry_size;
674+ if off + sym_entry_size > buf. len ( ) {
675+ break ;
676+ }
677+
678+ let st_info = buf[ off + st_info_offset] ;
679+ let binding = st_info >> 4 ;
680+
681+ // Only process GLOBAL and WEAK symbols
682+ if binding != elf:: STB_GLOBAL && binding != elf:: STB_WEAK {
683+ continue ;
684+ }
685+
686+ let st_shndx_offset = st_other_offset + 1 ;
687+ let st_shndx_bytes: [ u8 ; 2 ] =
688+ buf[ off + st_shndx_offset..off + st_shndx_offset + 2 ] . try_into ( ) . unwrap_or ( [ 0 , 0 ] ) ;
689+ let st_shndx = match endian {
690+ Endianness :: Little => u16:: from_le_bytes ( st_shndx_bytes) ,
691+ Endianness :: Big => u16:: from_be_bytes ( st_shndx_bytes) ,
692+ } ;
693+ if st_shndx == elf:: SHN_UNDEF as u16 {
694+ continue ;
695+ }
696+
697+ let st_name_bytes: [ u8 ; 4 ] = buf[ off..off + 4 ] . try_into ( ) . unwrap_or ( [ 0 ; 4 ] ) ;
698+ let st_name_off = match endian {
699+ Endianness :: Little => u32:: from_le_bytes ( st_name_bytes) ,
700+ Endianness :: Big => u32:: from_be_bytes ( st_name_bytes) ,
701+ } as usize ;
702+
703+ if st_name_off >= strtab_data. len ( ) {
704+ continue ;
705+ }
706+ let name_end = strtab_data[ st_name_off..]
707+ . iter ( )
708+ . position ( |& b| b == 0 )
709+ . unwrap_or ( strtab_data. len ( ) - st_name_off) ;
710+ let name = match std:: str:: from_utf8 ( & strtab_data[ st_name_off..st_name_off + name_end] )
711+ {
712+ Ok ( s) => s,
713+ Err ( _) => continue ,
714+ } ;
715+
716+ if keep_symbols. contains ( name) {
717+ continue ;
718+ }
719+
720+ buf[ off + st_other_offset] = elf:: STV_HIDDEN ;
721+ }
722+ }
723+
724+ modified
725+ }
0 commit comments