1use alloc::{
2 collections::{BTreeMap, BTreeSet},
3 string::String,
4 vec,
5 vec::Vec,
6};
7use core::{cmp::Ordering, num::NonZeroU32, ops::Range};
8
9use anyhow::{Result, anyhow};
10
11use crate::{
12 diff::{
13 code::{diff_code, no_diff_code},
14 data::{
15 diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
16 diff_generic_section, no_diff_bss_section, no_diff_data_section, no_diff_data_symbol,
17 },
18 },
19 obj::{
20 InstructionRef, Object, Relocation, ResolvedRelocation, SectionKind, Symbol, SymbolFlag,
21 SymbolKind,
22 },
23};
24
25pub mod code;
26pub mod data;
27pub mod demangler;
28pub mod display;
29
30include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
31
32impl DiffObjConfig {
33 pub fn separator(&self) -> &'static str { if self.space_between_args { ", " } else { "," } }
34}
35
36#[derive(Debug, Clone)]
37pub struct SectionDiff {
38 pub match_percent: Option<f32>,
40 pub data_diff: Vec<DataDiff>,
41 pub reloc_diff: Vec<DataRelocationDiff>,
42}
43
44#[derive(Debug, Clone, Default)]
45pub struct SymbolDiff {
46 pub target_symbol: Option<usize>,
48 pub match_percent: Option<f32>,
49 pub diff_score: Option<(u64, u64)>,
50 pub instruction_rows: Vec<InstructionDiffRow>,
51 pub data_rows: Vec<DataDiffRow>,
52 pub order: Option<Ordering>,
53}
54
55#[derive(Debug, Clone, Default)]
56pub struct MappingSymbolDiff {
57 pub symbol_index: usize,
58 pub symbol_diff: SymbolDiff,
59}
60
61#[derive(Debug, Clone, Default)]
62pub struct InstructionDiffRow {
63 pub ins_ref: Option<InstructionRef>,
65 pub kind: InstructionDiffKind,
67 pub branch_from: Option<InstructionBranchFrom>,
69 pub branch_to: Option<InstructionBranchTo>,
71 pub arg_diff: Vec<InstructionArgDiffIndex>,
73}
74
75#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
76pub enum InstructionDiffKind {
77 #[default]
78 None,
79 OpMismatch,
80 ArgMismatch,
81 Replace,
82 Delete,
83 Insert,
84}
85
86#[derive(Debug, Clone, Default)]
87pub struct DataDiff {
88 pub data: Vec<u8>,
89 pub size: usize,
90 pub kind: DataDiffKind,
91}
92
93#[derive(Debug, Clone)]
94pub struct DataRelocationDiff {
95 pub reloc: Relocation,
96 pub range: Range<u64>,
97 pub kind: DataDiffKind,
98}
99
100#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
101pub enum DataDiffKind {
102 #[default]
103 None,
104 Replace,
105 Delete,
106 Insert,
107}
108
109#[derive(Debug, Clone, Default)]
110pub struct DataDiffRow {
111 pub address: u64,
112 pub segments: Vec<DataDiff>,
113 pub relocations: Vec<DataRelocationDiff>,
114}
115
116#[repr(transparent)]
118#[derive(Debug, Copy, Clone, Default)]
119pub struct InstructionArgDiffIndex(pub Option<NonZeroU32>);
120
121impl InstructionArgDiffIndex {
122 pub const NONE: Self = Self(None);
123
124 #[inline(always)]
125 pub fn new(idx: u32) -> Self {
126 Self(Some(unsafe { NonZeroU32::new_unchecked(idx.saturating_add(1)) }))
127 }
128
129 #[inline(always)]
130 pub fn get(&self) -> Option<u32> { self.0.map(|idx| idx.get() - 1) }
131
132 #[inline(always)]
133 pub fn is_some(&self) -> bool { self.0.is_some() }
134
135 #[inline(always)]
136 pub fn is_none(&self) -> bool { self.0.is_none() }
137}
138
139#[derive(Debug, Clone)]
140pub struct InstructionBranchFrom {
141 pub ins_idx: Vec<u32>,
143 pub branch_idx: u32,
145}
146
147#[derive(Debug, Clone)]
148pub struct InstructionBranchTo {
149 pub ins_idx: u32,
151 pub branch_idx: u32,
153}
154
155#[derive(Debug, Default)]
156pub struct ObjectDiff {
157 pub symbols: Vec<SymbolDiff>,
159 pub sections: Vec<SectionDiff>,
161 pub mapping_symbols: Vec<MappingSymbolDiff>,
164}
165
166impl ObjectDiff {
167 pub fn new_from_obj(obj: &Object) -> Self {
168 let mut result = Self {
169 symbols: Vec::with_capacity(obj.symbols.len()),
170 sections: Vec::with_capacity(obj.sections.len()),
171 mapping_symbols: vec![],
172 };
173 for _ in obj.symbols.iter() {
174 result.symbols.push(SymbolDiff {
175 target_symbol: None,
176 match_percent: None,
177 diff_score: None,
178 ..Default::default()
179 });
180 }
181 for _ in obj.sections.iter() {
182 result.sections.push(SectionDiff {
183 match_percent: None,
185 data_diff: vec![],
186 reloc_diff: vec![],
187 });
188 }
189 result
190 }
191}
192
193#[derive(Debug, Default)]
194pub struct DiffObjsResult {
195 pub left: Option<ObjectDiff>,
196 pub right: Option<ObjectDiff>,
197 pub prev: Option<ObjectDiff>,
198}
199
200pub fn diff_objs(
201 left: Option<&Object>,
202 right: Option<&Object>,
203 prev: Option<&Object>,
204 diff_config: &DiffObjConfig,
205 mapping_config: &MappingConfig,
206) -> Result<DiffObjsResult> {
207 let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
208 let section_matches = matching_sections(left, right)?;
209 let mut left = left.map(|p| (p, ObjectDiff::new_from_obj(p)));
210 let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p)));
211 let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p)));
212
213 for symbol_match in &symbol_matches {
214 match *symbol_match {
215 SymbolMatch {
216 left: Some(left_symbol_ref),
217 right: Some(right_symbol_ref),
218 prev: prev_symbol_ref,
219 section_kind,
220 } => {
221 let (left_obj, left_out) = left.as_mut().unwrap();
222 let (right_obj, right_out) = right.as_mut().unwrap();
223 match section_kind {
224 SectionKind::Code => {
225 let (left_diff, right_diff) = diff_code(
226 left_obj,
227 right_obj,
228 left_symbol_ref,
229 right_symbol_ref,
230 diff_config,
231 )?;
232 left_out.symbols[left_symbol_ref] = left_diff;
233 right_out.symbols[right_symbol_ref] = right_diff;
234
235 if let Some(prev_symbol_ref) = prev_symbol_ref {
236 let (_prev_obj, prev_out) = prev.as_mut().unwrap();
237 let (_, prev_diff) = diff_code(
238 left_obj,
239 right_obj,
240 right_symbol_ref,
241 prev_symbol_ref,
242 diff_config,
243 )?;
244 prev_out.symbols[prev_symbol_ref] = prev_diff;
245 }
246 }
247 SectionKind::Data => {
248 let (left_diff, right_diff) = diff_data_symbol(
249 left_obj,
250 right_obj,
251 left_symbol_ref,
252 right_symbol_ref,
253 )?;
254 left_out.symbols[left_symbol_ref] = left_diff;
255 right_out.symbols[right_symbol_ref] = right_diff;
256 }
257 SectionKind::Bss | SectionKind::Common => {
258 let (left_diff, right_diff) = diff_bss_symbol(
259 left_obj,
260 right_obj,
261 left_symbol_ref,
262 right_symbol_ref,
263 )?;
264 left_out.symbols[left_symbol_ref] = left_diff;
265 right_out.symbols[right_symbol_ref] = right_diff;
266 }
267 SectionKind::Unknown => unreachable!(),
268 }
269 }
270 SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
271 let (left_obj, left_out) = left.as_mut().unwrap();
272 match section_kind {
273 SectionKind::Code => {
274 left_out.symbols[left_symbol_ref] =
275 no_diff_code(left_obj, left_symbol_ref, diff_config)?;
276 }
277 SectionKind::Data => {
278 left_out.symbols[left_symbol_ref] =
279 no_diff_data_symbol(left_obj, left_symbol_ref)?;
280 }
281 SectionKind::Bss | SectionKind::Common => {
282 }
284 SectionKind::Unknown => unreachable!(),
285 }
286 }
287 SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => {
288 let (right_obj, right_out) = right.as_mut().unwrap();
289 match section_kind {
290 SectionKind::Code => {
291 right_out.symbols[right_symbol_ref] =
292 no_diff_code(right_obj, right_symbol_ref, diff_config)?;
293 }
294 SectionKind::Data => {
295 right_out.symbols[right_symbol_ref] =
296 no_diff_data_symbol(right_obj, right_symbol_ref)?;
297 }
298 SectionKind::Bss | SectionKind::Common => {
299 }
301 SectionKind::Unknown => unreachable!(),
302 }
303 }
304 SymbolMatch { left: None, right: None, .. } => {
305 }
307 }
308 }
309
310 for section_match in section_matches {
311 match section_match {
312 SectionMatch {
313 left: Some(left_section_idx),
314 right: Some(right_section_idx),
315 section_kind,
316 } => {
317 let (left_obj, left_out) = left.as_mut().unwrap();
318 let (right_obj, right_out) = right.as_mut().unwrap();
319 match section_kind {
320 SectionKind::Code => {
321 let (left_diff, right_diff) = diff_generic_section(
322 left_obj,
323 right_obj,
324 left_out,
325 right_out,
326 left_section_idx,
327 right_section_idx,
328 )?;
329 left_out.sections[left_section_idx] = left_diff;
330 right_out.sections[right_section_idx] = right_diff;
331 }
332 SectionKind::Data => {
333 let (left_diff, right_diff) = diff_data_section(
334 left_obj,
335 right_obj,
336 left_out,
337 right_out,
338 left_section_idx,
339 right_section_idx,
340 )?;
341 left_out.sections[left_section_idx] = left_diff;
342 right_out.sections[right_section_idx] = right_diff;
343 }
344 SectionKind::Bss | SectionKind::Common => {
345 let (left_diff, right_diff) = diff_bss_section(
346 left_obj,
347 right_obj,
348 left_out,
349 right_out,
350 left_section_idx,
351 right_section_idx,
352 )?;
353 left_out.sections[left_section_idx] = left_diff;
354 right_out.sections[right_section_idx] = right_diff;
355 }
356 SectionKind::Unknown => unreachable!(),
357 }
358 }
359 SectionMatch { left: Some(left_section_idx), right: None, section_kind } => {
360 let (left_obj, left_out) = left.as_mut().unwrap();
361 match section_kind {
362 SectionKind::Code => {}
363 SectionKind::Data => {
364 left_out.sections[left_section_idx] =
365 no_diff_data_section(left_obj, left_section_idx)?;
366 }
367 SectionKind::Bss | SectionKind::Common => {
368 left_out.sections[left_section_idx] = no_diff_bss_section()?;
369 }
370 SectionKind::Unknown => unreachable!(),
371 }
372 }
373 SectionMatch { left: None, right: Some(right_section_idx), section_kind } => {
374 let (right_obj, right_out) = right.as_mut().unwrap();
375 match section_kind {
376 SectionKind::Code => {}
377 SectionKind::Data => {
378 right_out.sections[right_section_idx] =
379 no_diff_data_section(right_obj, right_section_idx)?;
380 }
381 SectionKind::Bss | SectionKind::Common => {
382 right_out.sections[right_section_idx] = no_diff_bss_section()?;
383 }
384 SectionKind::Unknown => unreachable!(),
385 }
386 }
387 SectionMatch { left: None, right: None, .. } => {
388 }
390 }
391 }
392
393 if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
394 (right.as_mut(), left.as_mut())
395 {
396 if let Some(right_name) = mapping_config.selecting_left.as_deref() {
397 generate_mapping_symbols(
398 left_obj,
399 left_out,
400 right_obj,
401 right_out,
402 MappingSymbol::Right(right_name),
403 diff_config,
404 )?;
405 }
406 if let Some(left_name) = mapping_config.selecting_right.as_deref() {
407 generate_mapping_symbols(
408 left_obj,
409 left_out,
410 right_obj,
411 right_out,
412 MappingSymbol::Left(left_name),
413 diff_config,
414 )?;
415 }
416 }
417
418 if let Some((left_obj, left_out)) = left.as_mut()
419 && let Some((right_obj, right_out)) = right.as_mut()
420 {
421 let mut done_section_names = BTreeSet::new();
422 for left_section in left_obj.sections.iter() {
423 if done_section_names.contains(&left_section.name) {
424 continue;
425 }
426 done_section_names.insert(&left_section.name);
427 diff_order_for_section_name(
428 left_obj,
429 right_obj,
430 left_out,
431 right_out,
432 &left_section.name,
433 &symbol_matches,
434 )?;
435 }
436 }
437
438 Ok(DiffObjsResult {
439 left: left.map(|(_, o)| o),
440 right: right.map(|(_, o)| o),
441 prev: prev.map(|(_, o)| o),
442 })
443}
444
445fn symbols_matching_section_name<'obj>(
446 obj: &'obj Object,
447 section_name: &str,
448) -> impl Iterator<Item = (usize, &'obj Symbol)> {
449 obj.symbols.iter().enumerate().filter(move |(_, s)| {
450 let curr_section_name = symbol_section(obj, s).map(|(n, _)| n);
451 curr_section_name == Some(section_name)
452 && s.kind != SymbolKind::Section
453 && s.size > 0
454 && !s.flags.contains(SymbolFlag::Hidden)
455 && !s.flags.contains(SymbolFlag::Ignored)
456 })
457}
458
459fn diff_order_for_section_name(
460 left_obj: &Object,
461 right_obj: &Object,
462 left_diff: &mut ObjectDiff,
463 right_diff: &mut ObjectDiff,
464 section_name: &str,
465 symbol_matches: &Vec<SymbolMatch>,
466) -> Result<()> {
467 let mut left_paired_symbol_idxs = BTreeSet::new();
468 let mut right_paired_symbol_idxs = BTreeSet::new();
469 let mut left_sym_idx_to_right_sym_idx = BTreeMap::new();
470 for symbol_match in symbol_matches {
471 let Some(left_symbol_idx) = symbol_match.left else {
472 continue;
473 };
474 let Some(right_symbol_idx) = symbol_match.right else {
475 continue;
476 };
477 left_paired_symbol_idxs.insert(left_symbol_idx);
478 right_paired_symbol_idxs.insert(right_symbol_idx);
479 left_sym_idx_to_right_sym_idx.insert(left_symbol_idx, right_symbol_idx);
480 }
481
482 let left_paired_symbols: Vec<_> = symbols_matching_section_name(left_obj, section_name)
483 .filter(|(sym_idx, _)| left_paired_symbol_idxs.contains(sym_idx))
484 .collect();
485 let right_paired_symbols: Vec<_> = symbols_matching_section_name(right_obj, section_name)
486 .filter(|(sym_idx, _)| right_paired_symbol_idxs.contains(sym_idx))
487 .collect();
488
489 let mut expected_right_order_idx = 0;
490 for (left_order_idx, (left_symbol_idx, _left_symbol)) in left_paired_symbols.iter().enumerate()
491 {
492 let right_symbol_idx = left_sym_idx_to_right_sym_idx.get(left_symbol_idx).unwrap();
493 let right_order_idx = right_paired_symbols
494 .iter()
495 .position(|(sym_idx, _)| sym_idx == right_symbol_idx)
496 .ok_or_else(|| {
497 anyhow!("Failed to find right side symbol for paired left side symbol")
498 })?;
499 if right_order_idx == left_order_idx {
500 left_diff.symbols[*left_symbol_idx].order = Some(Ordering::Equal);
502 right_diff.symbols[*right_symbol_idx].order = Some(Ordering::Equal);
503 expected_right_order_idx = left_order_idx + 1
504 } else if right_order_idx == expected_right_order_idx {
505 left_diff.symbols[*left_symbol_idx].order = Some(Ordering::Equal);
508 right_diff.symbols[*right_symbol_idx].order = Some(Ordering::Equal);
509 } else {
510 left_diff.symbols[*left_symbol_idx].order =
512 Some(expected_right_order_idx.cmp(&right_order_idx));
513 right_diff.symbols[*right_symbol_idx].order =
514 Some(right_order_idx.cmp(&expected_right_order_idx));
515 expected_right_order_idx = right_order_idx;
516 }
517 expected_right_order_idx += 1;
518 }
519 Ok(())
520}
521
522#[derive(Debug, Clone)]
524pub struct SimilarSymbol {
525 pub symbol_idx: usize,
526 pub match_percent: f32,
527}
528
529pub fn find_similar_code_symbols(
532 source_obj: &Object,
533 source_symbol_idx: usize,
534 target_obj: &Object,
535 diff_config: &DiffObjConfig,
536) -> Vec<SimilarSymbol> {
537 let source_symbol = &source_obj.symbols[source_symbol_idx];
538 let source_section_kind = symbol_section_kind(source_obj, source_symbol);
539 if source_section_kind != SectionKind::Code {
540 return vec![];
541 }
542
543 let mut results = Vec::new();
544 for (target_idx, target_symbol) in target_obj.symbols.iter().enumerate() {
545 if target_symbol.size == 0 || target_symbol.flags.contains(SymbolFlag::Ignored) {
546 continue;
547 }
548 if symbol_section_kind(target_obj, target_symbol) != SectionKind::Code {
549 continue;
550 }
551 let Ok((left_diff, _)) =
552 diff_code(source_obj, target_obj, source_symbol_idx, target_idx, diff_config)
553 else {
554 continue;
555 };
556 let Some(match_percent) = left_diff.match_percent else { continue };
557 results.push(SimilarSymbol { symbol_idx: target_idx, match_percent });
558 }
559 results.sort_by(|a, b| {
560 b.match_percent.partial_cmp(&a.match_percent).unwrap_or(core::cmp::Ordering::Equal)
561 });
562 results
563}
564
565#[derive(Clone, Copy)]
566enum MappingSymbol<'a> {
567 Left(&'a str),
568 Right(&'a str),
569}
570
571fn generate_mapping_symbols(
575 left_obj: &Object,
576 left_out: &mut ObjectDiff,
577 right_obj: &Object,
578 right_out: &mut ObjectDiff,
579 mapping_symbol: MappingSymbol,
580 config: &DiffObjConfig,
581) -> Result<()> {
582 let (base_obj, base_name, target_obj) = match mapping_symbol {
583 MappingSymbol::Left(name) => (left_obj, name, right_obj),
584 MappingSymbol::Right(name) => (right_obj, name, left_obj),
585 };
586 let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
587 return Ok(());
588 };
589 let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
590 for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
591 if target_symbol.size == 0
592 || target_symbol.flags.contains(SymbolFlag::Ignored)
593 || symbol_section_kind(target_obj, target_symbol) != base_section_kind
594 {
595 continue;
596 }
597 let (left_symbol_idx, right_symbol_idx) = match mapping_symbol {
598 MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
599 MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
600 };
601 let (left_diff, right_diff) = match base_section_kind {
602 SectionKind::Code => {
603 diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config)
604 }
605 SectionKind::Data => {
606 diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
607 }
608 SectionKind::Bss | SectionKind::Common => {
609 diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
610 }
611 SectionKind::Unknown => continue,
612 }?;
613 match mapping_symbol {
614 MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
615 symbol_index: right_symbol_idx,
616 symbol_diff: right_diff,
617 }),
618 MappingSymbol::Right(_) => left_out
619 .mapping_symbols
620 .push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
621 }
622 }
623 Ok(())
624}
625
626#[derive(Copy, Clone, Eq, PartialEq)]
627struct SymbolMatch {
628 left: Option<usize>,
629 right: Option<usize>,
630 prev: Option<usize>,
631 section_kind: SectionKind,
632}
633
634#[derive(Copy, Clone, Eq, PartialEq)]
635struct SectionMatch {
636 left: Option<usize>,
637 right: Option<usize>,
638 section_kind: SectionKind,
639}
640
641#[derive(Debug, Clone, Default)]
642#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
643pub struct MappingConfig {
644 pub mappings: BTreeMap<String, String>,
646 pub selecting_left: Option<String>,
648 pub selecting_right: Option<String>,
650}
651
652fn apply_symbol_mappings(
653 left: &Object,
654 right: &Object,
655 mapping_config: &MappingConfig,
656 left_used: &mut BTreeSet<usize>,
657 right_used: &mut BTreeSet<usize>,
658 matches: &mut Vec<SymbolMatch>,
659) -> Result<()> {
660 if let Some(left_name) = &mapping_config.selecting_left
663 && let Some(left_symbol) = left.symbol_by_name(left_name)
664 {
665 left_used.insert(left_symbol);
666 }
667 if let Some(right_name) = &mapping_config.selecting_right
668 && let Some(right_symbol) = right.symbol_by_name(right_name)
669 {
670 right_used.insert(right_symbol);
671 }
672
673 for (left_name, right_name) in &mapping_config.mappings {
675 let Some(left_symbol_index) = left.symbol_by_name(left_name) else {
676 continue;
677 };
678 if left_used.contains(&left_symbol_index) {
679 continue;
680 }
681 let Some(right_symbol_index) = right.symbol_by_name(right_name) else {
682 continue;
683 };
684 if right_used.contains(&right_symbol_index) {
685 continue;
686 }
687 let left_section_kind = left
688 .symbols
689 .get(left_symbol_index)
690 .and_then(|s| s.section)
691 .and_then(|section_index| left.sections.get(section_index))
692 .map_or(SectionKind::Unknown, |s| s.kind);
693 let right_section_kind = right
694 .symbols
695 .get(right_symbol_index)
696 .and_then(|s| s.section)
697 .and_then(|section_index| right.sections.get(section_index))
698 .map_or(SectionKind::Unknown, |s| s.kind);
699 if left_section_kind != right_section_kind {
700 log::warn!(
701 "Symbol section kind mismatch: {left_name} ({left_section_kind:?}) vs {right_name} ({right_section_kind:?})"
702 );
703 continue;
704 }
705 matches.push(SymbolMatch {
706 left: Some(left_symbol_index),
707 right: Some(right_symbol_index),
708 prev: None, section_kind: left_section_kind,
710 });
711 left_used.insert(left_symbol_index);
712 right_used.insert(right_symbol_index);
713 }
714 Ok(())
715}
716
717fn matching_symbols(
719 left: Option<&Object>,
720 right: Option<&Object>,
721 prev: Option<&Object>,
722 mappings: &MappingConfig,
723) -> Result<Vec<SymbolMatch>> {
724 let mut matches = Vec::new();
725 let mut left_used = BTreeSet::new();
726 let mut right_used = BTreeSet::new();
727 if let Some(left) = left {
728 if let Some(right) = right {
729 apply_symbol_mappings(
730 left,
731 right,
732 mappings,
733 &mut left_used,
734 &mut right_used,
735 &mut matches,
736 )?;
737 }
738 for fuzzy_literals in [false, true] {
741 for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
742 if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
743 continue;
744 }
745 let section_kind = symbol_section_kind(left, symbol);
746 if section_kind == SectionKind::Unknown {
747 continue;
748 }
749 if left_used.contains(&symbol_idx) {
750 continue;
751 }
752 let symbol_match = SymbolMatch {
753 left: Some(symbol_idx),
754 right: find_symbol(right, left, symbol_idx, Some(&right_used), fuzzy_literals),
755 prev: find_symbol(prev, left, symbol_idx, None, fuzzy_literals),
756 section_kind,
757 };
758 matches.push(symbol_match);
759 if let Some(right) = symbol_match.right {
760 left_used.insert(symbol_idx);
761 right_used.insert(right);
762 }
763 }
764 }
765 }
766 if let Some(right) = right {
767 for fuzzy_literals in [false, true] {
770 for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
771 if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
772 continue;
773 }
774 let section_kind = symbol_section_kind(right, symbol);
775 if section_kind == SectionKind::Unknown {
776 continue;
777 }
778 if right_used.contains(&symbol_idx) {
779 continue;
780 }
781 let symbol_match = SymbolMatch {
782 left: None,
783 right: Some(symbol_idx),
784 prev: find_symbol(prev, right, symbol_idx, None, fuzzy_literals),
785 section_kind,
786 };
787 matches.push(symbol_match);
788 if symbol_match.prev.is_some() {
789 right_used.insert(symbol_idx);
790 }
791 }
792 }
793 }
794 Ok(matches)
795}
796
797fn unmatched_symbols<'obj, 'used>(
798 obj: &'obj Object,
799 used: Option<&'used BTreeSet<usize>>,
800) -> impl Iterator<Item = (usize, &'obj Symbol)> + 'used
801where
802 'obj: 'used,
803{
804 obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
805 !symbol.flags.contains(SymbolFlag::Ignored)
806 && !used.is_some_and(|u| u.contains(&symbol_idx))
808 })
809}
810
811fn symbol_section<'obj>(obj: &'obj Object, symbol: &Symbol) -> Option<(&'obj str, SectionKind)> {
812 if let Some(section) = symbol.section.and_then(|section_idx| obj.sections.get(section_idx)) {
813 let section_name =
815 section.name.split_once('$').map_or(section.name.as_str(), |(prefix, _)| prefix);
816 Some((section_name, section.kind))
817 } else if symbol.flags.contains(SymbolFlag::Common) {
818 Some((".comm", SectionKind::Common))
819 } else {
820 None
821 }
822}
823
824fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
825 match symbol.section {
826 Some(section_index) => obj.sections[section_index].kind,
827 None if symbol.flags.contains(SymbolFlag::Common) => SectionKind::Common,
828 None => SectionKind::Unknown,
829 }
830}
831
832fn find_symbol(
833 obj: Option<&Object>,
834 in_obj: &Object,
835 in_symbol_idx: usize,
836 used: Option<&BTreeSet<usize>>,
837 fuzzy_literals: bool,
838) -> Option<usize> {
839 let in_symbol = &in_obj.symbols[in_symbol_idx];
840 let obj = obj?;
841 let (section_name, section_kind) = symbol_section(in_obj, in_symbol)?;
842
843 if in_symbol.flags.contains(SymbolFlag::CompilerGenerated)
846 && matches!(section_kind, SectionKind::Code | SectionKind::Data | SectionKind::Bss)
847 {
848 let mut closest_match_symbol_idx = None;
849 let mut closest_match_percent = 0.0;
850 for (symbol_idx, symbol) in unmatched_symbols(obj, used) {
851 let Some(section_index) = symbol.section else {
852 continue;
853 };
854 if obj.sections[section_index].name != section_name {
855 continue;
856 }
857 if !symbol.flags.contains(SymbolFlag::CompilerGenerated) {
858 continue;
859 }
860 match section_kind {
861 SectionKind::Data | SectionKind::Code => {
862 if let Ok((left_diff, _right_diff)) =
866 diff_data_symbol(in_obj, obj, in_symbol_idx, symbol_idx)
867 && let Some(match_percent) = left_diff.match_percent
868 && (match_percent == 100.0
869 || (fuzzy_literals
870 && match_percent >= 50.0
871 && match_percent > closest_match_percent))
872 {
873 closest_match_symbol_idx = Some(symbol_idx);
874 closest_match_percent = match_percent;
875 if match_percent == 100.0 {
876 break;
877 }
878 }
879 }
880 SectionKind::Bss => {
881 if in_symbol.size == symbol.size {
883 closest_match_symbol_idx = Some(symbol_idx);
884 break;
885 }
886 }
887 _ => unreachable!(),
888 }
889 }
890 return closest_match_symbol_idx;
891 }
892
893 if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| {
895 symbol_name_matches(in_symbol, symbol)
896 && symbol_section_kind(obj, symbol) == section_kind
897 && symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name)
898 }) {
899 return Some(symbol_idx);
900 }
901
902 None
903}
904
905fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
907 let mut matches = Vec::with_capacity(
908 left.as_ref()
909 .map_or(0, |o| o.sections.len())
910 .max(right.as_ref().map_or(0, |o| o.sections.len())),
911 );
912 if let Some(left) = left {
913 for (section_idx, section) in left.sections.iter().enumerate() {
914 if section.kind == SectionKind::Unknown {
915 continue;
916 }
917 matches.push(SectionMatch {
918 left: Some(section_idx),
919 right: find_section(right, §ion.name, section.kind, &matches),
920 section_kind: section.kind,
921 });
922 }
923 }
924 if let Some(right) = right {
925 for (section_idx, section) in right.sections.iter().enumerate() {
926 if section.kind == SectionKind::Unknown {
927 continue;
928 }
929 if matches.iter().any(|m| m.right == Some(section_idx)) {
930 continue;
931 }
932 matches.push(SectionMatch {
933 left: None,
934 right: Some(section_idx),
935 section_kind: section.kind,
936 });
937 }
938 }
939 Ok(matches)
940}
941
942fn find_section(
943 obj: Option<&Object>,
944 name: &str,
945 section_kind: SectionKind,
946 matches: &[SectionMatch],
947) -> Option<usize> {
948 obj?.sections.iter().enumerate().position(|(i, s)| {
949 s.kind == section_kind && s.name == name && !matches.iter().any(|m| m.right == Some(i))
950 })
951}
952
953#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
954pub enum DiffSide {
955 Target,
957 Base,
959}
960
961pub(crate) fn address_eq(left: ResolvedRelocation, right: ResolvedRelocation) -> bool {
962 if right.symbol.size == 0 && left.symbol.size != 0 {
963 left.symbol.address as i64 + left.relocation.addend
968 == right.symbol.address as i64 + right.relocation.addend
969 } else {
970 left.symbol.address == right.symbol.address
973 && left.relocation.addend == right.relocation.addend
974 }
975}
976
977pub(crate) fn section_name_eq(
978 left_obj: &Object,
979 right_obj: &Object,
980 left_section_index: usize,
981 right_section_index: usize,
982) -> bool {
983 left_obj.sections.get(left_section_index).is_some_and(|left_section| {
984 right_obj
985 .sections
986 .get(right_section_index)
987 .is_some_and(|right_section| left_section.name == right_section.name)
988 })
989}
990
991pub(crate) fn symbol_name_matches(left: &Symbol, right: &Symbol) -> bool {
992 if let Some(left_name) = &left.normalized_name
993 && let Some(right_name) = &right.normalized_name
994 {
995 left_name == right_name
996 } else {
997 left.name == right.name
998 }
999}