Skip to content

Commit 23009bf

Browse files
authored
Implement diffing individual data symbols (#244)
* Implement diffing individual data symbols * Remove unused code for diffing sections * Data diff view: Make rows show offset within the symbol, not within the section * Remove SelectedSymbol enum as it only has a single variant now * Create fake data section symbols to allow diffing entire sections again * Fix text sections not having their size zeroed out * Update test snapshots * Clean up code for inferring section symbol size * Fix bug where PPC pool references weren't ignoring section symbols * Update comment * Always add unique section symbols for data sections * Update test snapshots * Remove unnecessary clone in format! call * Auto-start mapping for unpaired data symbols
1 parent 6fb4bb8 commit 23009bf

20 files changed

+687
-221
lines changed

objdiff-cli/src/cmd/report.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use objdiff_core::{
99
},
1010
config::path::platform_path,
1111
diff,
12-
obj::{self, SectionKind, SymbolFlag},
12+
obj::{self, SectionKind, SymbolFlag, SymbolKind},
1313
};
1414
use prost::Message;
1515
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
@@ -247,6 +247,7 @@ fn report_object(
247247
|| symbol.size == 0
248248
|| symbol.flags.contains(SymbolFlag::Hidden)
249249
|| symbol.flags.contains(SymbolFlag::Ignored)
250+
|| symbol.kind == SymbolKind::Section
250251
{
251252
continue;
252253
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
obj::{
2222
FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags,
2323
ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet,
24+
SymbolKind,
2425
},
2526
};
2627

@@ -832,6 +833,7 @@ fn make_fake_pool_reloc(
832833
&& s.size > 0
833834
&& !s.flags.contains(SymbolFlag::Hidden)
834835
&& !s.flags.contains(SymbolFlag::Ignored)
836+
&& s.kind != SymbolKind::Section
835837
&& (s.address..s.address + s.size).contains(&target_address)
836838
})?;
837839
addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;

objdiff-core/src/diff/code.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ pub fn no_diff_code(
4141
instruction_rows.push(InstructionDiffRow { ins_ref: Some(*i), ..Default::default() });
4242
}
4343
resolve_branches(&ops, &mut instruction_rows);
44-
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
44+
Ok(SymbolDiff {
45+
target_symbol: None,
46+
match_percent: None,
47+
diff_score: None,
48+
instruction_rows,
49+
..Default::default()
50+
})
4551
}
4652

4753
const PENALTY_IMM_DIFF: u64 = 1;
@@ -147,12 +153,14 @@ pub fn diff_code(
147153
match_percent: Some(match_percent),
148154
diff_score: Some((diff_score, max_score)),
149155
instruction_rows: left_rows,
156+
..Default::default()
150157
},
151158
SymbolDiff {
152159
target_symbol: Some(left_symbol_idx),
153160
match_percent: Some(match_percent),
154161
diff_score: Some((diff_score, max_score)),
155162
instruction_rows: right_rows,
163+
..Default::default()
156164
},
157165
))
158166
}

objdiff-core/src/diff/data.rs

Lines changed: 163 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ pub fn diff_bss_symbol(
2424
target_symbol: Some(right_symbol_ref),
2525
match_percent: Some(percent),
2626
diff_score: None,
27-
instruction_rows: vec![],
27+
..Default::default()
2828
},
2929
SymbolDiff {
3030
target_symbol: Some(left_symbol_ref),
3131
match_percent: Some(percent),
3232
diff_score: None,
33-
instruction_rows: vec![],
33+
..Default::default()
3434
},
3535
))
3636
}
@@ -84,7 +84,83 @@ pub fn resolve_relocation<'obj>(
8484
ResolvedRelocation { relocation: reloc, symbol }
8585
}
8686

