Skip to content

Commit e2b98eb

Browse files
Fix ARM trailing data directive size
1 parent 9c1dad4 commit e2b98eb

5 files changed

Lines changed: 62 additions & 8 deletions

File tree

‎objdiff-core/src/arch/arm.rs‎

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ impl ArchArm {
162162
}
163163

164164
impl Arch for ArchArm {
165+
fn pre_init(&mut self, sections: &[Section], symbols: &[Symbol], _symbol_indices: &[usize]) {
166+
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
167+
}
168+
165169
fn post_init(&mut self, sections: &[Section], symbols: &[Symbol], _symbol_indices: &[usize]) {
166170
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
167171
}
@@ -474,20 +478,24 @@ impl Arch for ArchArm {
474478
section: &Section,
475479
mut next_address: u64,
476480
) -> Result<u64> {
477-
// TODO: This should probably check the disasm mode and trim accordingly,
478-
// but self.disasm_modes isn't populated until post_init, so it needs a refactor.
479-
480481
// Trim any trailing 2-byte zeroes from the end (padding)
481482
while next_address >= symbol.address + 2
483+
&& !symbol.section.is_some_and(|section_idx| {
484+
self.disasm_modes
485+
.get(&section_idx)
486+
.and_then(|mappings| {
487+
mappings.iter().rfind(|mapping| mapping.address as u64 <= next_address - 2)
488+
})
489+
.is_some_and(|mapping| mapping.ends_with_complete_data_words(next_address))
490+
})
482491
&& let Some(data) = section.data_range(next_address - 2, 2)
483492
&& data == [0u8; 2]
493+
&& !section.relocation_at(next_address.saturating_sub(4), 4).is_some_and(|relocation| {
494+
relocation.address + self.data_reloc_size(relocation.flags) as u64
495+
> next_address - 2
496+
})
484497
{
485498
next_address -= 2;
486-
if let Some(relocation) = section.relocation_at(next_address, 2) {
487-
// Avoid cutting trailing relocations in half.
488-
next_address += self.data_reloc_size(relocation.flags) as u64;
489-
break;
490-
}
491499
}
492500
Ok(next_address.saturating_sub(symbol.address))
493501
}
@@ -500,6 +508,11 @@ struct DisasmMode {
500508
}
501509

502510
impl DisasmMode {
511+
fn ends_with_complete_data_words(self, end_address: u64) -> bool {
512+
let data_size = end_address.saturating_sub(self.address as u64);
513+
self.mapping == unarm::ParseMode::Data && data_size >= 4 && data_size.is_multiple_of(4)
514+
}
515+
503516
fn from_object_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
504517
sym.name()
505518
.ok()
@@ -645,3 +658,18 @@ impl unarm::FormatIns for ArgsFormatter<'_> {
645658
Ok(())
646659
}
647660
}
661+
662+
#[cfg(test)]
663+
mod tests {
664+
use super::DisasmMode;
665+
666+
#[test]
667+
fn complete_data_words_exclude_trailing_halfword_padding() {
668+
let mapping = DisasmMode { address: 0x1000, mapping: unarm::ParseMode::Data };
669+
670+
assert!(!mapping.ends_with_complete_data_words(0x1002));
671+
assert!(mapping.ends_with_complete_data_words(0x1004));
672+
assert!(!mapping.ends_with_complete_data_words(0x1006));
673+
assert!(mapping.ends_with_complete_data_words(0x1008));
674+
}
675+
}

‎objdiff-core/src/arch/mod.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ impl dyn Arch {
390390
}
391391

392392
pub trait Arch: Any + Debug + Send + Sync {
393+
/// Performs arch-specific initialization needed before inferring zero-sized symbols.
394+
fn pre_init(&mut self, _sections: &[Section], _symbols: &[Symbol], _symbol_indices: &[usize]) {}
395+
393396
/// Finishes arch-specific initialization that must be done after sections have been combined.
394397
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol], _symbol_indices: &[usize]) {
395398
}

‎objdiff-core/src/obj/read.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig, diff_side: DiffSide) -> Result
10761076
let (mut symbols, symbol_indices) =
10771077
map_symbols(arch.as_ref(), &obj_file, &section_indices, split_meta.as_ref(), config)?;
10781078
map_relocations(arch.as_ref(), &obj_file, &mut sections, &section_indices, &symbol_indices)?;
1079+
arch.pre_init(&sections, &symbols, &symbol_indices);
10791080
// Infer symbol sizes for 0-size symbols (must be done after map_relocations is called)
10801081
infer_symbol_sizes(arch.as_ref(), &mut symbols, &sections)?;
10811082
parse_line_info(&obj_file, &mut sections, &section_indices, data)?;

‎objdiff-core/tests/arch_arm.rs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ fn trim_trailing_hword() {
115115
insta::assert_snapshot!(output);
116116
}
117117

118+
#[test]
119+
#[cfg(feature = "arm")]
120+
fn preserve_trailing_data_directive() {
121+
let diff_config = diff::DiffObjConfig::default();
122+
let obj = obj::read::parse(
123+
include_object!("data/arm/issue_382.o"),
124+
&diff_config,
125+
diff::DiffSide::Base,
126+
)
127+
.unwrap();
128+
let symbol_idx = obj.symbols.iter().position(|s| s.name == "sub_08014184").unwrap();
129+
let symbol = &obj.symbols[symbol_idx];
130+
assert_eq!(symbol.size, 0xac);
131+
132+
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
133+
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
134+
assert!(
135+
output.contains(r#"Opcode(".word", 65534)"#),
136+
"the final $d mapping symbol must preserve the 4-byte data directive:\n{output}"
137+
);
138+
}
139+
118140
#[test]
119141
#[cfg(feature = "arm")]
120142
fn do_not_trim_trailing_relocations() {
210 KB
Binary file not shown.

0 commit comments

Comments
 (0)