Skip to content

Commit c6971f3

Browse files
committed
Add initial support for x86-64 relocations
1 parent 3965a03 commit c6971f3

File tree

7 files changed

+1914
-34
lines changed

7 files changed

+1914
-34
lines changed

objdiff-core/src/arch/x86.rs

Lines changed: 90 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,36 @@ use crate::{
1515

1616
#[derive(Debug)]
1717
pub struct ArchX86 {
18-
bits: u32,
18+
arch: Architecture,
1919
endianness: object::Endianness,
2020
}
2121

22+
#[derive(Debug)]
23+
enum Architecture {
24+
X86,
25+
X86_64,
26+
}
27+
2228
impl ArchX86 {
2329
pub fn new(object: &object::File) -> Result<Self> {
24-
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
30+
let arch = match object.architecture() {
31+
object::Architecture::I386 => Architecture::X86,
32+
object::Architecture::X86_64 => Architecture::X86_64,
33+
_ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()),
34+
};
35+
Ok(Self { arch, endianness: object.endianness() })
2536
}
2637

2738
fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> {
28-
Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE)
39+
Decoder::with_ip(
40+
match self.arch {
41+
Architecture::X86 => 32,
42+
Architecture::X86_64 => 64,
43+
},
44+
code,
45+
address,
46+
DecoderOptions::NONE,
47+
)
2948
}
3049

3150
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
@@ -38,6 +57,27 @@ impl ArchX86 {
3857
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
3958
formatter
4059
}
60+
61+
fn reloc_size(&self, flags: RelocationFlags) -> Option<usize> {
62+
match self.arch {
63+
Architecture::X86 => match flags {
64+
RelocationFlags::Coff(typ) => match typ {
65+
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
66+
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
67+
_ => None,
68+
},
69+
_ => None,
70+
},
71+
Architecture::X86_64 => match flags {
72+
RelocationFlags::Coff(typ) => match typ {
73+
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
74+
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
75+
_ => None,
76+
},
77+
_ => None,
78+
},
79+
}
80+
}
4181
}
4282

4383
impl Arch for ArchX86 {
@@ -88,10 +128,9 @@ impl Arch for ArchX86 {
88128
// memory operand.
89129
let mut reloc_replace = None;
90130
if let Some(reloc) = resolved.relocation {
91-
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll.
92-
// guaranteed to be random.
131+
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random.
93132
let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
94-
let reloc_size = reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
133+
let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
95134
let offsets = decoder.get_constant_offsets(&instruction);
96135
if reloc_offset == offsets.displacement_offset() as u64
97136
&& reloc_size == offsets.displacement_size()
@@ -148,12 +187,28 @@ impl Arch for ArchX86 {
148187
_relocation: &object::Relocation,
149188
flags: RelocationFlags,
150189
) -> Result<i64> {
151-
match flags {
152-
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
153-
let data = section.data()?[address as usize..address as usize + 4].try_into()?;
154-
Ok(self.endianness.read_i32_bytes(data) as i64)
155-
}
156-
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
190+
match self.arch {
191+
Architecture::X86 => match flags {
192+
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
193+
let data =
194+
section.data()?[address as usize..address as usize + 4].try_into()?;
195+
Ok(self.endianness.read_i32_bytes(data) as i64)
196+
}
197+
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
198+
},
199+
Architecture::X86_64 => match flags {
200+
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => {
201+
let data =
202+
section.data()?[address as usize..address as usize + 4].try_into()?;
203+
Ok(self.endianness.read_i32_bytes(data) as i64)
204+
}
205+
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => {
206+
let data =
207+
section.data()?[address as usize..address as usize + 8].try_into()?;
208+
Ok(self.endianness.read_i64_bytes(data))
209+
}
210+
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
211+
},
157212
}
158213
}
159214

@@ -168,27 +223,29 @@ impl Arch for ArchX86 {
168223
}
169224

170225
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
171-
match flags {
172-
RelocationFlags::Coff(typ) => match typ {
173-
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
174-
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
226+
match self.arch {
227+
Architecture::X86 => match flags {
228+
RelocationFlags::Coff(typ) => match typ {
229+
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
230+
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
231+
_ => None,
232+
},
233+
_ => None,
234+
},
235+
Architecture::X86_64 => match flags {
236+
RelocationFlags::Coff(typ) => match typ {
237+
pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
238+
pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
239+
pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
240+
_ => None,
241+
},
175242
_ => None,
176243
},
177-
_ => None,
178244
}
179245
}
180246

181-
fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) }
182-
}
183-
184-
fn reloc_size(flags: RelocationFlags) -> Option<usize> {
185-
match flags {
186-
RelocationFlags::Coff(typ) => match typ {
187-
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
188-
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
189-
_ => None,
190-
},
191-
_ => None,
247+
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
248+
self.reloc_size(flags).unwrap_or(1)
192249
}
193250
}
194251

@@ -343,7 +400,7 @@ mod test {
343400

344401
#[test]
345402
fn test_scan_instructions() {
346-
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little };
403+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
347404
let code = [
348405
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
349406
0x00, 0x00, 0x00,
@@ -362,7 +419,7 @@ mod test {
362419

363420
#[test]
364421
fn test_process_instruction() {
365-
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little };
422+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
366423
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
367424
let opcode = iced_x86::Mnemonic::Mov as u16;
368425
let mut parts = Vec::new();
@@ -398,7 +455,7 @@ mod test {
398455

399456
#[test]
400457
fn test_process_instruction_with_reloc_1() {
401-
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little };
458+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
402459
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
403460
let opcode = iced_x86::Mnemonic::Mov as u16;
404461
let mut parts = Vec::new();
@@ -443,7 +500,7 @@ mod test {
443500

444501
#[test]
445502
fn test_process_instruction_with_reloc_2() {
446-
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little };
503+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
447504
let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00];
448505
let opcode = iced_x86::Mnemonic::Mov as u16;
449506
let mut parts = Vec::new();
@@ -486,7 +543,7 @@ mod test {
486543

487544
#[test]
488545
fn test_process_instruction_with_reloc_3() {
489-
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little };
546+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
490547
let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
491548
let opcode = iced_x86::Mnemonic::Call as u16;
492549
let mut parts = Vec::new();

objdiff-core/tests/arch_x86.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,17 @@ fn read_x86_combine_sections() {
2626
let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap();
2727
insta::assert_debug_snapshot!(obj.sections);
2828
}
29+
30+
#[test]
31+
#[cfg(feature = "x86")]
32+
fn read_x86_64() {
33+
let diff_config = diff::DiffObjConfig::default();
34+
let obj = obj::read::parse(include_object!("data/x86_64/vs2022.o"), &diff_config).unwrap();
35+
insta::assert_debug_snapshot!(obj);
36+
let symbol_idx =
37+
obj.symbols.iter().position(|s| s.name == "?Dot@Vector@@QEAAMPEAU1@@Z").unwrap();
38+
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
39+
insta::assert_debug_snapshot!(diff.instruction_rows);
40+
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
41+
insta::assert_snapshot!(output);
42+
}
5.57 KB
Binary file not shown.

objdiff-core/tests/snapshots/arch_x86__read_x86.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ expression: obj
44
---
55
Object {
66
arch: ArchX86 {
7-
bits: 32,
7+
arch: X86,
88
endianness: Little,
99
},
1010
endianness: Little,

0 commit comments

Comments
 (0)