1use alloc::{vec, vec::Vec};
2use core::{cmp::Ordering, ops::Range};
3
4use anyhow::{Result, anyhow};
5use similar::{Algorithm, capture_diff_slices, diff_ratio};
6
7use super::{
8 DataDiff, DataDiffKind, DataDiffRow, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
9 code::{address_eq, section_name_eq},
10};
11use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind};
12
13pub fn diff_bss_symbol(
14 left_obj: &Object,
15 right_obj: &Object,
16 left_symbol_ref: usize,
17 right_symbol_ref: usize,
18) -> Result<(SymbolDiff, SymbolDiff)> {
19 let left_symbol = &left_obj.symbols[left_symbol_ref];
20 let right_symbol = &right_obj.symbols[right_symbol_ref];
21 let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
22 Ok((
23 SymbolDiff {
24 target_symbol: Some(right_symbol_ref),
25 match_percent: Some(percent),
26 diff_score: None,
27 ..Default::default()
28 },
29 SymbolDiff {
30 target_symbol: Some(left_symbol_ref),
31 match_percent: Some(percent),
32 diff_score: None,
33 ..Default::default()
34 },
35 ))
36}
37
38pub fn symbol_name_matches(left: &Symbol, right: &Symbol) -> bool {
39 if let Some(left_name) = &left.normalized_name
40 && let Some(right_name) = &right.normalized_name
41 {
42 left_name == right_name
43 } else {
44 left.name == right.name
45 }
46}
47
48fn reloc_eq(
49 left_obj: &Object,
50 right_obj: &Object,
51 left: ResolvedRelocation,
52 right: ResolvedRelocation,
53) -> bool {
54 if left.relocation.flags != right.relocation.flags {
55 return false;
56 }
57
58 let symbol_name_addend_matches = symbol_name_matches(left.symbol, right.symbol)
59 && left.relocation.addend == right.relocation.addend;
60 match (left.symbol.section, right.symbol.section) {
61 (Some(sl), Some(sr)) => {
62 section_name_eq(left_obj, right_obj, sl, sr)
64 && (symbol_name_addend_matches || address_eq(left, right))
65 }
66 (Some(_), None) | (None, Some(_)) | (None, None) => symbol_name_addend_matches,
67 }
68}
69
70#[inline]
71pub fn resolve_relocation<'obj>(
72 symbols: &'obj [Symbol],
73 reloc: &'obj Relocation,
74) -> ResolvedRelocation<'obj> {
75 let symbol = &symbols[reloc.target_symbol];
76 ResolvedRelocation { relocation: reloc, symbol }
77}
78
79fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>, Vec<DataDiff>) {
81 let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
82 let bytes_match_ratio = diff_ratio(&ops, left_data.len(), right_data.len());
83
84 let mut left_data_diff = Vec::<DataDiff>::new();
85 let mut right_data_diff = Vec::<DataDiff>::new();
86 for op in ops {
87 let (tag, left_range, right_range) = op.as_tag_tuple();
88 let left_len = left_range.len();
89 let right_len = right_range.len();
90 let mut len = left_len.max(right_len);
91 let kind = match tag {
92 similar::DiffTag::Equal => DataDiffKind::None,
93 similar::DiffTag::Delete => DataDiffKind::Delete,
94 similar::DiffTag::Insert => DataDiffKind::Insert,
95 similar::DiffTag::Replace => {
96 len = left_len.min(right_len);
98 DataDiffKind::Replace
99 }
100 };
101 let left_data = &left_data[left_range];
102 let right_data = &right_data[right_range];
103 left_data_diff.push(DataDiff {
104 data: left_data[..len.min(left_data.len())].to_vec(),
105 kind,
106 size: len,
107 });
108 right_data_diff.push(DataDiff {
109 data: right_data[..len.min(right_data.len())].to_vec(),
110 kind,
111 size: len,
112 });
113 if kind == DataDiffKind::Replace {
114 match left_len.cmp(&right_len) {
115 Ordering::Less => {
116 let len = right_len - left_len;
117 left_data_diff.push(DataDiff {
118 data: vec![],
119 kind: DataDiffKind::Insert,
120 size: len,
121 });
122 right_data_diff.push(DataDiff {
123 data: right_data[left_len..right_len].to_vec(),
124 kind: DataDiffKind::Insert,
125 size: len,
126 });
127 }
128 Ordering::Greater => {
129 let len = left_len - right_len;
130 left_data_diff.push(DataDiff {
131 data: left_data[right_len..left_len].to_vec(),
132 kind: DataDiffKind::Delete,
133 size: len,
134 });
135 right_data_diff.push(DataDiff {
136 data: vec![],
137 kind: DataDiffKind::Delete,
138 size: len,
139 });
140 }
141 Ordering::Equal => {}
142 }
143 }
144 }
145
146 (bytes_match_ratio, left_data_diff, right_data_diff)
147}
148
149fn diff_data_relocs_for_range<'left, 'right>(
151 left_obj: &'left Object,
152 right_obj: &'right Object,
153 left_section_idx: usize,
154 right_section_idx: usize,
155 left_range: Range<usize>,
156 right_range: Range<usize>,
157) -> Vec<(DataDiffKind, Option<ResolvedRelocation<'left>>, Option<ResolvedRelocation<'right>>)> {
158 let left_section = &left_obj.sections[left_section_idx];
159 let right_section = &right_obj.sections[right_section_idx];
160 let mut diffs = Vec::new();
161 for left_reloc in left_section.relocations.iter() {
162 if !left_range.contains(&(left_reloc.address as usize)) {
163 continue;
164 }
165 let left_offset = left_reloc.address as usize - left_range.start;
166 let left_reloc = resolve_relocation(&left_obj.symbols, left_reloc);
167 let Some(right_reloc) = right_section.relocations.iter().find(|r| {
168 if !right_range.contains(&(r.address as usize)) {
169 return false;
170 }
171 let right_offset = r.address as usize - right_range.start;
172 right_offset == left_offset
173 }) else {
174 diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
175 continue;
176 };
177 let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
178 if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
179 diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
180 } else {
181 diffs.push((DataDiffKind::Replace, Some(left_reloc), Some(right_reloc)));
182 }
183 }
184 for right_reloc in right_section.relocations.iter() {
185 if !right_range.contains(&(right_reloc.address as usize)) {
186 continue;
187 }
188 let right_offset = right_reloc.address as usize - right_range.start;
189 let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
190 let Some(_) = left_section.relocations.iter().find(|r| {
191 if !left_range.contains(&(r.address as usize)) {
192 return false;
193 }
194 let left_offset = r.address as usize - left_range.start;
195 left_offset == right_offset
196 }) else {
197 diffs.push((DataDiffKind::Insert, None, Some(right_reloc)));
198 continue;
199 };
200 }
203 diffs
204}
205
206pub fn no_diff_data_section(obj: &Object, section_idx: usize) -> Result<SectionDiff> {
207 let section = &obj.sections[section_idx];
208
209 let data_diff = vec![DataDiff {
210 data: section.data.0.clone(),
211 kind: DataDiffKind::None,
212 size: section.data.len(),
213 }];
214
215 let mut reloc_diffs = Vec::new();
216 for reloc in section.relocations.iter() {
217 let reloc_len = obj.arch.data_reloc_size(reloc.flags);
218 let range = reloc.address..reloc.address + reloc_len as u64;
219 reloc_diffs.push(DataRelocationDiff {
220 reloc: reloc.clone(),
221 kind: DataDiffKind::None,
222 range,
223 });
224 }
225
226 Ok(SectionDiff { match_percent: Some(0.0), data_diff, reloc_diff: reloc_diffs })
227}
228
229pub fn diff_data_section(
231 left_obj: &Object,
232 right_obj: &Object,
233 left_diff: &ObjectDiff,
234 right_diff: &ObjectDiff,
235 left_section_idx: usize,
236 right_section_idx: usize,
237) -> Result<(SectionDiff, SectionDiff)> {
238 let left_section = &left_obj.sections[left_section_idx];
239 let right_section = &right_obj.sections[right_section_idx];
240 let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
241 .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
242 .max()
243 .unwrap_or(0)
244 .min(left_section.size);
245 let right_max = symbols_matching_section(&right_obj.symbols, right_section_idx)
246 .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| a + s.size))
247 .max()
248 .unwrap_or(0)
249 .min(right_section.size);
250 let left_data = &left_section.data[..left_max as usize];
251 let right_data = &right_section.data[..right_max as usize];
252
253 let (bytes_match_ratio, left_data_diff, right_data_diff) =
254 diff_data_range(left_data, right_data);
255 let match_percent = bytes_match_ratio * 100.0;
256
257 let mut left_reloc_diffs = Vec::new();
258 let mut right_reloc_diffs = Vec::new();
259 for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range(
260 left_obj,
261 right_obj,
262 left_section_idx,
263 right_section_idx,
264 0..left_max as usize,
265 0..right_max as usize,
266 ) {
267 if let Some(left_reloc) = left_reloc {
268 let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
269 let range = left_reloc.relocation.address..left_reloc.relocation.address + len as u64;
270 left_reloc_diffs.push(DataRelocationDiff {
271 reloc: left_reloc.relocation.clone(),
272 kind: diff_kind,
273 range,
274 });
275 }
276 if let Some(right_reloc) = right_reloc {
277 let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
278 let range = right_reloc.relocation.address..right_reloc.relocation.address + len as u64;
279 right_reloc_diffs.push(DataRelocationDiff {
280 reloc: right_reloc.relocation.clone(),
281 kind: diff_kind,
282 range,
283 });
284 }
285 }
286
287 let (mut left_section_diff, mut right_section_diff) = diff_generic_section(
288 left_obj,
289 right_obj,
290 left_diff,
291 right_diff,
292 left_section_idx,
293 right_section_idx,
294 )?;
295 let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == DataDiffKind::None);
296 left_section_diff.data_diff = left_data_diff;
297 right_section_diff.data_diff = right_data_diff;
298 left_section_diff.reloc_diff = left_reloc_diffs;
299 right_section_diff.reloc_diff = right_reloc_diffs;
300 if all_left_relocs_match {
301 if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
306 left_section_diff.match_percent = Some(match_percent);
307 }
308 }
309 Ok((left_section_diff, right_section_diff))
310}
311
312pub fn no_diff_data_symbol(obj: &Object, symbol_index: usize) -> Result<SymbolDiff> {
313 let symbol = &obj.symbols[symbol_index];
314 let section_idx = symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
315 let section = &obj.sections[section_idx];
316
317 let start = symbol
318 .address
319 .checked_sub(section.address)
320 .ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
321 let end = start + symbol.size;
322 if end > section.size {
323 return Err(anyhow!(
324 "Symbol {} size out of section bounds ({} > {})",
325 symbol.name,
326 end,
327 section.size
328 ));
329 }
330 let range = start as usize..end as usize;
331 let data = §ion.data[range.clone()];
332
333 let data_diff = vec![DataDiff {
334 data: data.to_vec(),
335 kind: DataDiffKind::None,
336 size: symbol.size as usize,
337 }];
338
339 let mut reloc_diffs = Vec::new();
340 for reloc in section.relocations.iter() {
341 if !range.contains(&(reloc.address as usize)) {
342 continue;
343 }
344 let reloc_len = obj.arch.data_reloc_size(reloc.flags);
345 let range = reloc.address..reloc.address + reloc_len as u64;
346 reloc_diffs.push(DataRelocationDiff {
347 reloc: reloc.clone(),
348 kind: DataDiffKind::None,
349 range,
350 });
351 }
352
353 let data_rows = build_data_diff_rows(&data_diff, &reloc_diffs, symbol.address);
354 Ok(SymbolDiff {
355 target_symbol: None,
356 match_percent: None,
357 diff_score: None,
358 data_rows,
359 ..Default::default()
360 })
361}
362
363pub fn diff_data_symbol(
364 left_obj: &Object,
365 right_obj: &Object,
366 left_symbol_idx: usize,
367 right_symbol_idx: usize,
368) -> Result<(SymbolDiff, SymbolDiff)> {
369 let left_symbol = &left_obj.symbols[left_symbol_idx];
370 let right_symbol = &right_obj.symbols[right_symbol_idx];
371
372 let left_section_idx =
373 left_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
374 let right_section_idx =
375 right_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
376
377 let left_section = &left_obj.sections[left_section_idx];
378 let right_section = &right_obj.sections[right_section_idx];
379
380 let left_start = left_symbol
381 .address
382 .checked_sub(left_section.address)
383 .ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
384 let right_start = right_symbol
385 .address
386 .checked_sub(right_section.address)
387 .ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
388 let left_end = left_start + left_symbol.size;
389 if left_end > left_section.size {
390 return Err(anyhow!(
391 "Symbol {} size out of section bounds ({} > {})",
392 left_symbol.name,
393 left_end,
394 left_section.size
395 ));
396 }
397 let right_end = right_start + right_symbol.size;
398 if right_end > right_section.size {
399 return Err(anyhow!(
400 "Symbol {} size out of section bounds ({} > {})",
401 right_symbol.name,
402 right_end,
403 right_section.size
404 ));
405 }
406 let left_range = left_start as usize..left_end as usize;
407 let right_range = right_start as usize..right_end as usize;
408 let left_data = &left_section.data[left_range.clone()];
409 let right_data = &right_section.data[right_range.clone()];
410
411 let (bytes_match_ratio, left_data_diff, right_data_diff) =
412 diff_data_range(left_data, right_data);
413
414 let reloc_diffs = diff_data_relocs_for_range(
415 left_obj,
416 right_obj,
417 left_section_idx,
418 right_section_idx,
419 left_range,
420 right_range,
421 );
422
423 let mut match_ratio = bytes_match_ratio;
424 let mut left_reloc_diffs = Vec::new();
425 let mut right_reloc_diffs = Vec::new();
426 if !reloc_diffs.is_empty() {
427 let mut total_reloc_bytes = 0;
428 let mut matching_reloc_bytes = 0;
429 for (diff_kind, left_reloc, right_reloc) in reloc_diffs {
430 let reloc_diff_len = match (left_reloc, right_reloc) {
431 (None, None) => unreachable!(),
432 (None, Some(right_reloc)) => {
433 right_obj.arch.data_reloc_size(right_reloc.relocation.flags)
434 }
435 (Some(left_reloc), _) => left_obj.arch.data_reloc_size(left_reloc.relocation.flags),
436 };
437 total_reloc_bytes += reloc_diff_len;
438 if diff_kind == DataDiffKind::None {
439 matching_reloc_bytes += reloc_diff_len;
440 }
441
442 if let Some(left_reloc) = left_reloc {
443 let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
444 let range =
445 left_reloc.relocation.address..left_reloc.relocation.address + len as u64;
446 left_reloc_diffs.push(DataRelocationDiff {
447 reloc: left_reloc.relocation.clone(),
448 kind: diff_kind,
449 range,
450 });
451 }
452 if let Some(right_reloc) = right_reloc {
453 let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
454 let range =
455 right_reloc.relocation.address..right_reloc.relocation.address + len as u64;
456 right_reloc_diffs.push(DataRelocationDiff {
457 reloc: right_reloc.relocation.clone(),
458 kind: diff_kind,
459 range,
460 });
461 }
462 }
463 if total_reloc_bytes > 0 {
464 let relocs_match_ratio = matching_reloc_bytes as f32 / total_reloc_bytes as f32;
465 match_ratio = ((bytes_match_ratio * (left_data.len() as f32))
471 + (relocs_match_ratio * total_reloc_bytes as f32))
472 / (left_data.len() + total_reloc_bytes) as f32;
473 }
474 }
475
476 left_reloc_diffs
477 .sort_by(|a, b| a.range.start.cmp(&b.range.start).then(a.range.end.cmp(&b.range.end)));
478 right_reloc_diffs
479 .sort_by(|a, b| a.range.start.cmp(&b.range.start).then(a.range.end.cmp(&b.range.end)));
480
481 let match_percent = match_ratio * 100.0;
482 let left_rows = build_data_diff_rows(&left_data_diff, &left_reloc_diffs, left_symbol.address);
483 let right_rows =
484 build_data_diff_rows(&right_data_diff, &right_reloc_diffs, right_symbol.address);
485
486 Ok((
487 SymbolDiff {
488 target_symbol: Some(right_symbol_idx),
489 match_percent: Some(match_percent),
490 diff_score: None,
491 data_rows: left_rows,
492 ..Default::default()
493 },
494 SymbolDiff {
495 target_symbol: Some(left_symbol_idx),
496 match_percent: Some(match_percent),
497 diff_score: None,
498 data_rows: right_rows,
499 ..Default::default()
500 },
501 ))
502}
503
504pub fn diff_generic_section(
507 left_obj: &Object,
508 _right_obj: &Object,
509 left_diff: &ObjectDiff,
510 _right_diff: &ObjectDiff,
511 left_section_idx: usize,
512 _right_section_idx: usize,
513) -> Result<(SectionDiff, SectionDiff)> {
514 let match_percent = if symbols_matching_section(&left_obj.symbols, left_section_idx)
515 .map(|(i, _)| &left_diff.symbols[i])
516 .all(|d| d.match_percent == Some(100.0))
517 {
518 100.0 } else {
520 let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
521 .map(|(i, s)| (s, &left_diff.symbols[i]))
522 .fold((0.0, 0.0), |(matched, total), (s, d)| {
523 (matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
524 });
525 if total == 0.0 { 100.0 } else { matched / total }
526 };
527 Ok((
528 SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
529 SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
530 ))
531}
532
533pub fn no_diff_bss_section() -> Result<SectionDiff> {
534 Ok(SectionDiff { match_percent: Some(0.0), data_diff: vec![], reloc_diff: vec![] })
535}
536
537pub fn diff_bss_section(
539 left_obj: &Object,
540 right_obj: &Object,
541 left_diff: &ObjectDiff,
542 right_diff: &ObjectDiff,
543 left_section_idx: usize,
544 right_section_idx: usize,
545) -> Result<(SectionDiff, SectionDiff)> {
546 let left_section = &left_obj.sections[left_section_idx];
547 let left_sizes = symbols_matching_section(&left_obj.symbols, left_section_idx)
548 .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
549 .collect::<Vec<_>>();
550 let right_section = &right_obj.sections[right_section_idx];
551 let right_sizes = symbols_matching_section(&right_obj.symbols, right_section_idx)
552 .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
553 .collect::<Vec<_>>();
554 let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
555 let mut match_percent = diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
556
557 let (generic_diff, _) = diff_generic_section(
561 left_obj,
562 right_obj,
563 left_diff,
564 right_diff,
565 left_section_idx,
566 right_section_idx,
567 )?;
568 if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
569 match_percent = generic_diff.match_percent.unwrap();
570 }
571
572 Ok((
573 SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
574 SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
575 ))
576}
577
578fn symbols_matching_section(
579 symbols: &[Symbol],
580 section_idx: usize,
581) -> impl Iterator<Item = (usize, &Symbol)> + '_ {
582 symbols.iter().enumerate().filter(move |(_, s)| {
583 s.section == Some(section_idx)
584 && s.kind != SymbolKind::Section
585 && s.size > 0
586 && !s.flags.contains(SymbolFlag::Hidden)
587 && !s.flags.contains(SymbolFlag::Ignored)
588 })
589}
590
591pub const BYTES_PER_ROW: usize = 16;
592
593fn build_data_diff_row(
594 data_diffs: &[DataDiff],
595 reloc_diffs: &[DataRelocationDiff],
596 symbol_address: u64,
597 row_index: usize,
598) -> DataDiffRow {
599 let row_start = row_index * BYTES_PER_ROW;
600 let row_end = row_start + BYTES_PER_ROW;
601 let mut row_diff = DataDiffRow {
602 address: symbol_address + row_start as u64,
603 segments: Vec::new(),
604 relocations: Vec::new(),
605 };
606
607 let mut current_offset = 0;
609 for diff in data_diffs {
610 let diff_end = current_offset + diff.size;
611 if current_offset < row_end && diff_end > row_start {
612 let start_in_diff = row_start.saturating_sub(current_offset);
613 let end_in_diff = row_end.min(diff_end) - current_offset;
614 if start_in_diff < end_in_diff {
615 let data_slice = if diff.data.is_empty() {
616 Vec::new()
617 } else {
618 diff.data[start_in_diff..end_in_diff.min(diff.data.len())].to_vec()
619 };
620 row_diff.segments.push(DataDiff {
621 data: data_slice,
622 kind: diff.kind,
623 size: end_in_diff - start_in_diff,
624 });
625 }
626 }
627 current_offset = diff_end;
628 if current_offset >= row_start + BYTES_PER_ROW {
629 break;
630 }
631 }
632
633 let row_end_absolute = row_diff.address + BYTES_PER_ROW as u64;
635 row_diff.relocations = reloc_diffs
636 .iter()
637 .filter(|rd| rd.range.start < row_end_absolute && rd.range.end > row_diff.address)
638 .cloned()
639 .collect();
640
641 row_diff
642}
643
644fn build_data_diff_rows(
645 segments: &[DataDiff],
646 relocations: &[DataRelocationDiff],
647 symbol_address: u64,
648) -> Vec<DataDiffRow> {
649 let total_len = segments.iter().map(|s| s.size as u64).sum::<u64>();
650 let num_rows = total_len.div_ceil(BYTES_PER_ROW as u64) as usize;
651 (0..num_rows)
652 .map(|row_index| build_data_diff_row(segments, relocations, symbol_address, row_index))
653 .collect()
654}