1- use alloc:: { string:: ToString , vec:: Vec } ;
1+ use alloc:: { collections :: BTreeMap , string:: ToString , vec:: Vec } ;
22use core:: ops:: Range ;
33
44use anyhow:: { bail, Result } ;
@@ -25,6 +25,7 @@ pub struct ArchMips {
2525 pub abi : Abi ,
2626 pub isa_extension : Option < IsaExtension > ,
2727 pub ri_gp_value : i32 ,
28+ pub paired_relocations : Vec < BTreeMap < u64 , i64 > > ,
2829}
2930
3031const EF_MIPS_ABI : u32 = 0x0000F000 ;
@@ -64,16 +65,60 @@ impl ArchMips {
6465
6566 // Parse the ri_gp_value stored in .reginfo to be able to correctly
6667 // calculate R_MIPS_GPREL16 relocations later. The value is stored
67- // 0x14 bytes into .reginfo (on 32 bit platforms)
68+ // 0x14 bytes into .reginfo (on 32-bit platforms)
69+ let endianness = object. endianness ( ) ;
6870 let ri_gp_value = object
6971 . section_by_name ( ".reginfo" )
7072 . and_then ( |section| section. data ( ) . ok ( ) )
7173 . and_then ( |data| data. get ( 0x14 ..0x18 ) )
7274 . and_then ( |s| s. try_into ( ) . ok ( ) )
73- . map ( |bytes| object . endianness ( ) . read_i32_bytes ( bytes) )
75+ . map ( |bytes| endianness. read_i32_bytes ( bytes) )
7476 . unwrap_or ( 0 ) ;
7577
76- Ok ( Self { endianness : object. endianness ( ) , abi, isa_extension, ri_gp_value } )
78+ // Parse all relocations to pair R_MIPS_HI16 and R_MIPS_LO16. Since the instructions only
79+ // have 16-bit immediate fields, the 32-bit addend is split across the two relocations.
80+ // R_MIPS_LO16 relocations without an immediately preceding R_MIPS_HI16 use the last seen
81+ // R_MIPS_HI16 addend.
82+ // See https://refspecs.linuxfoundation.org/elf/mipsabi.pdf pages 4-17 and 4-18
83+ let mut paired_relocations = Vec :: with_capacity ( object. sections ( ) . count ( ) + 1 ) ;
84+ for obj_section in object. sections ( ) {
85+ let data = obj_section. data ( ) ?;
86+ let mut last_hi = None ;
87+ let mut last_hi_addend = 0 ;
88+ let mut addends = BTreeMap :: new ( ) ;
89+ for ( addr, reloc) in obj_section. relocations ( ) {
90+ if !reloc. has_implicit_addend ( ) {
91+ continue ;
92+ }
93+ match reloc. flags ( ) {
94+ object:: RelocationFlags :: Elf { r_type : elf:: R_MIPS_HI16 } => {
95+ let code = data[ addr as usize ..addr as usize + 4 ] . try_into ( ) ?;
96+ let addend = ( ( endianness. read_u32_bytes ( code) & 0x0000FFFF ) << 16 ) as i32 ;
97+ last_hi = Some ( addr) ;
98+ last_hi_addend = addend;
99+ }
100+ object:: RelocationFlags :: Elf { r_type : elf:: R_MIPS_LO16 } => {
101+ let code = data[ addr as usize ..addr as usize + 4 ] . try_into ( ) ?;
102+ let addend = ( endianness. read_u32_bytes ( code) & 0x0000FFFF ) as i16 as i32 ;
103+ let full_addend = ( last_hi_addend + addend) as i64 ;
104+ if let Some ( hi_addr) = last_hi. take ( ) {
105+ addends. insert ( hi_addr, full_addend) ;
106+ }
107+ addends. insert ( addr, full_addend) ;
108+ }
109+ _ => {
110+ last_hi = None ;
111+ }
112+ }
113+ }
114+ let section_index = obj_section. index ( ) . 0 ;
115+ if section_index >= paired_relocations. len ( ) {
116+ paired_relocations. resize_with ( section_index + 1 , BTreeMap :: new) ;
117+ }
118+ paired_relocations[ section_index] = addends;
119+ }
120+
121+ Ok ( Self { endianness, abi, isa_extension, ri_gp_value, paired_relocations } )
77122 }
78123
79124 fn instruction_flags ( & self , diff_config : & DiffObjConfig ) -> rabbitizer:: InstructionFlags {
@@ -127,18 +172,16 @@ impl Arch for ArchMips {
127172 diff_config : & DiffObjConfig ,
128173 ) -> Result < Vec < ScannedInstruction > > {
129174 let instruction_flags = self . instruction_flags ( diff_config) ;
130- let start_address = address;
131175 let mut ops = Vec :: < ScannedInstruction > :: with_capacity ( code. len ( ) / 4 ) ;
132- let mut cur_addr = start_address as u32 ;
176+ let mut cur_addr = address as u32 ;
133177 for chunk in code. chunks_exact ( 4 ) {
134178 let code = self . endianness . read_u32_bytes ( chunk. try_into ( ) ?) ;
135- let vram = Vram :: new ( cur_addr ) ;
136- let instruction = rabbitizer:: Instruction :: new ( code, vram , instruction_flags) ;
179+ let instruction =
180+ rabbitizer:: Instruction :: new ( code, Vram :: new ( cur_addr ) , instruction_flags) ;
137181 let opcode = instruction. opcode ( ) as u16 ;
138- let branch_dest =
139- instruction. get_branch_offset_generic ( ) . map ( |o| ( vram + o) . inner ( ) as u64 ) ;
182+ let branch_dest = instruction. get_branch_vram_generic ( ) . map ( |v| v. inner ( ) as u64 ) ;
140183 ops. push ( ScannedInstruction {
141- ins_ref : InstructionRef { address, size : 4 , opcode } ,
184+ ins_ref : InstructionRef { address : cur_addr as u64 , size : 4 , opcode } ,
142185 branch_dest,
143186 } ) ;
144187 cur_addr += 4 ;
@@ -164,6 +207,7 @@ impl Arch for ArchMips {
164207 function_range,
165208 resolved. section_index ,
166209 & display_flags,
210+ diff_config,
167211 cb,
168212 ) ?;
169213 Ok ( ( ) )
@@ -177,6 +221,17 @@ impl Arch for ArchMips {
177221 reloc : & object:: Relocation ,
178222 flags : RelocationFlags ,
179223 ) -> Result < i64 > {
224+ // Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
225+ if let RelocationFlags :: Elf ( elf:: R_MIPS_HI16 | elf:: R_MIPS_LO16 ) = flags {
226+ if let Some ( addend) = self
227+ . paired_relocations
228+ . get ( section. index ( ) . 0 )
229+ . and_then ( |m| m. get ( & address) . copied ( ) )
230+ {
231+ return Ok ( addend) ;
232+ }
233+ }
234+
180235 let data = section. data ( ) ?;
181236 let code = data[ address as usize ..address as usize + 4 ] . try_into ( ) ?;
182237 let addend = self . endianness . read_u32_bytes ( code) ;
@@ -246,6 +301,7 @@ fn push_args(
246301 function_range : Range < u64 > ,
247302 section_index : usize ,
248303 display_flags : & rabbitizer:: InstructionDisplayFlags ,
304+ diff_config : & DiffObjConfig ,
249305 mut arg_cb : impl FnMut ( InstructionPart ) -> Result < ( ) > ,
250306) -> Result < ( ) > {
251307 let operands = instruction. valued_operands_iter ( ) ;
@@ -305,9 +361,14 @@ fn push_args(
305361 } ) ) ) ?;
306362 }
307363 arg_cb ( InstructionPart :: basic ( "(" ) ) ?;
308- arg_cb ( InstructionPart :: opaque (
309- base. either_name ( instruction. flags ( ) . abi ( ) , display_flags. named_gpr ( ) ) ,
310- ) ) ?;
364+ let mut value =
365+ base. either_name ( instruction. flags ( ) . abi ( ) , display_flags. named_gpr ( ) ) ;
366+ if !diff_config. mips_register_prefix {
367+ if let Some ( trimmed) = value. strip_prefix ( '$' ) {
368+ value = trimmed;
369+ }
370+ }
371+ arg_cb ( InstructionPart :: opaque ( value) ) ?;
311372 arg_cb ( InstructionPart :: basic ( ")" ) ) ?;
312373 }
313374 // ValuedOperand::r5900_immediate15(..) => match relocation {
@@ -321,9 +382,14 @@ fn push_args(
321382 // }
322383 // },
323384 _ => {
324- arg_cb ( InstructionPart :: opaque (
325- op. display ( instruction, display_flags, None :: < & str > ) . to_string ( ) ,
326- ) ) ?;
385+ let value = op. display ( instruction, display_flags, None :: < & str > ) . to_string ( ) ;
386+ if !diff_config. mips_register_prefix {
387+ if let Some ( value) = value. strip_prefix ( '$' ) {
388+ arg_cb ( InstructionPart :: opaque ( value) ) ?;
389+ continue ;
390+ }
391+ }
392+ arg_cb ( InstructionPart :: opaque ( value) ) ?;
327393 }
328394 }
329395 }
0 commit comments