From 94c4219a3830a189983a0a91cbfaf62dba64fab6 Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Tue, 2 Jun 2026 13:15:27 -0400 Subject: [PATCH 1/3] ARM: Fix relocations being ignored when inferring function sizes --- objdiff-core/src/obj/read.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 6046070d..5cfb6d73 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -181,7 +181,6 @@ fn map_symbol( fn map_symbols( arch: &dyn Arch, obj_file: &object::File, - sections: &[Section], section_indices: &[usize], split_meta: Option<&SplitMeta>, config: &DiffObjConfig, @@ -223,9 +222,6 @@ fn map_symbols( symbols.push(symbol); } - // Infer symbol sizes for 0-size symbols - infer_symbol_sizes(arch, &mut symbols, sections)?; - Ok((symbols, symbol_indices)) } @@ -286,7 +282,7 @@ fn is_local_label(symbol: &Symbol) -> bool { } fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> { - // Above, we've sorted the symbols by section and then by address. + // Above, we've sorted the symbols by section and then by address, and also mapped section relocations. // Set symbol sizes based on the next symbol's address let mut iter_idx = 0; @@ -1075,15 +1071,11 @@ pub fn parse(data: &[u8], config: &DiffObjConfig, diff_side: DiffSide) -> Result let split_meta = parse_split_meta(&obj_file)?; let (mut sections, section_indices) = map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?; - let (mut symbols, symbol_indices) = map_symbols( - arch.as_ref(), - &obj_file, - §ions, - §ion_indices, - split_meta.as_ref(), - config, - )?; + let (mut symbols, symbol_indices) = + map_symbols(arch.as_ref(), &obj_file, §ion_indices, split_meta.as_ref(), config)?; map_relocations(arch.as_ref(), &obj_file, &mut sections, §ion_indices, &symbol_indices)?; + // Infer symbol sizes for 0-size symbols (must be done after map_relocations is called) + infer_symbol_sizes(arch.as_ref(), &mut symbols, §ions)?; parse_line_info(&obj_file, &mut sections, §ion_indices, data)?; if config.combine_data_sections || config.combine_text_sections { combine_sections(&mut sections, &mut symbols, config)?; From dc9b997227887762ce4dbd7ee9d3761ce2a90454 Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Tue, 2 Jun 2026 13:19:46 -0400 Subject: [PATCH 2/3] ARM: Add test for trailing relocations --- objdiff-core/tests/arch_arm.rs | 17 ++ objdiff-core/tests/data/arm/fake_tank.o | Bin 0 -> 9144 bytes ...m__do_not_trim_trailing_relocations-2.snap | 15 ++ ...arm__do_not_trim_trailing_relocations.snap | 160 ++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 objdiff-core/tests/data/arm/fake_tank.o create mode 100644 objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap create mode 100644 objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap diff --git a/objdiff-core/tests/arch_arm.rs b/objdiff-core/tests/arch_arm.rs index 9c507e1d..9756f9cd 100644 --- a/objdiff-core/tests/arch_arm.rs +++ b/objdiff-core/tests/arch_arm.rs @@ -98,3 +98,20 @@ fn trim_trailing_hword() { let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); insta::assert_snapshot!(output); } + +#[test] +#[cfg(feature = "arm")] +fn do_not_trim_trailing_relocations() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse( + include_object!("data/arm/fake_tank.o"), + &diff_config, + diff::DiffSide::Base, + ) + .unwrap(); + let symbol_idx = obj.symbols.iter().position(|s| s.name == "FakeTankIdleInit").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/arm/fake_tank.o b/objdiff-core/tests/data/arm/fake_tank.o new file mode 100644 index 0000000000000000000000000000000000000000..2e986f4cafcb6a2c49001247b5ab38eaf3a60c63 GIT binary patch literal 9144 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 N^VKiRom{2gzX2CJ>97C* literal 0 HcmV?d00001 diff --git a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap new file mode 100644 index 00000000..aeeb9050 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap @@ -0,0 +1,15 @@ +--- +source: objdiff-core/tests/arch_arm.rs +expression: output +--- +[(Address(0), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 24), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(16)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(20), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(2), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 43), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)] +[(Address(4), Dim, 5), (Spacing(4), Normal, 0), (Opcode("add", 1), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(36)), Normal, 0), (Eol, Normal, 0)] +[(Address(6), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 43), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)] +[(Address(8), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 43), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)] +[(Address(10), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strb", 117), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)] +[(Address(12), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strb", 117), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(28)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)] +[(Address(14), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strh", 124), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(22)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)] +[(Address(16), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bx", 9), Normal, 10), (Argument(Opaque("lr")), Normal, 0), (Eol, Normal, 0)] +[(Address(18), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".hword", 65534), Normal, 10), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)] +[(Address(20), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".hword", 65534), Normal, 10), (Symbol(Symbol { name: "gCurrentSprite", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap new file mode 100644 index 00000000..7d1a6be4 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap @@ -0,0 +1,160 @@ +--- +source: objdiff-core/tests/arch_arm.rs +expression: diff.instruction_rows +--- +[ + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 176, + size: 2, + opcode: 24, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 178, + size: 2, + opcode: 43, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 180, + size: 2, + opcode: 1, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 182, + size: 2, + opcode: 43, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 184, + size: 2, + opcode: 43, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 186, + size: 2, + opcode: 117, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 188, + size: 2, + opcode: 117, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 190, + size: 2, + opcode: 124, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 192, + size: 2, + opcode: 9, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 194, + size: 2, + opcode: 65534, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 196, + size: 2, + opcode: 65534, + branch_dest: None, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, +] From 206a71239c608fb792e11312b1c399dfcc784f46 Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Tue, 2 Jun 2026 14:02:00 -0400 Subject: [PATCH 3/3] ARM: Fix trailing relocations being cut in half --- objdiff-core/src/arch/arm.rs | 6 +++++- .../arch_arm__do_not_trim_trailing_relocations-2.snap | 2 +- .../arch_arm__do_not_trim_trailing_relocations.snap | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index 35416c9f..d8fdd5e2 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -471,9 +471,13 @@ impl Arch for ArchArm { while next_address >= symbol.address + 2 && let Some(data) = section.data_range(next_address - 2, 2) && data == [0u8; 2] - && section.relocation_at(next_address - 2, 2).is_none() { next_address -= 2; + if let Some(relocation) = section.relocation_at(next_address, 2) { + // Avoid cutting trailing relocations in half. + next_address += self.data_reloc_size(relocation.flags) as u64; + break; + } } Ok(next_address.saturating_sub(symbol.address)) } diff --git a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap index aeeb9050..8346e010 100644 --- a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap +++ b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations-2.snap @@ -12,4 +12,4 @@ expression: output [(Address(14), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strh", 124), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(22)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)] [(Address(16), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bx", 9), Normal, 10), (Argument(Opaque("lr")), Normal, 0), (Eol, Normal, 0)] [(Address(18), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".hword", 65534), Normal, 10), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)] -[(Address(20), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".hword", 65534), Normal, 10), (Symbol(Symbol { name: "gCurrentSprite", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)] +[(Address(20), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "gCurrentSprite", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap index 7d1a6be4..b4d238f6 100644 --- a/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap +++ b/objdiff-core/tests/snapshots/arch_arm__do_not_trim_trailing_relocations.snap @@ -147,7 +147,7 @@ expression: diff.instruction_rows ins_ref: Some( InstructionRef { address: 196, - size: 2, + size: 4, opcode: 65534, branch_dest: None, },