1use alloc::{
2 collections::{BTreeMap, BTreeSet},
3 string::String,
4 vec,
5 vec::Vec,
6};
7use core::{num::NonZeroU32, ops::Range};
8
9use anyhow::Result;
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 symbol_name_matches,
18 },
19 },
20 obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
21};
22
23pub mod code;
24pub mod data;
25pub mod demangler;
26pub mod display;
27
28include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
29
30impl DiffObjConfig {
31 pub fn separator(&self) -> &'static str { if self.space_between_args { ", " } else { "," } }
32}
33
34#[derive(Debug, Clone)]
35pub struct SectionDiff {
36 pub match_percent: Option<f32>,
38 pub data_diff: Vec<DataDiff>,
39 pub reloc_diff: Vec<DataRelocationDiff>,
40}
41
42#[derive(Debug, Clone, Default)]
43pub struct SymbolDiff {
44 pub target_symbol: Option<usize>,
46 pub match_percent: Option<f32>,
47 pub diff_score: Option<(u64, u64)>,
48 pub instruction_rows: Vec<InstructionDiffRow>,
49 pub data_rows: Vec<DataDiffRow>,
50}
51
52#[derive(Debug, Clone, Default)]
53pub struct MappingSymbolDiff {
54 pub symbol_index: usize,
55 pub symbol_diff: SymbolDiff,
56}
57
58#[derive(Debug, Clone, Default)]
59pub struct InstructionDiffRow {
60 pub ins_ref: Option<InstructionRef>,
62 pub kind: InstructionDiffKind,
64 pub branch_from: Option<InstructionBranchFrom>,
66 pub branch_to: Option<InstructionBranchTo>,
68 pub arg_diff: Vec<InstructionArgDiffIndex>,
70}
71
72#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
73pub enum InstructionDiffKind {
74 #[default]
75 None,
76 OpMismatch,
77 ArgMismatch,
78 Replace,
79 Delete,
80 Insert,
81}
82
83#[derive(Debug, Clone, Default)]
84pub struct DataDiff {
85 pub data: Vec<u8>,
86 pub size: usize,
87 pub kind: DataDiffKind,
88}
89
90#[derive(Debug, Clone)]
91pub struct DataRelocationDiff {
92 pub reloc: Relocation,
93 pub range: Range<u64>,
94 pub kind: DataDiffKind,
95}
96
97#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
98pub enum DataDiffKind {
99 #[default]
100 None,
101 Replace,
102 Delete,
103 Insert,
104}
105
106#[derive(Debug, Clone, Default)]
107pub struct DataDiffRow {
108 pub address: u64,
109 pub segments: Vec<DataDiff>,
110 pub relocations: Vec<DataRelocationDiff>,
111}
112
113#[repr(transparent)]
115#[derive(Debug, Copy, Clone, Default)]
116pub struct InstructionArgDiffIndex(pub Option<NonZeroU32>);
117
118impl InstructionArgDiffIndex {
119 pub const NONE: Self = Self(None);
120
121 #[inline(always)]
122 pub fn new(idx: u32) -> Self {
123 Self(Some(unsafe { NonZeroU32::new_unchecked(idx.saturating_add(1)) }))
124 }
125
126 #[inline(always)]
127 pub fn get(&self) -> Option<u32> { self.0.map(|idx| idx.get() - 1) }
128
129 #[inline(always)]
130 pub fn is_some(&self) -> bool { self.0.is_some() }
131
132 #[inline(always)]
133 pub fn is_none(&self) -> bool { self.0.is_none() }
134}
135
136#[derive(Debug, Clone)]
137pub struct InstructionBranchFrom {
138 pub ins_idx: Vec<u32>,
140 pub branch_idx: u32,
142}
143
144#[derive(Debug, Clone)]
145pub struct InstructionBranchTo {
146 pub ins_idx: u32,
148 pub branch_idx: u32,
150}
151
152#[derive(Debug, Default)]
153pub struct ObjectDiff {
154 pub symbols: Vec<SymbolDiff>,
156 pub sections: Vec<SectionDiff>,
158 pub mapping_symbols: Vec<MappingSymbolDiff>,
161}
162
163impl ObjectDiff {
164 pub fn new_from_obj(obj: &Object) -> Self {
165 let mut result = Self {
166 symbols: Vec::with_capacity(obj.symbols.len()),
167 sections: Vec::with_capacity(obj.sections.len()),
168 mapping_symbols: vec![],
169 };
170 for _ in obj.symbols.iter() {
171 result.symbols.push(SymbolDiff {
172 target_symbol: None,
173 match_percent: None,
174 diff_score: None,
175 ..Default::default()
176 });
177 }
178 for _ in obj.sections.iter() {
179 result.sections.push(SectionDiff {
180 match_percent: None,
182 data_diff: vec![],
183 reloc_diff: vec![],
184 });
185 }
186 result
187 }
188}
189
190#[derive(Debug, Default)]
191pub struct DiffObjsResult {
192 pub left: Option<ObjectDiff>,
193 pub right: Option<ObjectDiff>,
194 pub prev: Option<ObjectDiff>,
195}
196
197pub fn diff_objs(
198 left: Option<&Object>,
199 right: Option<&Object>,
200 prev: Option<&Object>,
201 diff_config: &DiffObjConfig,
202 mapping_config: &MappingConfig,
203) -> Result<DiffObjsResult> {
204 let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
205 let section_matches = matching_sections(left, right)?;
206 let mut left = left.map(|p| (p, ObjectDiff::new_from_obj(p)));
207 let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p)));
208 let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p)));
209
210 for symbol_match in symbol_matches {
211 match symbol_match {
212 SymbolMatch {
213 left: Some(left_symbol_ref),
214 right: Some(right_symbol_ref),
215 prev: prev_symbol_ref,
216 section_kind,
217 } => {
218 let (left_obj, left_out) = left.as_mut().unwrap();
219 let (right_obj, right_out) = right.as_mut().unwrap();
220 match section_kind {
221 SectionKind::Code => {
222 let (left_diff, right_diff) = diff_code(
223 left_obj,
224 right_obj,
225 left_symbol_ref,
226 right_symbol_ref,
227 diff_config,
228 )?;
229 left_out.symbols[left_symbol_ref] = left_diff;
230 right_out.symbols[right_symbol_ref] = right_diff;
231
232 if let Some(prev_symbol_ref) = prev_symbol_ref {
233 let (_prev_obj, prev_out) = prev.as_mut().unwrap();
234 let (_, prev_diff) = diff_code(
235 left_obj,
236 right_obj,
237 right_symbol_ref,
238 prev_symbol_ref,
239 diff_config,
240 )?;
241 prev_out.symbols[prev_symbol_ref] = prev_diff;
242 }
243 }
244 SectionKind::Data => {
245 let (left_diff, right_diff) = diff_data_symbol(
246 left_obj,
247 right_obj,
248 left_symbol_ref,
249 right_symbol_ref,
250 )?;
251 left_out.symbols[left_symbol_ref] = left_diff;
252 right_out.symbols[right_symbol_ref] = right_diff;
253 }
254 SectionKind::Bss | SectionKind::Common => {
255 let (left_diff, right_diff) = diff_bss_symbol(
256 left_obj,
257 right_obj,
258 left_symbol_ref,
259 right_symbol_ref,
260 )?;
261 left_out.symbols[left_symbol_ref] = left_diff;
262 right_out.symbols[right_symbol_ref] = right_diff;
263 }
264 SectionKind::Unknown => unreachable!(),
265 }
266 }
267 SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
268 let (left_obj, left_out) = left.as_mut().unwrap();
269 match section_kind {
270 SectionKind::Code => {
271 left_out.symbols[left_symbol_ref] =
272 no_diff_code(left_obj, left_symbol_ref, diff_config)?;
273 }
274 SectionKind::Data => {
275 left_out.symbols[left_symbol_ref] =
276 no_diff_data_symbol(left_obj, left_symbol_ref)?;
277 }
278 SectionKind::Bss | SectionKind::Common => {
279 }
281 SectionKind::Unknown => unreachable!(),
282 }
283 }
284 SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => {
285 let (right_obj, right_out) = right.as_mut().unwrap();
286 match section_kind {
287 SectionKind::Code => {
288 right_out.symbols[right_symbol_ref] =
289 no_diff_code(right_obj, right_symbol_ref, diff_config)?;
290 }
291 SectionKind::Data => {
292 right_out.symbols[right_symbol_ref] =
293 no_diff_data_symbol(right_obj, right_symbol_ref)?;
294 }
295 SectionKind::Bss | SectionKind::Common => {
296 }
298 SectionKind::Unknown => unreachable!(),
299 }
300 }
301 SymbolMatch { left: None, right: None, .. } => {
302 }
304 }
305 }
306
307 for section_match in section_matches {
308 match section_match {
309 SectionMatch {
310 left: Some(left_section_idx),
311 right: Some(right_section_idx),
312 section_kind,
313 } => {
314 let (left_obj, left_out) = left.as_mut().unwrap();
315 let (right_obj, right_out) = right.as_mut().unwrap();
316 match section_kind {
317 SectionKind::Code => {
318 let (left_diff, right_diff) = diff_generic_section(
319 left_obj,
320 right_obj,
321 left_out,
322 right_out,
323 left_section_idx,
324 right_section_idx,
325 )?;
326 left_out.sections[left_section_idx] = left_diff;
327 right_out.sections[right_section_idx] = right_diff;
328 }
329 SectionKind::Data => {
330 let (left_diff, right_diff) = diff_data_section(
331 left_obj,
332 right_obj,
333 left_out,
334 right_out,
335 left_section_idx,
336 right_section_idx,
337 )?;
338 left_out.sections[left_section_idx] = left_diff;
339 right_out.sections[right_section_idx] = right_diff;
340 }
341 SectionKind::Bss | SectionKind::Common => {
342 let (left_diff, right_diff) = diff_bss_section(
343 left_obj,
344 right_obj,
345 left_out,
346 right_out,
347 left_section_idx,
348 right_section_idx,
349 )?;
350 left_out.sections[left_section_idx] = left_diff;
351 right_out.sections[right_section_idx] = right_diff;
352 }
353 SectionKind::Unknown => unreachable!(),
354 }
355 }
356 SectionMatch { left: Some(left_section_idx), right: None, section_kind } => {
357 let (left_obj, left_out) = left.as_mut().unwrap();
358 match section_kind {
359 SectionKind::Code => {}
360 SectionKind::Data => {
361 left_out.sections[left_section_idx] =
362 no_diff_data_section(left_obj, left_section_idx)?;
363 }
364 SectionKind::Bss | SectionKind::Common => {
365 left_out.sections[left_section_idx] = no_diff_bss_section()?;
366 }
367 SectionKind::Unknown => unreachable!(),
368 }
369 }
370 SectionMatch { left: None, right: Some(right_section_idx), section_kind } => {
371 let (right_obj, right_out) = right.as_mut().unwrap();
372 match section_kind {
373 SectionKind::Code => {}
374 SectionKind::Data => {
375 right_out.sections[right_section_idx] =
376 no_diff_data_section(right_obj, right_section_idx)?;
377 }
378 SectionKind::Bss | SectionKind::Common => {
379 right_out.sections[right_section_idx] = no_diff_bss_section()?;
380 }
381 SectionKind::Unknown => unreachable!(),
382 }
383 }
384 SectionMatch { left: None, right: None, .. } => {
385 }
387 }
388 }
389
390 if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
391 (right.as_mut(), left.as_mut())
392 {
393 if let Some(right_name) = mapping_config.selecting_left.as_deref() {
394 generate_mapping_symbols(
395 left_obj,
396 left_out,
397 right_obj,
398 right_out,
399 MappingSymbol::Right(right_name),
400 diff_config,
401 )?;
402 }
403 if let Some(left_name) = mapping_config.selecting_right.as_deref() {
404 generate_mapping_symbols(
405 left_obj,
406 left_out,
407 right_obj,
408 right_out,
409 MappingSymbol::Left(left_name),
410 diff_config,
411 )?;
412 }
413 }
414
415 Ok(DiffObjsResult {
416 left: left.map(|(_, o)| o),
417 right: right.map(|(_, o)| o),
418 prev: prev.map(|(_, o)| o),
419 })
420}
421
422#[derive(Clone, Copy)]
423enum MappingSymbol<'a> {
424 Left(&'a str),
425 Right(&'a str),
426}
427
428fn generate_mapping_symbols(
432 left_obj: &Object,
433 left_out: &mut ObjectDiff,
434 right_obj: &Object,
435 right_out: &mut ObjectDiff,
436 mapping_symbol: MappingSymbol,
437 config: &DiffObjConfig,
438) -> Result<()> {
439 let (base_obj, base_name, target_obj) = match mapping_symbol {
440 MappingSymbol::Left(name) => (left_obj, name, right_obj),
441 MappingSymbol::Right(name) => (right_obj, name, left_obj),
442 };
443 let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
444 return Ok(());
445 };
446 let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
447 for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
448 if target_symbol.size == 0
449 || target_symbol.flags.contains(SymbolFlag::Ignored)
450 || symbol_section_kind(target_obj, target_symbol) != base_section_kind
451 {
452 continue;
453 }
454 let (left_symbol_idx, right_symbol_idx) = match mapping_symbol {
455 MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
456 MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
457 };
458 let (left_diff, right_diff) = match base_section_kind {
459 SectionKind::Code => {
460 diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config)
461 }
462 SectionKind::Data => {
463 diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
464 }
465 SectionKind::Bss | SectionKind::Common => {
466 diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
467 }
468 SectionKind::Unknown => continue,
469 }?;
470 match mapping_symbol {
471 MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
472 symbol_index: right_symbol_idx,
473 symbol_diff: right_diff,
474 }),
475 MappingSymbol::Right(_) => left_out
476 .mapping_symbols
477 .push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
478 }
479 }
480 Ok(())
481}
482
483#[derive(Copy, Clone, Eq, PartialEq)]
484struct SymbolMatch {
485 left: Option<usize>,
486 right: Option<usize>,
487 prev: Option<usize>,
488 section_kind: SectionKind,
489}
490
491#[derive(Copy, Clone, Eq, PartialEq)]
492struct SectionMatch {
493 left: Option<usize>,
494 right: Option<usize>,
495 section_kind: SectionKind,
496}
497
498#[derive(Debug, Clone, Default)]
499#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
500pub struct MappingConfig {
501 pub mappings: BTreeMap<String, String>,
503 pub selecting_left: Option<String>,
505 pub selecting_right: Option<String>,
507}
508
509fn apply_symbol_mappings(
510 left: &Object,
511 right: &Object,
512 mapping_config: &MappingConfig,
513 left_used: &mut BTreeSet<usize>,
514 right_used: &mut BTreeSet<usize>,
515 matches: &mut Vec<SymbolMatch>,
516) -> Result<()> {
517 if let Some(left_name) = &mapping_config.selecting_left
520 && let Some(left_symbol) = left.symbol_by_name(left_name)
521 {
522 left_used.insert(left_symbol);
523 }
524 if let Some(right_name) = &mapping_config.selecting_right
525 && let Some(right_symbol) = right.symbol_by_name(right_name)
526 {
527 right_used.insert(right_symbol);
528 }
529
530 for (left_name, right_name) in &mapping_config.mappings {
532 let Some(left_symbol_index) = left.symbol_by_name(left_name) else {
533 continue;
534 };
535 if left_used.contains(&left_symbol_index) {
536 continue;
537 }
538 let Some(right_symbol_index) = right.symbol_by_name(right_name) else {
539 continue;
540 };
541 if right_used.contains(&right_symbol_index) {
542 continue;
543 }
544 let left_section_kind = left
545 .symbols
546 .get(left_symbol_index)
547 .and_then(|s| s.section)
548 .and_then(|section_index| left.sections.get(section_index))
549 .map_or(SectionKind::Unknown, |s| s.kind);
550 let right_section_kind = right
551 .symbols
552 .get(right_symbol_index)
553 .and_then(|s| s.section)
554 .and_then(|section_index| right.sections.get(section_index))
555 .map_or(SectionKind::Unknown, |s| s.kind);
556 if left_section_kind != right_section_kind {
557 log::warn!(
558 "Symbol section kind mismatch: {left_name} ({left_section_kind:?}) vs {right_name} ({right_section_kind:?})"
559 );
560 continue;
561 }
562 matches.push(SymbolMatch {
563 left: Some(left_symbol_index),
564 right: Some(right_symbol_index),
565 prev: None, section_kind: left_section_kind,
567 });
568 left_used.insert(left_symbol_index);
569 right_used.insert(right_symbol_index);
570 }
571 Ok(())
572}
573
574fn matching_symbols(
576 left: Option<&Object>,
577 right: Option<&Object>,
578 prev: Option<&Object>,
579 mappings: &MappingConfig,
580) -> Result<Vec<SymbolMatch>> {
581 let mut matches = Vec::new();
582 let mut left_used = BTreeSet::new();
583 let mut right_used = BTreeSet::new();
584 if let Some(left) = left {
585 if let Some(right) = right {
586 apply_symbol_mappings(
587 left,
588 right,
589 mappings,
590 &mut left_used,
591 &mut right_used,
592 &mut matches,
593 )?;
594 }
595 for fuzzy_literals in [false, true] {
598 for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
599 if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
600 continue;
601 }
602 let section_kind = symbol_section_kind(left, symbol);
603 if section_kind == SectionKind::Unknown {
604 continue;
605 }
606 if left_used.contains(&symbol_idx) {
607 continue;
608 }
609 let symbol_match = SymbolMatch {
610 left: Some(symbol_idx),
611 right: find_symbol(right, left, symbol_idx, Some(&right_used), fuzzy_literals),
612 prev: find_symbol(prev, left, symbol_idx, None, fuzzy_literals),
613 section_kind,
614 };
615 matches.push(symbol_match);
616 if let Some(right) = symbol_match.right {
617 left_used.insert(symbol_idx);
618 right_used.insert(right);
619 }
620 }
621 }
622 }
623 if let Some(right) = right {
624 for fuzzy_literals in [false, true] {
627 for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
628 if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
629 continue;
630 }
631 let section_kind = symbol_section_kind(right, symbol);
632 if section_kind == SectionKind::Unknown {
633 continue;
634 }
635 if right_used.contains(&symbol_idx) {
636 continue;
637 }
638 let symbol_match = SymbolMatch {
639 left: None,
640 right: Some(symbol_idx),
641 prev: find_symbol(prev, right, symbol_idx, None, fuzzy_literals),
642 section_kind,
643 };
644 matches.push(symbol_match);
645 if symbol_match.prev.is_some() {
646 right_used.insert(symbol_idx);
647 }
648 }
649 }
650 }
651 Ok(matches)
652}
653
654fn unmatched_symbols<'obj, 'used>(
655 obj: &'obj Object,
656 used: Option<&'used BTreeSet<usize>>,
657) -> impl Iterator<Item = (usize, &'obj Symbol)> + 'used
658where
659 'obj: 'used,
660{
661 obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
662 !symbol.flags.contains(SymbolFlag::Ignored)
663 && !used.is_some_and(|u| u.contains(&symbol_idx))
665 })
666}
667
668fn symbol_section<'obj>(obj: &'obj Object, symbol: &Symbol) -> Option<(&'obj str, SectionKind)> {
669 if let Some(section) = symbol.section.and_then(|section_idx| obj.sections.get(section_idx)) {
670 let section_name =
672 section.name.split_once('$').map_or(section.name.as_str(), |(prefix, _)| prefix);
673 Some((section_name, section.kind))
674 } else if symbol.flags.contains(SymbolFlag::Common) {
675 Some((".comm", SectionKind::Common))
676 } else {
677 None
678 }
679}
680
681fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
682 match symbol.section {
683 Some(section_index) => obj.sections[section_index].kind,
684 None if symbol.flags.contains(SymbolFlag::Common) => SectionKind::Common,
685 None => SectionKind::Unknown,
686 }
687}
688
689fn find_symbol(
690 obj: Option<&Object>,
691 in_obj: &Object,
692 in_symbol_idx: usize,
693 used: Option<&BTreeSet<usize>>,
694 fuzzy_literals: bool,
695) -> Option<usize> {
696 let in_symbol = &in_obj.symbols[in_symbol_idx];
697 let obj = obj?;
698 let (section_name, section_kind) = symbol_section(in_obj, in_symbol)?;
699
700 if in_symbol.flags.contains(SymbolFlag::CompilerGenerated)
703 && matches!(section_kind, SectionKind::Code | SectionKind::Data | SectionKind::Bss)
704 {
705 let mut closest_match_symbol_idx = None;
706 let mut closest_match_percent = 0.0;
707 for (symbol_idx, symbol) in unmatched_symbols(obj, used) {
708 let Some(section_index) = symbol.section else {
709 continue;
710 };
711 if obj.sections[section_index].name != section_name {
712 continue;
713 }
714 if !symbol.flags.contains(SymbolFlag::CompilerGenerated) {
715 continue;
716 }
717 match section_kind {
718 SectionKind::Data | SectionKind::Code => {
719 if let Ok((left_diff, _right_diff)) =
723 diff_data_symbol(in_obj, obj, in_symbol_idx, symbol_idx)
724 && let Some(match_percent) = left_diff.match_percent
725 && (match_percent == 100.0
726 || (fuzzy_literals
727 && match_percent >= 50.0
728 && match_percent > closest_match_percent))
729 {
730 closest_match_symbol_idx = Some(symbol_idx);
731 closest_match_percent = match_percent;
732 if match_percent == 100.0 {
733 break;
734 }
735 }
736 }
737 SectionKind::Bss => {
738 if in_symbol.size == symbol.size {
740 closest_match_symbol_idx = Some(symbol_idx);
741 break;
742 }
743 }
744 _ => unreachable!(),
745 }
746 }
747 return closest_match_symbol_idx;
748 }
749
750 if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| {
752 symbol_name_matches(in_symbol, symbol)
753 && symbol_section_kind(obj, symbol) == section_kind
754 && symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name)
755 }) {
756 return Some(symbol_idx);
757 }
758
759 None
760}
761
762fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
764 let mut matches = Vec::with_capacity(
765 left.as_ref()
766 .map_or(0, |o| o.sections.len())
767 .max(right.as_ref().map_or(0, |o| o.sections.len())),
768 );
769 if let Some(left) = left {
770 for (section_idx, section) in left.sections.iter().enumerate() {
771 if section.kind == SectionKind::Unknown {
772 continue;
773 }
774 matches.push(SectionMatch {
775 left: Some(section_idx),
776 right: find_section(right, §ion.name, section.kind, &matches),
777 section_kind: section.kind,
778 });
779 }
780 }
781 if let Some(right) = right {
782 for (section_idx, section) in right.sections.iter().enumerate() {
783 if section.kind == SectionKind::Unknown {
784 continue;
785 }
786 if matches.iter().any(|m| m.right == Some(section_idx)) {
787 continue;
788 }
789 matches.push(SectionMatch {
790 left: None,
791 right: Some(section_idx),
792 section_kind: section.kind,
793 });
794 }
795 }
796 Ok(matches)
797}
798
799fn find_section(
800 obj: Option<&Object>,
801 name: &str,
802 section_kind: SectionKind,
803 matches: &[SectionMatch],
804) -> Option<usize> {
805 obj?.sections.iter().enumerate().position(|(i, s)| {
806 s.kind == section_kind && s.name == name && !matches.iter().any(|m| m.right == Some(i))
807 })
808}
809
810#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
811pub enum DiffSide {
812 Target,
814 Base,
816}