@@ -4,9 +4,11 @@ use std::{
44} ;
55
66use anyhow:: { bail, Result } ;
7+ use arm_attr:: { enums:: CpuArch , tag:: Tag , BuildAttrs } ;
78use object:: {
8- elf, File , Object , ObjectSection , ObjectSymbol , Relocation , RelocationFlags , SectionIndex ,
9- SectionKind , Symbol ,
9+ elf:: { self , SHT_ARM_ATTRIBUTES } ,
10+ Endian , File , Object , ObjectSection , ObjectSymbol , Relocation , RelocationFlags , SectionIndex ,
11+ SectionKind , Symbol , SymbolKind ,
1012} ;
1113use unarm:: {
1214 args:: { Argument , OffsetImm , OffsetReg , Register } ,
@@ -16,41 +18,93 @@ use unarm::{
1618
1719use crate :: {
1820 arch:: { ObjArch , ProcessCodeResult } ,
19- diff:: DiffObjConfig ,
21+ diff:: { ArmArchVersion , DiffObjConfig } ,
2022 obj:: { ObjIns , ObjInsArg , ObjInsArgValue , ObjReloc , ObjSection } ,
2123} ;
2224
2325pub struct ObjArchArm {
2426 /// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
2527 disasm_modes : HashMap < SectionIndex , Vec < DisasmMode > > ,
28+ detected_version : Option < ArmVersion > ,
29+ endianness : object:: Endianness ,
2630}
2731
2832impl ObjArchArm {
2933 pub fn new ( file : & File ) -> Result < Self > {
34+ let endianness = file. endianness ( ) ;
3035 match file {
3136 File :: Elf32 ( _) => {
32- let disasm_modes: HashMap < _ , _ > = file
33- . sections ( )
34- . filter ( |s| s. kind ( ) == SectionKind :: Text )
35- . map ( |s| {
36- let index = s. index ( ) ;
37- let mut mapping_symbols: Vec < _ > = file
38- . symbols ( )
39- . filter ( |s| s. section_index ( ) . map ( |i| i == index) . unwrap_or ( false ) )
40- . filter_map ( |s| DisasmMode :: from_symbol ( & s) )
41- . collect ( ) ;
42- mapping_symbols. sort_unstable_by_key ( |x| x. address ) ;
43- ( s. index ( ) , mapping_symbols)
44- } )
45- . collect ( ) ;
46- Ok ( Self { disasm_modes } )
37+ let disasm_modes = Self :: elf_get_mapping_symbols ( file) ;
38+ let detected_version = Self :: elf_detect_arm_version ( file) ?;
39+ Ok ( Self { disasm_modes, detected_version, endianness } )
4740 }
4841 _ => bail ! ( "Unsupported file format {:?}" , file. format( ) ) ,
4942 }
5043 }
44+
45+ fn elf_detect_arm_version ( file : & File ) -> Result < Option < ArmVersion > > {
46+ // Check ARM attributes
47+ if let Some ( arm_attrs) = file. sections ( ) . find ( |s| {
48+ s. kind ( ) == SectionKind :: Elf ( SHT_ARM_ATTRIBUTES ) && s. name ( ) == Ok ( ".ARM.attributes" )
49+ } ) {
50+ let attr_data = arm_attrs. uncompressed_data ( ) ?;
51+ let build_attrs = BuildAttrs :: new ( & attr_data, match file. endianness ( ) {
52+ object:: Endianness :: Little => arm_attr:: Endian :: Little ,
53+ object:: Endianness :: Big => arm_attr:: Endian :: Big ,
54+ } ) ?;
55+ for subsection in build_attrs. subsections ( ) {
56+ let subsection = subsection?;
57+ if !subsection. is_aeabi ( ) {
58+ continue ;
59+ }
60+ // Only checking first CpuArch tag. Others may exist, but that's very unlikely.
61+ let cpu_arch = subsection. into_public_tag_iter ( ) ?. find_map ( |( _, tag) | {
62+ if let Tag :: CpuArch ( cpu_arch) = tag {
63+ Some ( cpu_arch)
64+ } else {
65+ None
66+ }
67+ } ) ;
68+ match cpu_arch {
69+ Some ( CpuArch :: V4T ) => return Ok ( Some ( ArmVersion :: V4T ) ) ,
70+ Some ( CpuArch :: V5TE ) => return Ok ( Some ( ArmVersion :: V5Te ) ) ,
71+ Some ( CpuArch :: V6K ) => return Ok ( Some ( ArmVersion :: V6K ) ) ,
72+ Some ( arch) => bail ! ( "ARM arch {} not supported" , arch) ,
73+ None => { }
74+ } ;
75+ }
76+ }
77+
78+ Ok ( None )
79+ }
80+
81+ fn elf_get_mapping_symbols ( file : & File ) -> HashMap < SectionIndex , Vec < DisasmMode > > {
82+ file. sections ( )
83+ . filter ( |s| s. kind ( ) == SectionKind :: Text )
84+ . map ( |s| {
85+ let index = s. index ( ) ;
86+ let mut mapping_symbols: Vec < _ > = file
87+ . symbols ( )
88+ . filter ( |s| s. section_index ( ) . map ( |i| i == index) . unwrap_or ( false ) )
89+ . filter_map ( |s| DisasmMode :: from_symbol ( & s) )
90+ . collect ( ) ;
91+ mapping_symbols. sort_unstable_by_key ( |x| x. address ) ;
92+ ( s. index ( ) , mapping_symbols)
93+ } )
94+ . collect ( )
95+ }
5196}
5297
5398impl ObjArch for ObjArchArm {
99+ fn symbol_address ( & self , symbol : & Symbol ) -> u64 {
100+ let address = symbol. address ( ) ;
101+ if symbol. kind ( ) == SymbolKind :: Text {
102+ address & !1
103+ } else {
104+ address
105+ }
106+ }
107+
54108 fn process_code (
55109 & self ,
56110 address : u64 ,
@@ -81,10 +135,21 @@ impl ObjArch for ObjArchArm {
81135 mapping_symbols. iter ( ) . skip ( first_mapping_idx + 1 ) . take_while ( |x| x. address < end_addr) ;
82136 let mut next_mapping = mappings_iter. next ( ) ;
83137
84- let ins_count = code. len ( ) / first_mapping. instruction_size ( ) ;
138+ let ins_count = code. len ( ) / first_mapping. instruction_size ( start_addr ) ;
85139 let mut ops = Vec :: < u16 > :: with_capacity ( ins_count) ;
86140 let mut insts = Vec :: < ObjIns > :: with_capacity ( ins_count) ;
87- let mut parser = Parser :: new ( ArmVersion :: V5Te , first_mapping, start_addr, code) ;
141+
142+ let version = match config. arm_arch_version {
143+ ArmArchVersion :: Auto => self . detected_version . unwrap_or ( ArmVersion :: V5Te ) ,
144+ ArmArchVersion :: V4T => ArmVersion :: V4T ,
145+ ArmArchVersion :: V5TE => ArmVersion :: V5Te ,
146+ ArmArchVersion :: V6K => ArmVersion :: V6K ,
147+ } ;
148+ let endian = match self . endianness {
149+ object:: Endianness :: Little => unarm:: Endian :: Little ,
150+ object:: Endianness :: Big => unarm:: Endian :: Big ,
151+ } ;
152+ let mut parser = Parser :: new ( version, first_mapping, start_addr, endian, code) ;
88153
89154 while let Some ( ( address, op, ins) ) = parser. next ( ) {
90155 if let Some ( next) = next_mapping {
@@ -95,19 +160,26 @@ impl ObjArch for ObjArchArm {
95160 next_mapping = mappings_iter. next ( ) ;
96161 }
97162 }
98-
99163 let line = line_info. range ( ..=address as u64 ) . last ( ) . map ( |( _, & b) | b) ;
100164
101165 let reloc = relocations. iter ( ) . find ( |r| ( r. address as u32 & !1 ) == address) . cloned ( ) ;
102166
103167 let mut reloc_arg = None ;
104168 if let Some ( reloc) = & reloc {
105169 match reloc. flags {
170+ // Calls
106171 RelocationFlags :: Elf { r_type : elf:: R_ARM_THM_XPC22 }
107- | RelocationFlags :: Elf { r_type : elf:: R_ARM_PC24 } => {
172+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_THM_PC22 }
173+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_PC24 }
174+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_XPC25 }
175+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_CALL } => {
108176 reloc_arg =
109177 ins. args . iter ( ) . rposition ( |a| matches ! ( a, Argument :: BranchDest ( _) ) ) ;
110178 }
179+ // Data
180+ RelocationFlags :: Elf { r_type : elf:: R_ARM_ABS32 } => {
181+ reloc_arg = ins. args . iter ( ) . rposition ( |a| matches ! ( a, Argument :: UImm ( _) ) ) ;
182+ }
111183 _ => ( ) ,
112184 }
113185 } ;
@@ -138,11 +210,42 @@ impl ObjArch for ObjArchArm {
138210
139211 fn implcit_addend (
140212 & self ,
141- _section : & ObjSection ,
213+ section : & ObjSection ,
142214 address : u64 ,
143215 reloc : & Relocation ,
144216 ) -> anyhow:: Result < i64 > {
145- bail ! ( "Unsupported ARM implicit relocation {:#x}{:?}" , address, reloc. flags( ) )
217+ let address = address as usize ;
218+ Ok ( match reloc. flags ( ) {
219+ // ARM calls
220+ RelocationFlags :: Elf { r_type : elf:: R_ARM_PC24 }
221+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_XPC25 }
222+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_CALL } => {
223+ let data = section. data [ address..address + 4 ] . try_into ( ) ?;
224+ let addend = self . endianness . read_i32_bytes ( data) ;
225+ let imm24 = addend & 0xffffff ;
226+ ( imm24 << 2 ) << 8 >> 8
227+ }
228+
229+ // Thumb calls
230+ RelocationFlags :: Elf { r_type : elf:: R_ARM_THM_PC22 }
231+ | RelocationFlags :: Elf { r_type : elf:: R_ARM_THM_XPC22 } => {
232+ let data = section. data [ address..address + 2 ] . try_into ( ) ?;
233+ let high = self . endianness . read_i16_bytes ( data) as i32 ;
234+ let data = section. data [ address + 2 ..address + 4 ] . try_into ( ) ?;
235+ let low = self . endianness . read_i16_bytes ( data) as i32 ;
236+
237+ let imm22 = ( ( high & 0x7ff ) << 11 ) | ( low & 0x7ff ) ;
238+ ( imm22 << 1 ) << 9 >> 9
239+ }
240+
241+ // Data
242+ RelocationFlags :: Elf { r_type : elf:: R_ARM_ABS32 } => {
243+ let data = section. data [ address..address + 4 ] . try_into ( ) ?;
244+ self . endianness . read_i32_bytes ( data)
245+ }
246+
247+ flags => bail ! ( "Unsupported ARM implicit relocation {flags:?}" ) ,
248+ } as i64 )
146249 }
147250
148251 fn demangle ( & self , name : & str ) -> Option < String > {
@@ -209,6 +312,7 @@ fn push_args(
209312 args. push ( ObjInsArg :: Reloc ) ;
210313 } else {
211314 match arg {
315+ Argument :: None => { }
212316 Argument :: Reg ( reg) => {
213317 if reg. deref {
214318 deref = true ;
@@ -242,7 +346,7 @@ fn push_args(
242346 args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Opaque ( "^" . to_string ( ) . into ( ) ) ) ) ;
243347 }
244348 }
245- Argument :: UImm ( value) | Argument :: CoOpcode ( value) => {
349+ Argument :: UImm ( value) | Argument :: CoOpcode ( value) | Argument :: SatImm ( value ) => {
246350 args. push ( ObjInsArg :: PlainText ( "#" . into ( ) ) ) ;
247351 args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Unsigned ( * value as u64 ) ) ) ;
248352 }
@@ -282,7 +386,21 @@ fn push_args(
282386 offset. reg . to_string ( ) . into ( ) ,
283387 ) ) ) ;
284388 }
285- _ => args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Opaque ( arg. to_string ( ) . into ( ) ) ) ) ,
389+ Argument :: CpsrMode ( mode) => {
390+ args. push ( ObjInsArg :: PlainText ( "#" . into ( ) ) ) ;
391+ args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Unsigned ( mode. mode as u64 ) ) ) ;
392+ if mode. writeback {
393+ args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Opaque ( "!" . into ( ) ) ) ) ;
394+ }
395+ }
396+ Argument :: CoReg ( _)
397+ | Argument :: StatusReg ( _)
398+ | Argument :: StatusMask ( _)
399+ | Argument :: Shift ( _)
400+ | Argument :: CpsrFlags ( _)
401+ | Argument :: Endian ( _) => {
402+ args. push ( ObjInsArg :: Arg ( ObjInsArgValue :: Opaque ( arg. to_string ( ) . into ( ) ) ) )
403+ }
286404 }
287405 }
288406 }
0 commit comments