Skip to content

Commit e735adb

Browse files
committed
x86: Support inline data for jumptables
1 parent 6768df9 commit e735adb

File tree

12 files changed

+995
-8
lines changed

12 files changed

+995
-8
lines changed

objdiff-core/src/arch/arm.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
arch::Arch,
1515
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
1616
obj::{
17-
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
17+
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
1818
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
1919
},
2020
};
@@ -183,6 +183,7 @@ impl Arch for ArchArm {
183183
address: u64,
184184
code: &[u8],
185185
section_index: usize,
186+
_relocations: &[Relocation],
186187
diff_config: &DiffObjConfig,
187188
) -> Result<Vec<ScannedInstruction>> {
188189
let start_addr = address as u32;

objdiff-core/src/arch/arm64.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
arch::Arch,
1818
diff::{DiffObjConfig, display::InstructionPart},
1919
obj::{
20-
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
20+
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
2121
ScannedInstruction,
2222
},
2323
};
@@ -35,6 +35,7 @@ impl Arch for ArchArm64 {
3535
address: u64,
3636
code: &[u8],
3737
_section_index: usize,
38+
_relocations: &[Relocation],
3839
_diff_config: &DiffObjConfig,
3940
) -> Result<Vec<ScannedInstruction>> {
4041
let start_address = address;

objdiff-core/src/arch/mips.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ impl Arch for ArchMips {
194194
address: u64,
195195
code: &[u8],
196196
_section_index: usize,
197+
_relocations: &[Relocation],
197198
diff_config: &DiffObjConfig,
198199
) -> Result<Vec<ScannedInstruction>> {
199200
let instruction_flags = self.instruction_flags(diff_config);

objdiff-core/src/arch/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ pub trait Arch: Send + Sync + Debug {
170170
address: u64,
171171
code: &[u8],
172172
section_index: usize,
173+
relocations: &[Relocation],
173174
diff_config: &DiffObjConfig,
174175
) -> Result<Vec<ScannedInstruction>>;
175176

@@ -301,6 +302,7 @@ impl Arch for ArchDummy {
301302
_address: u64,
302303
_code: &[u8],
303304
_section_index: usize,
305+
_relocations: &[Relocation],
304306
_diff_config: &DiffObjConfig,
305307
) -> Result<Vec<ScannedInstruction>> {
306308
Ok(Vec::new())

objdiff-core/src/arch/ppc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl Arch for ArchPpc {
8787
address: u64,
8888
code: &[u8],
8989
_section_index: usize,
90+
_relocations: &[Relocation],
9091
_diff_config: &DiffObjConfig,
9192
) -> Result<Vec<ScannedInstruction>> {
9293
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");

objdiff-core/src/arch/x86.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloc::{boxed::Box, string::String, vec::Vec};
22

3-
use anyhow::{Result, anyhow, bail};
3+
use anyhow::{Context, Result, anyhow, bail};
44
use iced_x86::{
55
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
66
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
@@ -10,7 +10,9 @@ use object::{Endian as _, Object as _, ObjectSection as _, pe};
1010
use crate::{
1111
arch::Arch,
1212
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
13-
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
13+
obj::{
14+
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ScannedInstruction,
15+
},
1416
};
1517

1618
#[derive(Debug)]
@@ -80,18 +82,48 @@ impl ArchX86 {
8082
}
8183
}
8284

85+
const DATA_OPCODE: u16 = u16::MAX - 1;
86+
8387
impl Arch for ArchX86 {
8488
fn scan_instructions(
8589
&self,
8690
address: u64,
8791
code: &[u8],
8892
_section_index: usize,
93+
relocations: &[Relocation],
8994
_diff_config: &DiffObjConfig,
9095
) -> Result<Vec<ScannedInstruction>> {
9196
let mut out = Vec::with_capacity(code.len() / 2);
9297
let mut decoder = self.decoder(code, address);
9398
let mut instruction = Instruction::default();
94-
while decoder.can_decode() {
99+
let mut reloc_iter = relocations.iter().peekable();
100+
'outer: while decoder.can_decode() {
101+
let address = decoder.ip();
102+
while let Some(reloc) = reloc_iter.peek() {
103+
if reloc.address < address {
104+
reloc_iter.next();
105+
} else if reloc.address == address {
106+
// If the instruction starts at a relocation, it's inline data
107+
let size = self.reloc_size(reloc.flags).with_context(|| {
108+
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
109+
})?;
110+
if decoder.set_position(decoder.position() + size).is_ok() {
111+
decoder.set_ip(address + size as u64);
112+
out.push(ScannedInstruction {
113+
ins_ref: InstructionRef {
114+
address,
115+
size: size as u8,
116+
opcode: DATA_OPCODE,
117+
},
118+
branch_dest: None,
119+
});
120+
reloc_iter.next();
121+
continue 'outer;
122+
}
123+
} else {
124+
break;
125+
}
126+
}
95127
decoder.decode_out(&mut instruction);
96128
let branch_dest = match instruction.op0_kind() {
97129
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
@@ -101,7 +133,7 @@ impl Arch for ArchX86 {
101133
};
102134
out.push(ScannedInstruction {
103135
ins_ref: InstructionRef {
104-
address: instruction.ip(),
136+
address,
105137
size: instruction.len() as u8,
106138
opcode: instruction.mnemonic() as u16,
107139
},
@@ -117,6 +149,21 @@ impl Arch for ArchX86 {
117149
diff_config: &DiffObjConfig,
118150
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
119151
) -> Result<()> {
152+
if resolved.ins_ref.opcode == DATA_OPCODE {
153+
let (mnemonic, imm) = match resolved.ins_ref.size {
154+
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
155+
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
156+
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
157+
};
158+
cb(InstructionPart::opcode(mnemonic, DATA_OPCODE))?;
159+
if resolved.relocation.is_some() {
160+
cb(InstructionPart::reloc())?;
161+
} else {
162+
cb(InstructionPart::unsigned(imm))?;
163+
}
164+
return Ok(());
165+
}
166+
120167
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
121168
let mut formatter = self.formatter(diff_config);
122169
let mut instruction = Instruction::default();
@@ -406,7 +453,7 @@ mod test {
406453
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
407454
0x00, 0x00, 0x00,
408455
];
409-
let scanned = arch.scan_instructions(0, &code, 0, &DiffObjConfig::default()).unwrap();
456+
let scanned = arch.scan_instructions(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
410457
assert_eq!(scanned.len(), 2);
411458
assert_eq!(scanned[0].ins_ref.address, 0);
412459
assert_eq!(scanned[0].ins_ref.size, 10);

objdiff-core/src/diff/code.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ pub fn no_diff_code(
3232
symbol.address + symbol.size
3333
)
3434
})?;
35-
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
35+
let ops = obj.arch.scan_instructions(
36+
symbol.address,
37+
data,
38+
section_index,
39+
&section.relocations,
40+
diff_config,
41+
)?;
3642
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
3743
for i in &ops {
3844
instruction_rows
@@ -89,12 +95,14 @@ pub fn diff_code(
8995
left_symbol.address,
9096
left_data,
9197
left_section_idx,
98+
&left_section.relocations,
9299
diff_config,
93100
)?;
94101
let right_ops = right_obj.arch.scan_instructions(
95102
right_symbol.address,
96103
right_data,
97104
right_section_idx,
105+
&right_section.relocations,
98106
diff_config,
99107
)?;
100108
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;

objdiff-core/tests/arch_x86.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,16 @@ fn display_section_ordering() {
5555
diff::display::display_sections(&obj, &obj_diff, SymbolFilter::None, false, false, false);
5656
insta::assert_debug_snapshot!(section_display);
5757
}
58+
59+
#[test]
60+
#[cfg(feature = "x86")]
61+
fn read_x86_jumptable() {
62+
let diff_config = diff::DiffObjConfig::default();
63+
let obj = obj::read::parse(include_object!("data/x86/jumptable.o"), &diff_config).unwrap();
64+
insta::assert_debug_snapshot!(obj);
65+
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?test@@YAHH@Z").unwrap();
66+
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
67+
insta::assert_debug_snapshot!(diff.instruction_rows);
68+
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
69+
insta::assert_snapshot!(output);
70+
}
722 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)