Skip to content

Commit d1a8679

Browse files
committed
Implement diffing individual data symbols
1 parent 7c424a7 commit d1a8679

File tree

7 files changed

+255
-137
lines changed

7 files changed

+255
-137
lines changed

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
}
@@ -70,7 +70,83 @@ pub fn resolve_relocation<'obj>(
7070
ResolvedRelocation { relocation: reloc, symbol }
7171
}
7272

73-
/// Compares relocations contained with a certain data range.
73+
/// Compares the bytes within a certain data range.
74+
fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>, Vec<DataDiff>) {
75+
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
76+
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
77+
78+
let mut left_data_diff = Vec::<DataDiff>::new();
79+
let mut right_data_diff = Vec::<DataDiff>::new();
80+
for op in ops {
81+
let (tag, left_range, right_range) = op.as_tag_tuple();
82+
let left_len = left_range.len();
83+
let right_len = right_range.len();
84+
let mut len = left_len.max(right_len);
85+
let kind = match tag {
86+
similar::DiffTag::Equal => DataDiffKind::None,
87+
similar::DiffTag::Delete => DataDiffKind::Delete,
88+
similar::DiffTag::Insert => DataDiffKind::Insert,
89+
similar::DiffTag::Replace => {
90+
// Ensure replacements are equal length
91+
len = left_len.min(right_len);
92+
DataDiffKind::Replace
93+
}
94+
};
95+
let left_data = &left_data[left_range];
96+
let right_data = &right_data[right_range];
97+
left_data_diff.push(DataDiff {
98+
data: left_data[..len.min(left_data.len())].to_vec(),
99+
kind,
100+
len,
101+
..Default::default()
102+
});
103+
right_data_diff.push(DataDiff {
104+
data: right_data[..len.min(right_data.len())].to_vec(),
105+
kind,
106+
len,
107+
..Default::default()
108+
});
109+
if kind == DataDiffKind::Replace {
110+
match left_len.cmp(&right_len) {
111+
Ordering::Less => {
112+
let len = right_len - left_len;
113+
left_data_diff.push(DataDiff {
114+
data: vec![],
115+
kind: DataDiffKind::Insert,
116+
len,
117+
..Default::default()
118+
});
119+
right_data_diff.push(DataDiff {
120+
data: right_data[left_len..right_len].to_vec(),
121+
kind: DataDiffKind::Insert,
122+
len,
123+
..Default::default()
124+
});
125+
}
126+
Ordering::Greater => {
127+
let len = left_len - right_len;
128+
left_data_diff.push(DataDiff {
129+
data: left_data[right_len..left_len].to_vec(),
130+
kind: DataDiffKind::Delete,
131+
len,
132+
..Default::default()
133+
});
134+
right_data_diff.push(DataDiff {
135+
data: vec![],
136+
kind: DataDiffKind::Delete,
137+
len,
138+
..Default::default()
139+
});
140+
}
141+
Ordering::Equal => {}
142+
}
143+
}
144+
}
145+
146+
(bytes_match_ratio, left_data_diff, right_data_diff)
147+
}
148+
149+
/// Compares relocations contained within a certain data range.
74150
fn diff_data_relocs_for_range<'left, 'right>(
75151
left_obj: &'left Object,
76152
right_obj: &'right Object,
@@ -172,76 +248,10 @@ pub fn diff_data_section(
172248
.min(right_section.size);
173249
let left_data = &left_section.data[..left_max as usize];
174250
let right_data = &right_section.data[..right_max as usize];
175-
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
176-
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
177251

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

246256
let mut left_reloc_diffs = Vec::new();
247257
let mut right_reloc_diffs = Vec::new();
@@ -300,6 +310,55 @@ pub fn diff_data_section(
300310
Ok((left_section_diff, right_section_diff))
301311
}
302312

313+
pub fn no_diff_data_symbol(obj: &Object, symbol_index: usize) -> Result<SymbolDiff> {
314+
let symbol = &obj.symbols[symbol_index];
315+
let section_idx = symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
316+
let section = &obj.sections[section_idx];
317+
318+
let start = symbol
319+
.address
320+
.checked_sub(section.address)
321+
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
322+
let end = start + symbol.size;
323+
if end > section.size {
324+
return Err(anyhow!(
325+
"Symbol {} size out of section bounds ({} > {})",
326+
symbol.name,
327+
end,
328+
section.size
329+
));
330+
}
331+
let range = start as usize..end as usize;
332+
let data = &section.data[range.clone()];
333+
334+
let len = symbol.size as usize;
335+
let data_diff =
336+
vec![DataDiff { data: data.to_vec(), kind: DataDiffKind::None, len, ..Default::default() }];
337+
338+
let mut reloc_diffs = Vec::new();
339+
for reloc in section.relocations.iter() {
340+
if !range.contains(&(reloc.address as usize)) {
341+
continue;
342+
}
343+
let reloc_len = obj.arch.data_reloc_size(reloc.flags);
344+
let range = reloc.address as usize..reloc.address as usize + reloc_len;
345+
reloc_diffs.push(DataRelocationDiff {
346+
reloc: reloc.clone(),
347+
kind: DataDiffKind::None,
348+
range,
349+
});
350+
}
351+
352+
Ok(SymbolDiff {
353+
target_symbol: None,
354+
match_percent: None,
355+
diff_score: None,
356+
data_diff,
357+
data_reloc_diff: reloc_diffs,
358+
..Default::default()
359+
})
360+
}
361+
303362
pub fn diff_data_symbol(
304363
left_obj: &Object,
305364
right_obj: &Object,
@@ -348,6 +407,9 @@ pub fn diff_data_symbol(
348407
let left_data = &left_section.data[left_range.clone()];
349408
let right_data = &right_section.data[right_range.clone()];
350409

410+
let (bytes_match_ratio, left_data_diff, right_data_diff) =
411+
diff_data_range(left_data, right_data);
412+
351413
let reloc_diffs = diff_data_relocs_for_range(
352414
left_obj,
353415
right_obj,
@@ -357,10 +419,9 @@ pub fn diff_data_symbol(
357419
right_range,
358420
);
359421

360-
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
361-
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
362-
363422
let mut match_ratio = bytes_match_ratio;
423+
let mut left_reloc_diffs = Vec::new();
424+
let mut right_reloc_diffs = Vec::new();
364425
if !reloc_diffs.is_empty() {
365426
let mut total_reloc_bytes = 0;
366427
let mut matching_reloc_bytes = 0;
@@ -376,6 +437,27 @@ pub fn diff_data_symbol(
376437
if diff_kind == DataDiffKind::None {
377438
matching_reloc_bytes += reloc_diff_len;
378439
}
440+
441+
if let Some(left_reloc) = left_reloc {
442+
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
443+
let range = left_reloc.relocation.address as usize
444+
..left_reloc.relocation.address as usize + len;
445+
left_reloc_diffs.push(DataRelocationDiff {
446+
reloc: left_reloc.relocation.clone(),
447+
kind: diff_kind,
448+
range,
449+
});
450+
}
451+
if let Some(right_reloc) = right_reloc {
452+
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
453+
let range = right_reloc.relocation.address as usize
454+
..right_reloc.relocation.address as usize + len;
455+
right_reloc_diffs.push(DataRelocationDiff {
456+
reloc: right_reloc.relocation.clone(),
457+
kind: diff_kind,
458+
range,
459+
});
460+
}
379461
}
380462
if total_reloc_bytes > 0 {
381463
let relocs_match_ratio = matching_reloc_bytes as f32 / total_reloc_bytes as f32;
@@ -397,13 +479,17 @@ pub fn diff_data_symbol(
397479
target_symbol: Some(right_symbol_idx),
398480
match_percent: Some(match_percent),
399481
diff_score: None,
400-
instruction_rows: vec![],
482+
data_diff: left_data_diff,
483+
data_reloc_diff: left_reloc_diffs,
484+
..Default::default()
401485
},
402486
SymbolDiff {
403487
target_symbol: Some(left_symbol_idx),
404488
match_percent: Some(match_percent),
405489
diff_score: None,
406-
instruction_rows: vec![],
490+
data_diff: right_data_diff,
491+
data_reloc_diff: right_reloc_diffs,
492+
..Default::default()
407493
},
408494
))
409495
}

objdiff-core/src/diff/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
code::{diff_code, no_diff_code},
1414
data::{
1515
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
16-
diff_generic_section, no_diff_bss_section, no_diff_data_section,
16+
diff_generic_section, no_diff_bss_section, no_diff_data_section, no_diff_data_symbol,
1717
},
1818
},
1919
obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
@@ -44,6 +44,8 @@ pub struct SymbolDiff {
4444
pub match_percent: Option<f32>,
4545
pub diff_score: Option<(u64, u64)>,
4646
pub instruction_rows: Vec<InstructionDiffRow>,
47+
pub data_diff: Vec<DataDiff>,
48+
pub data_reloc_diff: Vec<DataRelocationDiff>,
4749
}
4850

4951
#[derive(Debug, Clone, Default)]
@@ -163,7 +165,7 @@ impl ObjectDiff {
163165
target_symbol: None,
164166
match_percent: None,
165167
diff_score: None,
166-
instruction_rows: vec![],
168+
..Default::default()
167169
});
168170
}
169171
for _ in obj.sections.iter() {
@@ -262,7 +264,11 @@ pub fn diff_objs(
262264
left_out.symbols[left_symbol_ref] =
263265
no_diff_code(left_obj, left_symbol_ref, diff_config)?;
264266
}
265-
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
267+
SectionKind::Data => {
268+
left_out.symbols[left_symbol_ref] =
269+
no_diff_data_symbol(left_obj, left_symbol_ref)?;
270+
}
271+
SectionKind::Bss | SectionKind::Common => {
266272
// Nothing needs to be done
267273
}
268274
SectionKind::Unknown => unreachable!(),
@@ -275,7 +281,11 @@ pub fn diff_objs(
275281
right_out.symbols[right_symbol_ref] =
276282
no_diff_code(right_obj, right_symbol_ref, diff_config)?;
277283
}
278-
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
284+
SectionKind::Data => {
285+
right_out.symbols[right_symbol_ref] =
286+
no_diff_data_symbol(right_obj, right_symbol_ref)?;
287+
}
288+
SectionKind::Bss | SectionKind::Common => {
279289
// Nothing needs to be done
280290
}
281291
SectionKind::Unknown => unreachable!(),

0 commit comments

Comments
 (0)