Skip to content

Commit 0c48d71

Browse files
committed
Improve local branch relocation handling
Reworks the local-branch handling logic to be more unified: scan_instructions does all the work up front, and process_instruction / display_instruction can simply use the calculated branch destination instead of performing their own is-relocation-target- function-local checks. (Hopefully) Fixes #192
1 parent 34220a8 commit 0c48d71

21 files changed

+1253
-226
lines changed

objdiff-core/src/arch/arm.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
1616
obj::{
1717
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
18-
ScannedInstruction, Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
18+
Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
1919
},
2020
};
2121

@@ -187,14 +187,14 @@ impl Arch for ArchArm {
187187
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
188188
}
189189

190-
fn scan_instructions(
190+
fn scan_instructions_internal(
191191
&self,
192192
address: u64,
193193
code: &[u8],
194194
section_index: usize,
195195
_relocations: &[Relocation],
196196
diff_config: &DiffObjConfig,
197-
) -> Result<Vec<ScannedInstruction>> {
197+
) -> Result<Vec<InstructionRef>> {
198198
let start_addr = address as u32;
199199
let end_addr = start_addr + code.len() as u32;
200200

@@ -219,7 +219,7 @@ impl Arch for ArchArm {
219219
let mut next_mapping = mappings_iter.next();
220220

221221
let ins_count = code.len() / mode.instruction_size(start_addr);
222-
let mut ops = Vec::<ScannedInstruction>::with_capacity(ins_count);
222+
let mut ops = Vec::<InstructionRef>::with_capacity(ins_count);
223223

224224
let parse_flags = self.parse_flags(diff_config);
225225

@@ -235,12 +235,10 @@ impl Arch for ArchArm {
235235
let data = &code[(address - start_addr) as usize..];
236236
if data.len() < ins_size {
237237
// Push the remainder as data
238-
ops.push(ScannedInstruction {
239-
ins_ref: InstructionRef {
240-
address: address as u64,
241-
size: data.len() as u8,
242-
opcode: u16::MAX,
243-
},
238+
ops.push(InstructionRef {
239+
address: address as u64,
240+
size: data.len() as u8,
241+
opcode: u16::MAX,
244242
branch_dest: None,
245243
});
246244
break;
@@ -256,12 +254,10 @@ impl Arch for ArchArm {
256254
}
257255
_ => {
258256
// Invalid instruction size
259-
ops.push(ScannedInstruction {
260-
ins_ref: InstructionRef {
261-
address: address as u64,
262-
size: ins_size as u8,
263-
opcode: u16::MAX,
264-
},
257+
ops.push(InstructionRef {
258+
address: address as u64,
259+
size: ins_size as u8,
260+
opcode: u16::MAX,
265261
branch_dest: None,
266262
});
267263
address += ins_size as u32;
@@ -325,8 +321,10 @@ impl Arch for ArchArm {
325321
unarm::ParseMode::Data => (u16::MAX, None),
326322
};
327323

328-
ops.push(ScannedInstruction {
329-
ins_ref: InstructionRef { address: address as u64, size: ins_size as u8, opcode },
324+
ops.push(InstructionRef {
325+
address: address as u64,
326+
size: ins_size as u8,
327+
opcode,
330328
branch_dest: branch_dest.map(|x| x as u64),
331329
});
332330
address += ins_size as u32;

objdiff-core/src/arch/arm64.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::{
1818
diff::{DiffObjConfig, display::InstructionPart},
1919
obj::{
2020
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
21-
ScannedInstruction,
2221
},
2322
};
2423

@@ -30,16 +29,16 @@ impl ArchArm64 {
3029
}
3130

3231
impl Arch for ArchArm64 {
33-
fn scan_instructions(
32+
fn scan_instructions_internal(
3433
&self,
3534
address: u64,
3635
code: &[u8],
3736
_section_index: usize,
3837
_relocations: &[Relocation],
3938
_diff_config: &DiffObjConfig,
40-
) -> Result<Vec<ScannedInstruction>> {
39+
) -> Result<Vec<InstructionRef>> {
4140
let start_address = address;
42-
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
41+
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
4342

4443
let mut reader = U8Reader::new(code);
4544
let decoder = InstDecoder::default();
@@ -58,8 +57,10 @@ impl Arch for ArchArm64 {
5857
DecodeError::InvalidOpcode
5958
| DecodeError::InvalidOperand
6059
| DecodeError::IncompleteDecoder => {
61-
ops.push(ScannedInstruction {
62-
ins_ref: InstructionRef { address, size: 4, opcode: u16::MAX },
60+
ops.push(InstructionRef {
61+
address,
62+
size: 4,
63+
opcode: u16::MAX,
6364
branch_dest: None,
6465
});
6566
continue;
@@ -68,9 +69,9 @@ impl Arch for ArchArm64 {
6869
}
6970

7071
let opcode = opcode_to_u16(ins.opcode);
71-
let ins_ref = InstructionRef { address, size: 4, opcode };
72-
let branch_dest = branch_dest(ins_ref, &code[offset as usize..offset as usize + 4]);
73-
ops.push(ScannedInstruction { ins_ref, branch_dest });
72+
let branch_dest =
73+
branch_dest(opcode, address, &code[offset as usize..offset as usize + 4]);
74+
ops.push(InstructionRef { address, size: 4, opcode, branch_dest });
7475
}
7576

7677
Ok(ops)
@@ -163,7 +164,7 @@ impl Arch for ArchArm64 {
163164
}
164165
}
165166

166-
fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
167+
fn branch_dest(opcode: u16, address: u64, code: &[u8]) -> Option<u64> {
167168
const OPCODE_B: u16 = opcode_to_u16(Opcode::B);
168169
const OPCODE_BL: u16 = opcode_to_u16(Opcode::BL);
169170
const OPCODE_BCC: u16 = opcode_to_u16(Opcode::Bcc(0));
@@ -173,21 +174,21 @@ fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
173174
const OPCODE_TBNZ: u16 = opcode_to_u16(Opcode::TBNZ);
174175

175176
let word = u32::from_le_bytes(code.try_into().ok()?);
176-
match ins_ref.opcode {
177+
match opcode {
177178
OPCODE_B | OPCODE_BL => {
178179
let offset = ((word & 0x03ff_ffff) << 2) as i32;
179180
let extended_offset = (offset << 4) >> 4;
180-
ins_ref.address.checked_add_signed(extended_offset as i64)
181+
address.checked_add_signed(extended_offset as i64)
181182
}
182183
OPCODE_BCC | OPCODE_CBZ | OPCODE_CBNZ => {
183184
let offset = (word as i32 & 0x00ff_ffe0) >> 3;
184185
let extended_offset = (offset << 11) >> 11;
185-
ins_ref.address.checked_add_signed(extended_offset as i64)
186+
address.checked_add_signed(extended_offset as i64)
186187
}
187188
OPCODE_TBZ | OPCODE_TBNZ => {
188189
let offset = (word as i32 & 0x0007_ffe0) >> 3;
189190
let extended_offset = (offset << 16) >> 16;
190-
ins_ref.address.checked_add_signed(extended_offset as i64)
191+
address.checked_add_signed(extended_offset as i64)
191192
}
192193
_ => None,
193194
}

objdiff-core/src/arch/mips.rs

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use alloc::{
33
string::{String, ToString},
44
vec::Vec,
55
};
6-
use core::ops::Range;
76

87
use anyhow::{Result, bail};
98
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
@@ -19,7 +18,7 @@ use crate::{
1918
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
2019
obj::{
2120
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
22-
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
21+
ResolvedInstructionRef, ResolvedRelocation, SymbolFlag, SymbolFlagSet,
2322
},
2423
};
2524

@@ -189,27 +188,24 @@ impl ArchMips {
189188
}
190189

191190
impl Arch for ArchMips {
192-
fn scan_instructions(
191+
fn scan_instructions_internal(
193192
&self,
194193
address: u64,
195194
code: &[u8],
196195
_section_index: usize,
197196
_relocations: &[Relocation],
198197
diff_config: &DiffObjConfig,
199-
) -> Result<Vec<ScannedInstruction>> {
198+
) -> Result<Vec<InstructionRef>> {
200199
let instruction_flags = self.instruction_flags(diff_config);
201-
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
200+
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
202201
let mut cur_addr = address as u32;
203202
for chunk in code.chunks_exact(4) {
204203
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
205204
let instruction =
206205
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
207206
let opcode = instruction.opcode() as u16;
208207
let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
209-
ops.push(ScannedInstruction {
210-
ins_ref: InstructionRef { address: cur_addr as u64, size: 4, opcode },
211-
branch_dest,
212-
});
208+
ops.push(InstructionRef { address: cur_addr as u64, size: 4, opcode, branch_dest });
213209
cur_addr += 4;
214210
}
215211
Ok(ops)
@@ -225,16 +221,7 @@ impl Arch for ArchMips {
225221
let display_flags = self.instruction_display_flags(diff_config);
226222
let opcode = instruction.opcode();
227223
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
228-
let start_address = resolved.symbol.address;
229-
let function_range = start_address..start_address + resolved.symbol.size;
230-
push_args(
231-
&instruction,
232-
resolved.relocation,
233-
function_range,
234-
resolved.section_index,
235-
&display_flags,
236-
cb,
237-
)?;
224+
push_args(&instruction, resolved.relocation, &display_flags, cb)?;
238225
Ok(())
239226
}
240227

@@ -338,8 +325,6 @@ impl Arch for ArchMips {
338325
fn push_args(
339326
instruction: &rabbitizer::Instruction,
340327
relocation: Option<ResolvedRelocation>,
341-
function_range: Range<u64>,
342-
section_index: usize,
343328
display_flags: &rabbitizer::InstructionDisplayFlags,
344329
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
345330
) -> Result<()> {
@@ -362,23 +347,7 @@ fn push_args(
362347
}
363348
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
364349
if let Some(resolved) = relocation {
365-
// If the relocation target is within the current function, we can
366-
// convert it into a relative branch target. Note that we check
367-
// target_address > start_address instead of >= so that recursive
368-
// tail calls are not considered branch targets.
369-
let target_address =
370-
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
371-
if resolved.symbol.section == Some(section_index)
372-
&& target_address.is_some_and(|addr| {
373-
addr > function_range.start && addr < function_range.end
374-
})
375-
{
376-
// TODO move this logic up a level
377-
let target_address = target_address.unwrap();
378-
arg_cb(InstructionPart::branch_dest(target_address))?;
379-
} else {
380-
push_reloc(resolved.relocation, &mut arg_cb)?;
381-
}
350+
push_reloc(resolved.relocation, &mut arg_cb)?;
382351
} else if let Some(branch_dest) = instruction
383352
.get_branch_offset_generic()
384353
.map(|o| (instruction.vram() + o).inner() as u64)

0 commit comments

Comments
 (0)