@@ -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 ;
@@ -312,6 +312,8 @@ pub trait ArchiveBuilder {
312312 ) -> io:: Result < ( ) > ;
313313
314314 fn build ( self : Box < Self > , output : & Path ) -> bool ;
315+
316+ fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) ;
315317}
316318
317319pub struct ArArchiveBuilderBuilder ;
@@ -331,6 +333,7 @@ pub struct ArArchiveBuilder<'a> {
331333 // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs
332334 // to be at the end of an archive in some cases for linkers to not get confused.
333335 entries : Vec < ( Vec < u8 > , ArchiveEntry ) > ,
336+ keep_symbols : Option < FxHashSet < String > > ,
334337}
335338
336339#[ derive( Debug ) ]
@@ -341,7 +344,17 @@ enum ArchiveEntry {
341344
342345impl < ' a > ArArchiveBuilder < ' a > {
343346 pub fn new ( sess : & ' a Session , object_reader : & ' static ObjectReader ) -> ArArchiveBuilder < ' a > {
344- ArArchiveBuilder { sess, object_reader, src_archives : vec ! [ ] , entries : vec ! [ ] }
347+ ArArchiveBuilder {
348+ sess,
349+ object_reader,
350+ src_archives : vec ! [ ] ,
351+ entries : vec ! [ ] ,
352+ keep_symbols : None ,
353+ }
354+ }
355+
356+ pub fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) {
357+ self . keep_symbols = Some ( keep) ;
345358 }
346359}
347360
@@ -454,6 +467,10 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
454467 }
455468 }
456469 }
470+
471+ fn set_keep_symbols ( & mut self , keep : FxHashSet < String > ) {
472+ self . keep_symbols = Some ( keep) ;
473+ }
457474}
458475
459476impl < ' a > ArArchiveBuilder < ' a > {
@@ -472,7 +489,7 @@ impl<'a> ArArchiveBuilder<'a> {
472489 let mut entries = Vec :: new ( ) ;
473490
474491 for ( entry_name, entry) in self . entries {
475- let data =
492+ let data: Box < dyn AsRef < [ u8 ] > > =
476493 match entry {
477494 ArchiveEntry :: FromArchive { archive_index, file_range } => {
478495 let src_archive = & self . src_archives [ archive_index] ;
@@ -492,6 +509,16 @@ impl<'a> ArArchiveBuilder<'a> {
492509 } ,
493510 } ;
494511
512+ let data: Box < dyn AsRef < [ u8 ] > > = if let Some ( ref keep) = self . keep_symbols {
513+ if let Some ( filtered) = elf_filter_global_symbols ( data. as_ref ( ) . as_ref ( ) , keep) {
514+ Box :: new ( filtered)
515+ } else {
516+ data
517+ }
518+ } else {
519+ data
520+ } ;
521+
495522 entries. push ( NewArchiveMember {
496523 buf : data,
497524 object_reader : self . object_reader ,
@@ -551,3 +578,142 @@ impl<'a> ArArchiveBuilder<'a> {
551578fn io_error_context ( context : & str , err : io:: Error ) -> io:: Error {
552579 io:: Error :: new ( io:: ErrorKind :: Other , format ! ( "{context}: {err}" ) )
553580}
581+
582+ /// For ELF object files, set `STV_HIDDEN` visibility on GLOBAL/WEAK symbols
583+ /// that are NOT in the `keep_symbols` set. Returns `Some(modified_data)` if
584+ /// any changes were made, `None` if the data is not an ELF object or no
585+ /// changes were needed.
586+ fn elf_filter_global_symbols ( data : & [ u8 ] , keep_symbols : & FxHashSet < String > ) -> Option < Vec < u8 > > {
587+ use object:: { Endianness , elf} ;
588+
589+ if data. len ( ) < 16 || & data[ 0 ..4 ] != elf:: ELFMAG {
590+ return None ;
591+ }
592+
593+ match data[ 4 ] {
594+ elf:: ELFCLASS64 => elf_filter_symbols_inner :: < elf:: FileHeader64 < Endianness > > (
595+ data,
596+ keep_symbols,
597+ /* sym_entry_size= */ 24 ,
598+ /* st_info_offset= */ 4 ,
599+ /* st_other_offset= */ 5 ,
600+ ) ,
601+ elf:: ELFCLASS32 => elf_filter_symbols_inner :: < elf:: FileHeader32 < Endianness > > (
602+ data,
603+ keep_symbols,
604+ /* sym_entry_size= */ 16 ,
605+ /* st_info_offset= */ 12 ,
606+ /* st_other_offset= */ 13 ,
607+ ) ,
608+ _ => None ,
609+ }
610+ }
611+
612+ fn elf_filter_symbols_inner < Elf : object:: read:: elf:: FileHeader < Endian = object:: Endianness > > (
613+ data : & [ u8 ] ,
614+ keep_symbols : & FxHashSet < String > ,
615+ sym_entry_size : usize ,
616+ st_info_offset : usize ,
617+ st_other_offset : usize ,
618+ ) -> Option < Vec < u8 > >
619+ where
620+ u64 : From < Elf :: Word > ,
621+ {
622+ use object:: read:: elf:: SectionHeader ;
623+ use object:: { Endianness , elf} ;
624+
625+ let endian = match Elf :: parse ( data) {
626+ Ok ( h) => match h. endian ( ) {
627+ Ok ( e) => e,
628+ Err ( _) => return None ,
629+ } ,
630+ Err ( _) => return None ,
631+ } ;
632+
633+ let header = Elf :: parse ( data) . unwrap ( ) ;
634+ let sections = match header. sections ( endian, data) {
635+ Ok ( s) => s,
636+ Err ( _) => return None ,
637+ } ;
638+
639+ let mut modified: Option < Vec < u8 > > = None ;
640+
641+ for section in sections. iter ( ) {
642+ if section. sh_type ( endian) != elf:: SHT_SYMTAB {
643+ continue ;
644+ }
645+
646+ let strtab_index = section. sh_link ( endian) as usize ;
647+ let strtab_section = match sections. section ( object:: SectionIndex ( strtab_index) ) {
648+ Ok ( s) => s,
649+ Err ( _) => continue ,
650+ } ;
651+ let strtab_data = match strtab_section. data ( endian, data) {
652+ Ok ( d) => d,
653+ Err ( _) => continue ,
654+ } ;
655+
656+ if sym_entry_size == 0 {
657+ continue ;
658+ }
659+
660+ let sym_offset = u64:: from ( section. sh_offset ( endian) ) as usize ;
661+ let sym_size = u64:: from ( section. sh_size ( endian) ) as usize ;
662+ let sym_count = sym_size / sym_entry_size;
663+
664+ let buf = modified. get_or_insert_with ( || data. to_vec ( ) ) ;
665+
666+ for i in 1 ..sym_count {
667+ let off = sym_offset + i * sym_entry_size;
668+ if off + sym_entry_size > buf. len ( ) {
669+ break ;
670+ }
671+
672+ let st_info = buf[ off + st_info_offset] ;
673+ let binding = st_info >> 4 ;
674+
675+ // Only process GLOBAL and WEAK symbols
676+ if binding != elf:: STB_GLOBAL && binding != elf:: STB_WEAK {
677+ continue ;
678+ }
679+
680+ let st_shndx_offset = st_other_offset + 1 ;
681+ let st_shndx_bytes: [ u8 ; 2 ] =
682+ buf[ off + st_shndx_offset..off + st_shndx_offset + 2 ] . try_into ( ) . unwrap_or ( [ 0 , 0 ] ) ;
683+ let st_shndx = match endian {
684+ Endianness :: Little => u16:: from_le_bytes ( st_shndx_bytes) ,
685+ Endianness :: Big => u16:: from_be_bytes ( st_shndx_bytes) ,
686+ } ;
687+ if st_shndx == elf:: SHN_UNDEF as u16 {
688+ continue ;
689+ }
690+
691+ let st_name_bytes: [ u8 ; 4 ] = buf[ off..off + 4 ] . try_into ( ) . unwrap_or ( [ 0 ; 4 ] ) ;
692+ let st_name_off = match endian {
693+ Endianness :: Little => u32:: from_le_bytes ( st_name_bytes) ,
694+ Endianness :: Big => u32:: from_be_bytes ( st_name_bytes) ,
695+ } as usize ;
696+
697+ if st_name_off >= strtab_data. len ( ) {
698+ continue ;
699+ }
700+ let name_end = strtab_data[ st_name_off..]
701+ . iter ( )
702+ . position ( |& b| b == 0 )
703+ . unwrap_or ( strtab_data. len ( ) - st_name_off) ;
704+ let name = match std:: str:: from_utf8 ( & strtab_data[ st_name_off..st_name_off + name_end] )
705+ {
706+ Ok ( s) => s,
707+ Err ( _) => continue ,
708+ } ;
709+
710+ if keep_symbols. contains ( name) {
711+ continue ;
712+ }
713+
714+ buf[ off + st_other_offset] = elf:: STV_HIDDEN ;
715+ }
716+ }
717+
718+ modified
719+ }
0 commit comments