@@ -4,6 +4,8 @@ use crate::bail;
44use crate :: elf;
55use crate :: error:: Result ;
66use object:: LittleEndian ;
7+ use object:: read:: elf:: FileHeader ;
8+ use object:: read:: elf:: SectionHeader ;
79
810#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
911pub ( crate ) enum FileKind {
@@ -12,6 +14,8 @@ pub(crate) enum FileKind {
1214 Archive ,
1315 ThinArchive ,
1416 Text ,
17+ LlvmIr ,
18+ GccIr ,
1519}
1620
1721impl FileKind {
@@ -34,16 +38,68 @@ impl FileKind {
3438 }
3539
3640 match header. e_type . get ( LittleEndian ) {
37- object:: elf:: ET_REL => Ok ( FileKind :: ElfObject ) ,
41+ object:: elf:: ET_REL => {
42+ if is_gcc_bitcode ( bytes, header) . unwrap_or ( false ) {
43+ Ok ( FileKind :: GccIr )
44+ } else {
45+ Ok ( FileKind :: ElfObject )
46+ }
47+ }
3848 object:: elf:: ET_DYN => Ok ( FileKind :: ElfDynamic ) ,
3949 t => bail ! ( "Unsupported ELF kind {t}" ) ,
4050 }
4151 } else if bytes. is_ascii ( ) {
4252 Ok ( FileKind :: Text )
4353 } else if bytes. starts_with ( b"BC" ) {
44- bail ! ( "LLVM IR (LTO mode) is not supported yet" ) ;
54+ Ok ( FileKind :: LlvmIr )
4555 } else {
4656 bail ! ( "Couldn't identify file type" ) ;
4757 }
4858 }
59+
60+ pub ( crate ) fn is_compiler_ir ( self ) -> bool {
61+ matches ! ( self , FileKind :: LlvmIr | FileKind :: GccIr )
62+ }
63+ }
64+
65+ /// Returns whether the supplied file contents is GCC IR. Scanning the entire section table would be
66+ /// expensive. Instead, we assume that we'll find a GCC LTO section within the first few sections,
67+ /// so just scan part of the section header strings table. It's unfortunate that GCC didn't tag
68+ /// these objects in some fast-to-check way.
69+ fn is_gcc_bitcode ( data : & [ u8 ] , header : & crate :: elf:: FileHeader ) -> Option < bool > {
70+ // If we don't have plugin support, then we skip checking if the file contains GCC IR. If it is,
71+ // then we'll figure that out later on and report an error. We do this because this code has a
72+ // measurable performance impact.
73+ if !cfg ! ( feature = "plugins" ) {
74+ return Some ( false ) ;
75+ }
76+ let e = LittleEndian ;
77+ let section_headers = header. section_headers ( e, data) . ok ( ) ?;
78+ let sh_str_index = header. shstrndx ( e, data) . ok ( ) ?;
79+ let strings_section_header = section_headers. get ( sh_str_index as usize ) ?;
80+ let start_offset = strings_section_header. sh_offset ( e) as usize ;
81+ let len = strings_section_header. sh_size ( e) as usize ;
82+ // In observed GCC IR files, the LTO section names start at offset 44 and end at 454. We want to
83+ // scan roughly the middle of this range.
84+ const START : usize = 100 ;
85+ // The longest GCC LTO section name is 47 bytes. We scan a bit more in case the first LTO
86+ // section started later than START.
87+ const MAX_SCAN : usize = 200 ;
88+ let strings = data. get ( start_offset + START ..start_offset + ( START + MAX_SCAN ) . min ( len) ) ?;
89+ Some ( memchr:: memmem:: find ( strings, b"\0 .gnu.lto_." ) . is_some ( ) )
90+ }
91+
92+ impl std:: fmt:: Display for FileKind {
93+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
94+ let s = match self {
95+ FileKind :: ElfObject => "ELF object" ,
96+ FileKind :: ElfDynamic => "ELF dynamic" ,
97+ FileKind :: Archive => "archive" ,
98+ FileKind :: ThinArchive => "thin archive" ,
99+ FileKind :: Text => "text" ,
100+ FileKind :: LlvmIr => "LLVM-IR" ,
101+ FileKind :: GccIr => "GCC-IR" ,
102+ } ;
103+ std:: fmt:: Display :: fmt ( s, f)
104+ }
49105}
0 commit comments