87-
/// Compares relocations contained with a certain data range.
87+
/// Compares the bytes within a certain data range.
88+
fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>, Vec<DataDiff>) {
89+
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
90+
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
91+
92+
let mut left_data_diff = Vec::<DataDiff>::new();
93+
let mut right_data_diff = Vec::<DataDiff>::new();
94+
for op in ops {
95+
let (tag, left_range, right_range) = op.as_tag_tuple();
96+
let left_len = left_range.len();
97+
let right_len = right_range.len();
98+
let mut len = left_len.max(right_len);
99+
let kind = match tag {
100+
similar::DiffTag::Equal => DataDiffKind::None,
101+
similar::DiffTag::Delete => DataDiffKind::Delete,
102+
similar::DiffTag::Insert => DataDiffKind::Insert,
103+
similar::DiffTag::Replace => {
104+
// Ensure replacements are equal length
105+
len = left_len.min(right_len);
106+
DataDiffKind::Replace
107+
}
108+
};
109+
let left_data = &left_data[left_range];
110+
let right_data = &right_data[right_range];
111+
left_data_diff.push(DataDiff {
112+
data: left_data[..len.min(left_data.len())].to_vec(),
113+
kind,
114+
len,
115+
..Default::default()
116+
});
117+
right_data_diff.push(DataDiff {
118+
data: right_data[..len.min(right_data.len())].to_vec(),
119+
kind,
120+
len,
121+
..Default::default()
122+
});
123+
if kind == DataDiffKind::Replace {
124+
match left_len.cmp(&right_len) {
125+
Ordering::Less => {
126+
let len = right_len - left_len;
127+
left_data_diff.push(DataDiff {
128+
data: vec![],
129+
kind: DataDiffKind::Insert,
130+
len,
131+
..Default::default()
132+
});
133+
right_data_diff.push(DataDiff {
134+
data: right_data[left_len..right_len].to_vec(),
135+
kind: DataDiffKind::Insert,
136+
len,
137+
..Default::default()
138+
});
139+
}
140+
Ordering::Greater => {
141+
let len = left_len - right_len;
142+
left_data_diff.push(DataDiff {
143+
data: left_data[right_len..left_len].to_vec(),
144+
kind: DataDiffKind::Delete,
145+
len,
146+
..Default::default()
147+
});
148+
right_data_diff.push(DataDiff {
149+
data: vec![],
150+
kind: DataDiffKind::Delete,
151+
len,
152+
..Default::default()
153+
});
154+
}
155+
Ordering::Equal => {}
156+
}
157+
}
158+
}
159+
160+
(bytes_match_ratio, left_data_diff, right_data_diff)
161+
}
162+
163+
/// Compares relocations contained within a certain data range.
88164
fn diff_data_relocs_for_range<'left, 'right>(
89165
left_obj: &'left Object,
90166
right_obj: &'right Object,
@@ -186,76 +262,10 @@ pub fn diff_data_section(
186262
.min(right_section.size);
187263
let left_data = &left_section.data[..left_max as usize];
188264
let right_data = &right_section.data[..right_max as usize];
189-
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
190-
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
191265

192-
let mut left_data_diff = Vec::<DataDiff>::new();
193-
let mut right_data_diff = Vec::<DataDiff>::new();
194-
for op in ops {
195-
let (tag, left_range, right_range) = op.as_tag_tuple();
196-
let left_len = left_range.len();
197-
let right_len = right_range.len();
198-
let mut len = left_len.max(right_len);
199-
let kind = match tag {
200-
similar::DiffTag::Equal => DataDiffKind::None,
201-
similar::DiffTag::Delete => DataDiffKind::Delete,
202-
similar::DiffTag::Insert => DataDiffKind::Insert,
203-
similar::DiffTag::Replace => {
204-
// Ensure replacements are equal length
205-
len = left_len.min(right_len);
206-
DataDiffKind::Replace
207-
}
208-
};
209-
let left_data = &left_section.data[left_range];
210-
let right_data = &right_section.data[right_range];
211-
left_data_diff.push(DataDiff {
212-
data: left_data[..len.min(left_data.len())].to_vec(),
213-
kind,
214-
len,
215-
..Default::default()
216-
});
217-
right_data_diff.push(DataDiff {
218-
data: right_data[..len.min(right_data.len())].to_vec(),
219-
kind,
220-
len,
221-
..Default::default()
222-
});
223-
if kind == DataDiffKind::Replace {
224-
match left_len.cmp(&right_len) {
225-
Ordering::Less => {
226-
let len = right_len - left_len;
227-
left_data_diff.push(DataDiff {
228-
data: vec![],
229-
kind: DataDiffKind::Insert,
230-
len,
231-
..Default::default()
232-
});
233-
right_data_diff.push(DataDiff {
234-
data: right_data[left_len..right_len].to_vec(),
235-
kind: DataDiffKind::Insert,
236-
len,
237-
..Default::default()
238-
});
239-
}
240-
Ordering::Greater => {
241-
let len = left_len - right_len;
242-
left_data_diff.push(DataDiff {
243-
data: left_data[right_len..left_len].to_vec(),
244-
kind: DataDiffKind::Delete,
245-
len,
246-
..Default::default()
247-
});
248-
right_data_diff.push(DataDiff {
249-
data: vec![],
250-
kind: DataDiffKind::Delete,
251-
len,
252-
..Default::default()
253-
});
254-
}
255-
Ordering::Equal => {}
256-
}
257-
}
258-
}
266+
let (bytes_match_ratio, left_data_diff, right_data_diff) =
267+
diff_data_range(left_data, right_data);
268+
let match_percent = bytes_match_ratio * 100.0;
259269

260270
let mut left_reloc_diffs = Vec::new();
261271
let mut right_reloc_diffs = Vec::new();
@@ -314,6 +324,55 @@ pub fn diff_data_section(
314324
Ok((left_section_diff, right_section_diff))
315325
}
316326

327+
pub fn no_diff_data_symbol(obj: &Object, symbol_index: usize) -> Result<SymbolDiff> {
328+
let symbol = &obj.symbols[symbol_index];
329+
let section_idx = symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
330+
let section = &obj.sections[section_idx];
331+
332+
let start = symbol
333+
.address
334+
.checked_sub(section.address)
335+
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
336+
let end = start + symbol.size;
337+
if end > section.size {
338+
return Err(anyhow!(
339+
"Symbol {} size out of section bounds ({} > {})",
340+
symbol.name,
341+
end,
342+
section.size
343+
));
344+
}
345+
let range = start as usize..end as usize;
346+
let data = &section.data[range.clone()];
347+
348+
let len = symbol.size as usize;
349+
let data_diff =
350+
vec![DataDiff { data: data.to_vec(), kind: DataDiffKind::None, len, ..Default::default() }];
351+
352+
let mut reloc_diffs = Vec::new();
353+
for reloc in section.relocations.iter() {
354+
if !range.contains(&(reloc.address as usize)) {
355+
continue;
356+
}
357+
let reloc_len = obj.arch.data_reloc_size(reloc.flags);
358+
let range = reloc.address as usize..reloc.address as usize + reloc_len;
359+
reloc_diffs.push(DataRelocationDiff {
360+
reloc: reloc.clone(),
361+
kind: DataDiffKind::None,
362+
range,
363+
});
364+
}
365+
366+
Ok(SymbolDiff {
367+
target_symbol: None,
368+
match_percent: None,
369+
diff_score: None,
370+
data_diff,
371+
data_reloc_diff: reloc_diffs,
372+
..Default::default()
373+
})
374+
}
375+
317376
pub fn diff_data_symbol(
318377
left_obj: &Object,
319378
right_obj: &Object,
@@ -362,6 +421,9 @@ pub fn diff_data_symbol(
362421
let left_data = &left_section.data[left_range.clone()];
363422
let right_data = &right_section.data[right_range.clone()];
364423

424+
let (bytes_match_ratio, left_data_diff, right_data_diff) =
425+
diff_data_range(left_data, right_data);
426+
365427
let reloc_diffs = diff_data_relocs_for_range(
366428
left_obj,
367429
right_obj,
@@ -371,10 +433,9 @@ pub fn diff_data_symbol(
371433
right_range,
372434
);
373435

374-
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
375-
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
376-
377436
let mut match_ratio = bytes_match_ratio;
437+
let mut left_reloc_diffs = Vec::new();
438+
let mut right_reloc_diffs = Vec::new();
378439
if !reloc_diffs.is_empty() {
379440
let mut total_reloc_bytes = 0;
380441
let mut matching_reloc_bytes = 0;
@@ -390,6 +451,27 @@ pub fn diff_data_symbol(
390451
if diff_kind == DataDiffKind::None {
391452
matching_reloc_bytes += reloc_diff_len;
392453
}
454+
455+
if let Some(left_reloc) = left_reloc {
456+
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
457+
let range = left_reloc.relocation.address as usize
458+
..left_reloc.relocation.address as usize + len;
459+
left_reloc_diffs.push(DataRelocationDiff {
460+
reloc: left_reloc.relocation.clone(),
461+
kind: diff_kind,
462+
range,
463+
});
464+
}
465+
if let Some(right_reloc) = right_reloc {
466+
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
467+
let range = right_reloc.relocation.address as usize
468+
..right_reloc.relocation.address as usize + len;
469+
right_reloc_diffs.push(DataRelocationDiff {
470+
reloc: right_reloc.relocation.clone(),
471+
kind: diff_kind,
472+
range,
473+
});
474+
}
393475
}
394476
if total_reloc_bytes > 0 {
395477
let relocs_match_ratio = matching_reloc_bytes as f32 / total_reloc_bytes as f32;
@@ -411,13 +493,17 @@ pub fn diff_data_symbol(
411493
target_symbol: Some(right_symbol_idx),
412494
match_percent: Some(match_percent),
413495
diff_score: None,
414-
instruction_rows: vec![],
496+
data_diff: left_data_diff,
497+
data_reloc_diff: left_reloc_diffs,
498+
..Default::default()
415499
},
416500
SymbolDiff {
417501
target_symbol: Some(left_symbol_idx),
418502
match_percent: Some(match_percent),
419503
diff_score: None,
420-
instruction_rows: vec![],
504+
data_diff: right_data_diff,
505+
data_reloc_diff: right_reloc_diffs,
506+
..Default::default()
421507
},
422508
))
423509
}

0 commit comments

Comments
 (0)