1
- use alloc:: { string:: ToString , vec:: Vec } ;
1
+ use alloc:: { collections :: BTreeMap , string:: ToString , vec:: Vec } ;
2
2
use core:: ops:: Range ;
3
3
4
4
use anyhow:: { bail, Result } ;
@@ -25,6 +25,7 @@ pub struct ArchMips {
25
25
pub abi : Abi ,
26
26
pub isa_extension : Option < IsaExtension > ,
27
27
pub ri_gp_value : i32 ,
28
+ pub paired_relocations : Vec < BTreeMap < u64 , i64 > > ,
28
29
}
29
30
30
31
const EF_MIPS_ABI : u32 = 0x0000F000 ;
@@ -64,16 +65,60 @@ impl ArchMips {
64
65
65
66
// Parse the ri_gp_value stored in .reginfo to be able to correctly
66
67
// 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 ( ) ;
68
70
let ri_gp_value = object
69
71
. section_by_name ( ".reginfo" )
70
72
. and_then ( |section| section. data ( ) . ok ( ) )
71
73
. and_then ( |data| data. get ( 0x14 ..0x18 ) )
72
74
. and_then ( |s| s. try_into ( ) . ok ( ) )
73
- . map ( |bytes| object . endianness ( ) . read_i32_bytes ( bytes) )
75
+ . map ( |bytes| endianness. read_i32_bytes ( bytes) )
74
76
. unwrap_or ( 0 ) ;
75
77
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 } )
77
122
}
78
123
79
124
fn instruction_flags ( & self , diff_config : & DiffObjConfig ) -> rabbitizer:: InstructionFlags {
@@ -127,18 +172,16 @@ impl Arch for ArchMips {
127
172
diff_config : & DiffObjConfig ,
128
173
) -> Result < Vec < ScannedInstruction > > {
129
174
let instruction_flags = self . instruction_flags ( diff_config) ;
130
- let start_address = address;
131
175
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 ;
133
177
for chunk in code. chunks_exact ( 4 ) {
134
178
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) ;
137
181
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 ) ;
140
183
ops. push ( ScannedInstruction {
141
- ins_ref : InstructionRef { address, size : 4 , opcode } ,
184
+ ins_ref : InstructionRef { address : cur_addr as u64 , size : 4 , opcode } ,
142
185
branch_dest,
143
186
} ) ;
144
187
cur_addr += 4 ;
@@ -164,6 +207,7 @@ impl Arch for ArchMips {
164
207
function_range,
165
208
resolved. section_index ,
166
209
& display_flags,
210
+ diff_config,
167
211
cb,
168
212
) ?;
169
213
Ok ( ( ) )
@@ -177,6 +221,17 @@ impl Arch for ArchMips {
177
221
reloc : & object:: Relocation ,
178
222
flags : RelocationFlags ,
179
223
) -> 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
+
180
235
let data = section. data ( ) ?;
181
236
let code = data[ address as usize ..address as usize + 4 ] . try_into ( ) ?;
182
237
let addend = self . endianness . read_u32_bytes ( code) ;
@@ -246,6 +301,7 @@ fn push_args(
246
301
function_range : Range < u64 > ,
247
302
section_index : usize ,
248
303
display_flags : & rabbitizer:: InstructionDisplayFlags ,
304
+ diff_config : & DiffObjConfig ,
249
305
mut arg_cb : impl FnMut ( InstructionPart ) -> Result < ( ) > ,
250
306
) -> Result < ( ) > {
251
307
let operands = instruction. valued_operands_iter ( ) ;
@@ -305,9 +361,14 @@ fn push_args(
305
361
} ) ) ) ?;
306
362
}
307
363
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) ) ?;
311
372
arg_cb ( InstructionPart :: basic ( ")" ) ) ?;
312
373
}
313
374
// ValuedOperand::r5900_immediate15(..) => match relocation {
@@ -321,9 +382,14 @@ fn push_args(
321
382
// }
322
383
// },
323
384
_ => {
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) ) ?;
327
393
}
328
394
}
329
395
}
0 commit comments