From c6971f3f2df6417b41a33add4c2c6385a850c30b Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 9 Mar 2025 22:45:29 -0600 Subject: [PATCH] Add initial support for x86-64 relocations --- objdiff-core/src/arch/x86.rs | 123 +- objdiff-core/tests/arch_x86.rs | 14 + objdiff-core/tests/data/x86_64/vs2022.o | Bin 0 -> 5703 bytes .../tests/snapshots/arch_x86__read_x86.snap | 2 +- .../snapshots/arch_x86__read_x86_64-2.snap | 279 +++ .../snapshots/arch_x86__read_x86_64-3.snap | 25 + .../snapshots/arch_x86__read_x86_64.snap | 1505 +++++++++++++++++ 7 files changed, 1914 insertions(+), 34 deletions(-) create mode 100644 objdiff-core/tests/data/x86_64/vs2022.o create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86_64-2.snap create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86_64-3.snap create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86_64.snap diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index c17ab2d..87db44f 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -15,17 +15,36 @@ use crate::{ #[derive(Debug)] pub struct ArchX86 { - bits: u32, + arch: Architecture, endianness: object::Endianness, } +#[derive(Debug)] +enum Architecture { + X86, + X86_64, +} + impl ArchX86 { pub fn new(object: &object::File) -> Result { - Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() }) + let arch = match object.architecture() { + object::Architecture::I386 => Architecture::X86, + object::Architecture::X86_64 => Architecture::X86_64, + _ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()), + }; + Ok(Self { arch, endianness: object.endianness() }) } fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> { - Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE) + Decoder::with_ip( + match self.arch { + Architecture::X86 => 32, + Architecture::X86_64 => 64, + }, + code, + address, + DecoderOptions::NONE, + ) } fn formatter(&self, diff_config: &DiffObjConfig) -> Box { @@ -38,6 +57,27 @@ impl ArchX86 { formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args); formatter } + + fn reloc_size(&self, flags: RelocationFlags) -> Option { + match self.arch { + Architecture::X86 => match flags { + RelocationFlags::Coff(typ) => match typ { + pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2), + pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4), + _ => None, + }, + _ => None, + }, + Architecture::X86_64 => match flags { + RelocationFlags::Coff(typ) => match typ { + pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4), + pe::IMAGE_REL_AMD64_ADDR64 => Some(8), + _ => None, + }, + _ => None, + }, + } + } } impl Arch for ArchX86 { @@ -88,10 +128,9 @@ impl Arch for ArchX86 { // memory operand. let mut reloc_replace = None; if let Some(reloc) = resolved.relocation { - const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. - // guaranteed to be random. + const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random. let reloc_offset = reloc.relocation.address - resolved.ins_ref.address; - let reloc_size = reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX); + let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX); let offsets = decoder.get_constant_offsets(&instruction); if reloc_offset == offsets.displacement_offset() as u64 && reloc_size == offsets.displacement_size() @@ -148,12 +187,28 @@ impl Arch for ArchX86 { _relocation: &object::Relocation, flags: RelocationFlags, ) -> Result { - match flags { - RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { - let data = section.data()?[address as usize..address as usize + 4].try_into()?; - Ok(self.endianness.read_i32_bytes(data) as i64) - } - flags => bail!("Unsupported x86 implicit relocation {flags:?}"), + match self.arch { + Architecture::X86 => match flags { + RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { + let data = + section.data()?[address as usize..address as usize + 4].try_into()?; + Ok(self.endianness.read_i32_bytes(data) as i64) + } + flags => bail!("Unsupported x86 implicit relocation {flags:?}"), + }, + Architecture::X86_64 => match flags { + RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => { + let data = + section.data()?[address as usize..address as usize + 4].try_into()?; + Ok(self.endianness.read_i32_bytes(data) as i64) + } + RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => { + let data = + section.data()?[address as usize..address as usize + 8].try_into()?; + Ok(self.endianness.read_i64_bytes(data)) + } + flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"), + }, } } @@ -168,27 +223,29 @@ impl Arch for ArchX86 { } fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> { - match flags { - RelocationFlags::Coff(typ) => match typ { - pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"), - pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"), + match self.arch { + Architecture::X86 => match flags { + RelocationFlags::Coff(typ) => match typ { + pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"), + pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"), + _ => None, + }, + _ => None, + }, + Architecture::X86_64 => match flags { + RelocationFlags::Coff(typ) => match typ { + pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"), + pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"), + pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"), + _ => None, + }, _ => None, }, - _ => None, } } - fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) } -} - -fn reloc_size(flags: RelocationFlags) -> Option { - match flags { - RelocationFlags::Coff(typ) => match typ { - pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2), - pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4), - _ => None, - }, - _ => None, + fn data_reloc_size(&self, flags: RelocationFlags) -> usize { + self.reloc_size(flags).unwrap_or(1) } } @@ -343,7 +400,7 @@ mod test { #[test] fn test_scan_instructions() { - let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little }; let code = [ 0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00, @@ -362,7 +419,7 @@ mod test { #[test] fn test_process_instruction() { - let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little }; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let opcode = iced_x86::Mnemonic::Mov as u16; let mut parts = Vec::new(); @@ -398,7 +455,7 @@ mod test { #[test] fn test_process_instruction_with_reloc_1() { - let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little }; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let opcode = iced_x86::Mnemonic::Mov as u16; let mut parts = Vec::new(); @@ -443,7 +500,7 @@ mod test { #[test] fn test_process_instruction_with_reloc_2() { - let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little }; let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00]; let opcode = iced_x86::Mnemonic::Mov as u16; let mut parts = Vec::new(); @@ -486,7 +543,7 @@ mod test { #[test] fn test_process_instruction_with_reloc_3() { - let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little }; let code = [0xe8, 0x00, 0x00, 0x00, 0x00]; let opcode = iced_x86::Mnemonic::Call as u16; let mut parts = Vec::new(); diff --git a/objdiff-core/tests/arch_x86.rs b/objdiff-core/tests/arch_x86.rs index da0e249..717e2b9 100644 --- a/objdiff-core/tests/arch_x86.rs +++ b/objdiff-core/tests/arch_x86.rs @@ -26,3 +26,17 @@ fn read_x86_combine_sections() { let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap(); insta::assert_debug_snapshot!(obj.sections); } + +#[test] +#[cfg(feature = "x86")] +fn read_x86_64() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse(include_object!("data/x86_64/vs2022.o"), &diff_config).unwrap(); + insta::assert_debug_snapshot!(obj); + let symbol_idx = + obj.symbols.iter().position(|s| s.name == "?Dot@Vector@@QEAAMPEAU1@@Z").unwrap(); + let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap(); + insta::assert_debug_snapshot!(diff.instruction_rows); + let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); + insta::assert_snapshot!(output); +} diff --git a/objdiff-core/tests/data/x86_64/vs2022.o b/objdiff-core/tests/data/x86_64/vs2022.o new file mode 100644 index 0000000000000000000000000000000000000000..646b8ce54069188b311922c011ae2effbcd1f62a GIT binary patch literal 5703 zcmd5=e{2)i9eG@*e~iFC=(@DT%f zK5^;Dq)B$o7?&cz_XFn%&I2>x%{|WF0nRg=N8=*zfEw5K#aR%j9q^px90M$H6IV)$ zNOkplbqt5&Rp9-Ob7@?MEK^><%9{et`?Gk~{$l^Ga*iH@j^B)NLEg2wc#+Po^h00J zP02V&AJ5GN@|n17%jQ(dTKXso(A|bJB3&>f7)%5n&0u zNbROzOIk^#gPlq|t_FM5XwakfB;sl~nhw@>Dv_?nNF<&~$J9hHpsMk7s5YETXW~jg zNo)1t4*&jOM2#vgm24tgmI(U@d4nt?Yl+%VjCXx3l2X%ZyXGu!UgvB~YO(Iv!C0ch zS*vy@V{s(~c}&}v1SO|$&?6Ol2LqDBJLr@0_jvoSjDylU+$TBDmVZet9Gf+su%*dm$l@)N6Oq><{KUO{Q8HXu;RUg44YQoEoTl=?6e)*IgO;Um&v$ekqvU1jN+K(I4L zBV{`!T+XQ)_YQsP9XjtFUJjPJ>B~4kbWGQuXWsfJ$3Y#M_#)IT@Yv{=WEqwEpk~r2 zNVt=2lA`v7W;Vk4hGy_-s9 z5lE}3WL`G|SDfAr0^XAp_b_x4z^Zm~@6FtjW-zkP)l`mQ0&~!3@L;8MM_w1?CnU;?aUkz^a1~v@i*CMeswem-|5wVVVhHQa(s)C`K)+m<1jY^zr}*%z{a1v5sPbLm&uf3j0a zDcIJOZXR9N5^@+sN47~?XSB;_v`y6i3lTrA=CTnQUxG&{7bgg8yqSdKy^UhGpxg(N49_u>1vE)9JXUC>33I!h`zzo$BxpY(J}*x_IxSXFWCFn8v)7_JgN;QP6j6UswEFi~}(JEx|V&kf)2joAiJ;$#bzL#3?R7%a)={&26B=k zqJg~4k!-B1vyhv!ki~)y=^2(-5XslDUT5HlK7EU^lxl^$zN>z*;2 z5vzw=QH~`R+*M7n3Xg7mC2Mt@TTzZB7R}A**QVrnG}nmrD{jT|H}~rvORS?~CtlB5 z{gqo$jwRN;mVT`|*X++TVlBo+3x#ql{lYzz9mBDIi7T>J8@Ltaz*QxCwZN~4DBuqs zu7K4OCya|WAyIBcIiMxvI3U={B5wkM-`Ff-hbeShkUBvAVIbz!f`>6tz=ItQM(Nm> zPs59sCvYi1p&Uz3@z9s;>80z{Pg5eTUgTDkL(G>qJno4C=85IGvim<`QV4$Q;HQoj z%CTJKFgLcI`E)^>4-5Gxk3~7mieK#TFnT+yd>m*flw+9-{33}0S5rA8AA$dU)RZjm-d++N7XFKdZwhRSX%S%zn#{f*=JGd zUeS_83(@OhCY(~DvfNffJ?nWOYwqhMQbP@xh0b-;Ohe6q+7P{<`n7PR>ydCOO(<5E zh-urxX@#}?of$0(7g`bu?eu%Yi6~q*sTBZeC6Y