1use alloc::{
2 boxed::Box,
3 collections::BTreeMap,
4 format,
5 string::{String, ToString},
6 vec,
7 vec::Vec,
8};
9use core::{
10 cmp::Ordering,
11 num::{NonZeroU32, NonZeroU64},
12};
13
14use anyhow::{Context, Result, anyhow, bail, ensure};
15use object::{Architecture, Object as _, ObjectSection as _, ObjectSymbol as _};
16
17use crate::{
18 arch::{Arch, RelocationOverride, RelocationOverrideTarget, new_arch},
19 diff::{DiffObjConfig, DiffSide},
20 obj::{
21 FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag,
22 SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
23 comment::{COMMENT_SECTION, CommentSym, MWComment},
24 split_meta::{SPLITMETA_SECTION, SplitMeta},
25 },
26 util::{align_data_slice_to, align_u64_to, read_u16, read_u32},
27};
28
29fn map_section_kind(section: &object::Section) -> SectionKind {
30 match section.kind() {
31 object::SectionKind::Text => SectionKind::Code,
32 object::SectionKind::Data
33 | object::SectionKind::ReadOnlyData
34 | object::SectionKind::ReadOnlyString
35 | object::SectionKind::Tls => SectionKind::Data,
36 object::SectionKind::UninitializedData
37 | object::SectionKind::UninitializedTls
38 | object::SectionKind::Common => SectionKind::Bss,
39 _ => SectionKind::Unknown,
40 }
41}
42
43fn get_normalized_symbol_name(name: &str) -> Option<String> {
46 const DUMMY_UNIQUE_ID: &str = "0000";
47 const DUMMY_UNIQUE_MSVC_ID: &str = "00000000";
48 if let Some((prefix, suffix)) = name.split_once("@class$")
49 && let Some(idx) = suffix.chars().position(|c| !c.is_numeric())
50 && idx > 0
51 {
52 let suffix = &suffix[idx..];
56 Some(format!("{prefix}@class${DUMMY_UNIQUE_ID}{suffix}"))
57 } else if let Some((prefix, suffix)) = name.split_once('$')
58 && suffix.chars().all(char::is_numeric)
59 {
60 Some(format!("{prefix}${DUMMY_UNIQUE_ID}"))
62 } else if let Some((prefix, suffix)) = name.split_once('.')
63 && suffix.chars().all(char::is_numeric)
64 {
65 Some(format!("{prefix}.{DUMMY_UNIQUE_ID}"))
67 } else if name.starts_with('?') {
68 let mut name_str = String::from(name);
72 let anon_indices: Vec<usize> = name_str.match_indices("?A0x").map(|(idx, _)| idx).collect();
73 if !anon_indices.is_empty() {
74 for idx in anon_indices {
75 if u32::from_str_radix(&name_str[idx + 4..idx + 12], 16).is_ok()
77 && &name_str[idx + 12..idx + 14] == "@@"
78 {
79 name_str.replace_range(idx + 4..idx + 12, DUMMY_UNIQUE_MSVC_ID);
81 }
82 }
83 Some(name_str)
84 } else {
85 None
86 }
87 } else {
88 None
89 }
90}
91
92fn is_symbol_name_compiler_generated(name: &str) -> bool {
95 if name.starts_with('@') && name[1..].chars().all(char::is_numeric) {
96 return true;
98 } else if name.starts_with("_$E") && name[3..].chars().all(char::is_numeric) {
99 return true;
101 } else if name.starts_with("$LC") && name[3..].chars().all(char::is_numeric) {
102 return true;
104 }
105 false
106}
107
108fn map_symbol(
109 arch: &dyn Arch,
110 file: &object::File,
111 symbol: &object::Symbol,
112 section_indices: &[usize],
113 split_meta: Option<&SplitMeta>,
114 comment_syms: Option<&Vec<CommentSym>>,
115 config: &DiffObjConfig,
116) -> Result<Symbol> {
117 let mut name = symbol.name().context("Failed to process symbol name")?.to_string();
118 let mut size = symbol.size();
119 if let (object::SymbolKind::Section, Some(section)) =
120 (symbol.kind(), symbol.section_index().and_then(|i| file.section_by_index(i).ok()))
121 {
122 let section_name = section.name().context("Failed to process section name")?;
123 name = format!("[{section_name}]");
124 size = 0;
130 }
131
132 let mut flags = arch.extra_symbol_flags(symbol);
133 if symbol.is_global() {
134 flags |= SymbolFlag::Global;
135 }
136 if symbol.is_local() {
137 flags |= SymbolFlag::Local;
138 }
139 if symbol.is_common() {
140 flags |= SymbolFlag::Common;
141 }
142 if symbol.is_weak() {
143 flags |= SymbolFlag::Weak;
144 }
145 if file.format() == object::BinaryFormat::Elf
146 && symbol.scope() == object::SymbolScope::Linkage
147 && (file.architecture() != Architecture::Arm || !symbol.is_global())
148 {
149 flags |= SymbolFlag::Hidden;
150 }
151 if file.format() == object::BinaryFormat::Coff
152 && let Ok(name) = symbol.name()
153 && (name.starts_with("except_data_")
154 || name.starts_with("__unwind")
155 || name.starts_with("__catch"))
156 {
157 flags |= SymbolFlag::Hidden;
158 }
159
160 let kind = match symbol.kind() {
161 object::SymbolKind::Text => SymbolKind::Function,
162 object::SymbolKind::Data => SymbolKind::Object,
163 object::SymbolKind::Section => SymbolKind::Section,
164 _ => SymbolKind::Unknown,
165 };
166 let address = arch.symbol_address(symbol.address(), kind);
167 let demangled_name = config.demangler.demangle(&name);
168 let comment_sym = comment_syms.map(|vec| vec[symbol.index().0 - 1]);
170 let align = comment_sym.and_then(|c| NonZeroU32::new(c.align));
171 let virtual_address = split_meta
173 .and_then(|m| m.virtual_addresses.as_ref())
174 .and_then(|v| v.get(symbol.index().0).cloned());
175 let section = symbol.section_index().and_then(|i| section_indices.get(i.0).copied());
176 let normalized_name = get_normalized_symbol_name(&name);
177 if is_symbol_name_compiler_generated(&name) {
178 flags |= SymbolFlag::CompilerGenerated;
179 }
180
181 Ok(Symbol {
182 name,
183 demangled_name,
184 normalized_name,
185 address,
186 size,
187 kind,
188 section,
189 flags,
190 align,
191 virtual_address,
192 })
193}
194
195fn map_symbols(
196 arch: &dyn Arch,
197 obj_file: &object::File,
198 section_indices: &[usize],
199 split_meta: Option<&SplitMeta>,
200 comment_syms: Option<&Vec<CommentSym>>,
201 config: &DiffObjConfig,
202) -> Result<(Vec<Symbol>, Vec<usize>)> {
203 let mut max_index = 0;
207 let mut obj_symbols = obj_file
208 .symbols()
209 .filter(|s| s.kind() != object::SymbolKind::File)
210 .inspect(|sym| max_index = max_index.max(sym.index().0))
211 .collect::<Vec<_>>();
212 obj_symbols.sort_by(|a, b| {
213 if let Some(a_section_index) = a.section_index()
215 && let Some(b_section_index) = b.section_index()
216 && let Ok(a_section) = obj_file.section_by_index(a_section_index)
217 && let Ok(b_section) = obj_file.section_by_index(b_section_index)
218 && let Ok(a_section_name) = a_section.name()
219 && let Ok(b_section_name) = b_section.name()
220 {
221 if a_section_name.contains('$') && !b_section_name.contains('$') {
222 return Ordering::Greater;
223 } else if !a_section_name.contains('$') && b_section_name.contains('$') {
224 return Ordering::Less;
225 }
226 }
227 a.section_index()
229 .map_or(usize::MAX, |s| s.0)
230 .cmp(&b.section_index().map_or(usize::MAX, |s| s.0))
231 .then_with(|| {
232 if a.kind() == object::SymbolKind::Section {
234 Ordering::Less
235 } else if b.kind() == object::SymbolKind::Section {
236 Ordering::Greater
237 } else {
238 Ordering::Equal
239 }
240 })
241 .then_with(|| a.address().cmp(&b.address()))
243 .then_with(|| a.size().cmp(&b.size()))
245 });
246 let mut symbols = Vec::<Symbol>::with_capacity(obj_symbols.len() + obj_file.sections().count());
247 let mut symbol_indices = vec![usize::MAX; max_index + 1];
248 for obj_symbol in obj_symbols {
249 let symbol = map_symbol(
250 arch,
251 obj_file,
252 &obj_symbol,
253 section_indices,
254 split_meta,
255 comment_syms,
256 config,
257 )?;
258 symbol_indices[obj_symbol.index().0] = symbols.len();
259 symbols.push(symbol);
260 }
261
262 Ok((symbols, symbol_indices))
263}
264
265fn add_section_symbols(sections: &[Section], symbols: &mut Vec<Symbol>) {
268 for (section_idx, section) in sections.iter().enumerate() {
269 if section.kind != SectionKind::Data {
270 continue;
271 }
272
273 let name = if section.flags.contains(SectionFlag::Combined) {
277 format!("[{}-0]", section.name)
282 } else {
283 format!("[{}]", section.id)
284 };
285
286 let size = symbols
289 .iter()
290 .filter(|s| {
291 s.section == Some(section_idx) && s.kind == SymbolKind::Object && s.size > 0
292 })
293 .map(|s| s.address + s.size)
294 .max()
295 .unwrap_or(section.size);
296
297 symbols.push(Symbol {
298 name,
299 demangled_name: None,
300 normalized_name: None,
301 address: 0,
302 size,
303 kind: SymbolKind::Section,
304 section: Some(section_idx),
305 flags: SymbolFlagSet::default() | SymbolFlag::Local,
306 align: None,
307 virtual_address: None,
308 });
309 }
310}
311
312fn is_local_label(symbol: &Symbol) -> bool {
315 const LABEL_PREFIXES: &[&str] = &[".L", "LAB_", "switchD_"];
316 symbol.size == 0
317 && symbol.flags.contains(SymbolFlag::Local)
318 && LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
319}
320
321fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> {
322 let mut iter_idx = 0;
326 let mut last_end = (0, 0);
327 while iter_idx < symbols.len() {
328 let symbol_idx = iter_idx;
329 let symbol = &symbols[symbol_idx];
330 let Some(section_idx) = symbol.section else {
331 break;
333 };
334 iter_idx += 1;
335 if symbol.size != 0 {
336 if symbol.kind != SymbolKind::Section {
337 last_end = (section_idx, symbol.address + symbol.size);
338 }
339 continue;
340 }
341 if last_end.0 == section_idx && last_end.1 > symbol.address {
343 continue;
344 }
345 let next_symbol = loop {
346 let Some(next_symbol) = symbols.get(iter_idx) else {
347 break None;
348 };
349 if next_symbol.section != Some(section_idx) {
350 break None;
351 }
352 if match symbol.kind {
353 SymbolKind::Function
354 if next_symbol.kind == SymbolKind::Function
355 && (next_symbol.name.starts_with("__unwind$")
356 || next_symbol.name.starts_with("__catch$")) =>
357 {
358 false
360 }
361 SymbolKind::Function | SymbolKind::Object => {
362 matches!(next_symbol.kind, SymbolKind::Function | SymbolKind::Object)
364 }
365 SymbolKind::Unknown | SymbolKind::Section => {
366 true
368 }
369 } && !is_local_label(next_symbol)
370 {
371 break Some(next_symbol);
372 }
373 iter_idx += 1;
374 };
375 let section = §ions[section_idx];
376 let next_address =
377 next_symbol.map(|s| s.address).unwrap_or_else(|| section.address + section.size);
378 let new_size = if symbol.kind == SymbolKind::Section && section.kind == SectionKind::Data {
379 0
382 } else if section.kind == SectionKind::Code {
383 arch.infer_function_size(symbol, section, next_address)?
384 } else {
385 next_address.saturating_sub(symbol.address)
386 };
387 if new_size > 0 {
388 let symbol = &mut symbols[symbol_idx];
389 symbol.size = new_size;
390 if symbol.kind != SymbolKind::Section {
391 symbol.flags |= SymbolFlag::SizeInferred;
392 }
393 if symbol.kind == SymbolKind::Unknown {
395 symbol.kind = match section.kind {
396 SectionKind::Code => SymbolKind::Function,
397 SectionKind::Data | SectionKind::Bss => SymbolKind::Object,
398 _ => SymbolKind::Unknown,
399 };
400 }
401 }
402 }
403 Ok(())
404}
405
406fn map_sections(
407 _arch: &dyn Arch,
408 obj_file: &object::File,
409 split_meta: Option<&SplitMeta>,
410) -> Result<(Vec<Section>, Vec<usize>)> {
411 let mut section_names = BTreeMap::<String, usize>::new();
412 let mut max_index = 0;
413 let section_count =
414 obj_file.sections().inspect(|s| max_index = max_index.max(s.index().0)).count();
415 let mut result = Vec::<Section>::with_capacity(section_count);
416 let mut section_indices = vec![usize::MAX; max_index + 1];
417 for section in obj_file.sections() {
418 let name = section.name().context("Failed to process section name")?;
419 let kind = map_section_kind(§ion);
420 let data = if kind == SectionKind::Unknown {
421 Vec::new()
423 } else {
424 section.uncompressed_data().context("Failed to read section data")?.into_owned()
425 };
426
427 let section_symbol = obj_file.symbols().find(|s| {
429 s.kind() == object::SymbolKind::Section && s.section_index() == Some(section.index())
430 });
431 let virtual_address = section_symbol.and_then(|s| {
432 split_meta
433 .and_then(|m| m.virtual_addresses.as_ref())
434 .and_then(|v| v.get(s.index().0).cloned())
435 });
436
437 let unique_id = section_names.entry(name.to_string()).or_insert(0);
438 let id = format!("{name}-{unique_id}");
439 *unique_id += 1;
440
441 section_indices[section.index().0] = result.len();
442 result.push(Section {
443 id,
444 name: name.to_string(),
445 address: section.address(),
446 size: section.size(),
447 kind,
448 data: SectionData(data),
449 flags: Default::default(),
450 align: NonZeroU64::new(section.align()),
451 relocations: Default::default(),
452 virtual_address,
453 line_info: Default::default(),
454 });
455 }
456 Ok((result, section_indices))
457}
458
459const LOW_PRIORITY_SYMBOLS: &[&str] =
460 &["__gnu_compiled_c", "__gnu_compiled_cplusplus", "gcc2_compiled."];
461
462fn best_symbol<'r, 'data, 'file>(
463 symbols: &'r [object::Symbol<'data, 'file>],
464 address: u64,
465) -> Option<(object::SymbolIndex, u64)> {
466 let mut closest_symbol_index = match symbols.binary_search_by_key(&address, |s| s.address()) {
467 Ok(index) => Some(index),
468 Err(index) => index.checked_sub(1),
469 }?;
470 let target_address = symbols[closest_symbol_index].address();
472 while let Some(prev_index) = closest_symbol_index.checked_sub(1) {
473 if symbols[prev_index].address() != target_address {
474 break;
475 }
476 closest_symbol_index = prev_index;
477 }
478 let mut best_symbol: Option<&'r object::Symbol<'data, 'file>> = None;
479 for symbol in symbols.iter().skip(closest_symbol_index) {
480 if symbol.address() > address {
481 break;
482 }
483 if symbol.kind() == object::SymbolKind::Section
484 || (symbol.size() > 0 && (symbol.address() + symbol.size()) <= address)
485 {
486 continue;
487 }
488 if let Some(best) = best_symbol {
490 if LOW_PRIORITY_SYMBOLS.contains(&best.name().unwrap_or_default())
491 && !LOW_PRIORITY_SYMBOLS.contains(&symbol.name().unwrap_or_default())
492 {
493 best_symbol = Some(symbol);
494 }
495 } else {
496 best_symbol = Some(symbol);
497 }
498 }
499 best_symbol.map(|s| (s.index(), s.address()))
500}
501
502fn map_section_relocations(
503 arch: &dyn Arch,
504 obj_file: &object::File,
505 obj_section: &object::Section,
506 symbol_indices: &[usize],
507 ordered_symbols: &[Vec<object::Symbol>],
508) -> Result<Vec<Relocation>> {
509 let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
510 for (address, reloc) in obj_section.relocations() {
511 let mut target_reloc = RelocationOverride {
512 target: match reloc.target() {
513 object::RelocationTarget::Symbol(symbol) => {
514 RelocationOverrideTarget::Symbol(symbol)
515 }
516 object::RelocationTarget::Section(section) => {
517 RelocationOverrideTarget::Section(section)
518 }
519 _ => RelocationOverrideTarget::Skip,
520 },
521 addend: reloc.addend(),
522 };
523
524 match arch.relocation_override(obj_file, obj_section, address, &reloc)? {
526 Some(reloc_override) => {
527 match reloc_override.target {
528 RelocationOverrideTarget::Keep => {}
529 target => {
530 target_reloc.target = target;
531 }
532 }
533 target_reloc.addend = reloc_override.addend;
534 }
535 None => {
536 ensure!(
537 !reloc.has_implicit_addend(),
538 "Unsupported {:?} implicit relocation {:?}",
539 obj_file.architecture(),
540 reloc.flags()
541 );
542 }
543 }
544
545 let (symbol_index, addend) = match target_reloc.target {
547 RelocationOverrideTarget::Keep => unreachable!(),
548 RelocationOverrideTarget::Skip => continue,
549 RelocationOverrideTarget::Symbol(symbol_index) => {
550 if symbol_index.0 == u32::MAX as usize {
552 continue;
553 }
554
555 if let Some(section_symbol) = obj_file
557 .symbol_by_index(symbol_index)
558 .ok()
559 .filter(|s| s.kind() == object::SymbolKind::Section)
560 {
561 let section_index =
562 section_symbol.section_index().context("Section symbol without section")?;
563 let target_address =
564 section_symbol.address().wrapping_add_signed(target_reloc.addend);
565 if let Some((new_idx, addr)) = ordered_symbols
566 .get(section_index.0)
567 .and_then(|symbols| best_symbol(symbols, target_address))
568 {
569 (new_idx, target_address.wrapping_sub(addr) as i64)
570 } else {
571 (symbol_index, target_reloc.addend)
572 }
573 } else {
574 (symbol_index, target_reloc.addend)
575 }
576 }
577 RelocationOverrideTarget::Section(section_index) => {
578 let section = match obj_file.section_by_index(section_index) {
579 Ok(section) => section,
580 Err(e) => {
581 log::warn!("Invalid relocation section: {e}");
582 continue;
583 }
584 };
585 let Ok(target_address) = u64::try_from(target_reloc.addend) else {
586 log::warn!(
587 "Negative section relocation addend: {}{}",
588 section.name()?,
589 target_reloc.addend
590 );
591 continue;
592 };
593 let Some(symbols) = ordered_symbols.get(section_index.0) else {
594 log::warn!(
595 "Couldn't resolve relocation target symbol for section {} (no symbols)",
596 section.name()?
597 );
598 continue;
599 };
600 if let Some((new_idx, addr)) = best_symbol(symbols, target_address) {
602 (new_idx, target_address.wrapping_sub(addr) as i64)
603 } else if let Some(section_symbol) =
604 symbols.iter().find(|s| s.kind() == object::SymbolKind::Section)
605 {
606 (
607 section_symbol.index(),
608 target_address.wrapping_sub(section_symbol.address()) as i64,
609 )
610 } else {
611 log::warn!(
612 "Couldn't resolve relocation target symbol for section {}",
613 section.name()?
614 );
615 continue;
616 }
617 }
618 };
619
620 let flags = match reloc.flags() {
621 object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type),
622 object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ),
623 flags => bail!("Unhandled relocation flags: {:?}", flags),
624 };
625 let target_symbol = match symbol_indices.get(symbol_index.0).copied() {
626 Some(i) => i,
627 None => {
628 log::warn!("Invalid symbol index {}", symbol_index.0);
629 continue;
630 }
631 };
632 relocations.push(Relocation { address, flags, target_symbol, addend });
633 }
634 relocations.sort_by_key(|r| r.address);
635 Ok(relocations)
636}
637
638fn map_relocations(
639 arch: &dyn Arch,
640 obj_file: &object::File,
641 sections: &mut [Section],
642 section_indices: &[usize],
643 symbol_indices: &[usize],
644) -> Result<()> {
645 let mut ordered_symbols =
647 Vec::<Vec<object::Symbol>>::with_capacity(obj_file.sections().count() + 1);
648 for symbol in obj_file.symbols() {
649 let Some(section_index) = symbol.section_index() else {
650 continue;
651 };
652 if symbol.kind() == object::SymbolKind::Section {
653 continue;
654 }
655 if section_index.0 >= ordered_symbols.len() {
656 ordered_symbols.resize_with(section_index.0 + 1, Vec::new);
657 }
658 ordered_symbols[section_index.0].push(symbol);
659 }
660 for vec in &mut ordered_symbols {
662 vec.sort_by(|a, b| a.address().cmp(&b.address()).then(a.size().cmp(&b.size())));
663 }
664 for obj_section in obj_file.sections() {
667 let section = &mut sections[section_indices[obj_section.index().0]];
668 if section.kind != SectionKind::Unknown {
669 section.relocations = map_section_relocations(
670 arch,
671 obj_file,
672 &obj_section,
673 symbol_indices,
674 &ordered_symbols,
675 )?;
676 }
677 }
678 Ok(())
679}
680
681fn perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Result<()> {
682 if !config.analyze_data_flow && !config.ppc_calculate_pool_relocations {
684 return Ok(());
685 }
686
687 let mut generated_relocations = Vec::<(usize, Vec<Relocation>)>::new();
688 let mut generated_flow_results = Vec::<(Symbol, Box<dyn FlowAnalysisResult>)>::new();
689 for (section_index, section) in obj.sections.iter().enumerate() {
690 if section.kind != SectionKind::Code {
691 continue;
692 }
693 for symbol in obj.symbols.iter() {
694 if symbol.section != Some(section_index) {
695 continue;
696 }
697 if symbol.kind != SymbolKind::Function {
698 continue;
699 }
700 let code =
701 section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
702 anyhow!(
703 "Symbol data out of bounds: {:#x}..{:#x}",
704 symbol.address,
705 symbol.address + symbol.size
706 )
707 })?;
708
709 if config.ppc_calculate_pool_relocations {
713 let relocations = obj.arch.generate_pooled_relocations(
714 symbol.address,
715 code,
716 §ion.relocations,
717 &obj.symbols,
718 );
719 generated_relocations.push((section_index, relocations));
720 }
721
722 if config.analyze_data_flow
724 && let Some(flow_result) =
725 obj.arch.data_flow_analysis(obj, symbol, code, §ion.relocations)
726 {
727 generated_flow_results.push((symbol.clone(), flow_result));
728 }
729 }
730 }
731 for (symbol, flow_result) in generated_flow_results {
732 obj.add_flow_analysis_result(&symbol, flow_result);
733 }
734 for (section_index, mut relocations) in generated_relocations {
735 obj.sections[section_index].relocations.append(&mut relocations);
736 }
737 for section in obj.sections.iter_mut() {
738 section.relocations.sort_by_key(|r| r.address);
739 }
740 Ok(())
741}
742
743fn parse_line_info(
744 obj_file: &object::File,
745 sections: &mut [Section],
746 section_indices: &[usize],
747 obj_data: &[u8],
748) -> Result<()> {
749 if let Err(e) = parse_line_info_dwarf1(obj_file, sections) {
751 log::warn!("Failed to parse DWARF 1.1 line info: {e}");
752 }
753
754 #[cfg(feature = "dwarf")]
756 if let Err(e) = super::dwarf2::parse_line_info_dwarf2(obj_file, sections) {
757 log::warn!("Failed to parse DWARF 2+ line info: {e}");
758 }
759
760 if let object::File::Coff(coff) = obj_file
762 && let Err(e) = parse_line_info_coff(coff, sections, section_indices, obj_data)
763 {
764 log::warn!("Failed to parse COFF line info: {e}");
765 }
766
767 if let Err(e) = super::mdebug::parse_line_info_mdebug(obj_file, sections) {
768 log::warn!("Failed to parse MIPS mdebug line info: {e}");
769 }
770
771 Ok(())
772}
773
774fn parse_line_info_dwarf1(obj_file: &object::File, sections: &mut [Section]) -> Result<()> {
776 let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
777 for section in obj_file.sections().filter(|s| s.name().is_ok_and(|n| n == ".line")) {
778 let data = section.uncompressed_data()?;
779 let mut reader: &[u8] = data.as_ref();
780
781 while !reader.is_empty() {
782 let mut section_data = reader;
783 let size = read_u32(obj_file, &mut section_data)? as usize;
784 if size > reader.len() {
785 bail!("Line info size {size} exceeds remaining size {}", reader.len());
786 }
787 (section_data, reader) = reader.split_at(size);
788
789 section_data = §ion_data[4..]; let base_address = read_u32(obj_file, &mut section_data)? as u64;
791 let out_section = text_sections.next().context("No text section for line info")?;
792 while !section_data.is_empty() {
793 let line_number = read_u32(obj_file, &mut section_data)?;
794 let statement_pos = read_u16(obj_file, &mut section_data)?;
795 if statement_pos != 0xFFFF {
796 log::warn!("Unhandled statement pos {statement_pos}");
797 }
798 let address_delta = read_u32(obj_file, &mut section_data)? as u64;
799 out_section.line_info.insert(base_address + address_delta, line_number);
800 }
801 }
802 }
803 Ok(())
804}
805
806fn parse_line_info_coff(
807 coff: &object::coff::CoffFile,
808 sections: &mut [Section],
809 section_indices: &[usize],
810 obj_data: &[u8],
811) -> Result<()> {
812 use object::{
813 coff::{CoffHeader as _, ImageSymbol as _},
814 endian::LittleEndian as LE,
815 };
816 let symbol_table = coff.coff_header().symbols(obj_data)?;
817
818 for sect in coff.sections() {
820 let ptr_linenums = sect.coff_section().pointer_to_linenumbers.get(LE) as usize;
821 let num_linenums = sect.coff_section().number_of_linenumbers.get(LE) as usize;
822
823 if num_linenums == 0 {
825 continue;
826 }
827
828 let Some(out_section) =
831 section_indices.get(sect.index().0).and_then(|&i| sections.get_mut(i))
832 else {
833 continue;
834 };
835
836 let Some(linenums) = &obj_data.get(
838 ptr_linenums..ptr_linenums + num_linenums * size_of::<object::pe::ImageLinenumber>(),
839 ) else {
840 continue;
841 };
842 let Ok(linenums) =
843 object::pod::slice_from_all_bytes::<object::pe::ImageLinenumber>(linenums)
844 else {
845 continue;
846 };
847
848 let mut cur_fun_start_linenumber = None;
857 for linenum in linenums {
858 let line_number = linenum.linenumber.get(LE);
859 if line_number == 0 {
860 cur_fun_start_linenumber = None;
871
872 let symtable_entry = linenum.symbol_table_index_or_virtual_address.get(LE);
876 let Ok(symbol) = symbol_table.symbol(object::SymbolIndex(symtable_entry as usize))
877 else {
878 continue;
879 };
880 let Ok(aux_fun) =
881 symbol_table.aux_function(object::SymbolIndex(symtable_entry as usize))
882 else {
883 continue;
884 };
885
886 if aux_fun.tag_index.get(LE) == 0 {
890 continue;
891 }
892 let Ok(bf_symbol) =
893 symbol_table.symbol(object::SymbolIndex(aux_fun.tag_index.get(LE) as usize))
894 else {
895 continue;
896 };
897 if bf_symbol.name(symbol_table.strings()) != Ok(b".bf") {
900 continue;
901 }
902 let Ok(bf_aux) = symbol_table.get::<object::pe::ImageAuxSymbolFunctionBeginEnd>(
906 object::SymbolIndex(aux_fun.tag_index.get(LE) as usize),
907 1,
908 ) else {
909 continue;
910 };
911 cur_fun_start_linenumber = Some(bf_aux.linenumber.get(LE) as u32);
914 out_section.line_info.insert(
917 sect.address() + symbol.value() as u64,
918 bf_aux.linenumber.get(LE) as u32,
919 );
920 } else if let Some(cur_linenumber) = cur_fun_start_linenumber {
921 let vaddr = linenum.symbol_table_index_or_virtual_address.get(LE);
922 out_section
923 .line_info
924 .insert(sect.address() + vaddr as u64, cur_linenumber + line_number as u32);
925 }
926 }
927 }
928 Ok(())
929}
930
931pub fn get_section_base_name(section: &Section) -> &str {
932 section
935 .name
936 .get(1..)
937 .and_then(|s| s.rfind(['$', '.']))
938 .and_then(|i| section.name.get(..i + 1))
939 .unwrap_or(§ion.name)
940}
941
942fn combine_sections(
943 sections: &mut [Section],
944 symbols: &mut [Symbol],
945 config: &DiffObjConfig,
946) -> Result<()> {
947 let mut data_sections = BTreeMap::<String, Vec<usize>>::new();
948 let mut text_sections = BTreeMap::<String, Vec<usize>>::new();
949 for (i, section) in sections.iter().enumerate() {
950 let base_name = get_section_base_name(section);
951 match section.kind {
952 SectionKind::Data | SectionKind::Bss => {
953 data_sections.entry(base_name.to_string()).or_default().push(i);
954 }
955 SectionKind::Code => {
956 text_sections.entry(base_name.to_string()).or_default().push(i);
957 }
958 _ => {}
959 }
960 }
961 if config.combine_data_sections {
962 for (combined_name, mut section_indices) in data_sections {
963 do_combine_sections(sections, symbols, &mut section_indices, combined_name)?;
964 }
965 }
966 if config.combine_text_sections {
967 for (combined_name, mut section_indices) in text_sections {
968 do_combine_sections(sections, symbols, &mut section_indices, combined_name)?;
969 }
970 }
971 Ok(())
972}
973
974fn do_combine_sections(
975 sections: &mut [Section],
976 symbols: &mut [Symbol],
977 section_indices: &mut [usize],
978 combined_name: String,
979) -> Result<()> {
980 if section_indices.len() < 2 {
981 return Ok(());
982 }
983 section_indices.sort_by(|&a, &b| {
985 let a_name = §ions[a].name;
986 let b_name = §ions[b].name;
987 if a_name.contains('$') && !b_name.contains('$') {
989 return Ordering::Greater;
990 } else if !a_name.contains('$') && b_name.contains('$') {
991 return Ordering::Less;
992 }
993 a_name.cmp(b_name)
994 });
995 let first_section_idx = section_indices[0];
996
997 let mut offsets = Vec::<u64>::with_capacity(section_indices.len());
999 let mut current_offset = 0;
1000 let mut data_size = 0;
1001 let mut num_relocations = 0;
1002 for i in section_indices.iter().copied() {
1003 let section = §ions[i];
1004 if section.address != 0 {
1005 bail!("Section {} ({}) has non-zero address", i, section.name);
1006 }
1007 offsets.push(current_offset);
1008 current_offset += section.size;
1009 let align = section.combined_alignment();
1010 current_offset = align_u64_to(current_offset, align);
1011 data_size += section.data.len();
1012 data_size = align_u64_to(data_size as u64, align) as usize;
1013 num_relocations += section.relocations.len();
1014 }
1015 if data_size > 0 {
1016 ensure!(data_size == current_offset as usize, "Data size mismatch");
1017 }
1018
1019 let mut data = Vec::<u8>::with_capacity(data_size);
1021 let mut relocations = Vec::<Relocation>::with_capacity(num_relocations);
1022 let mut line_info = BTreeMap::<u64, u32>::new();
1023 for (&i, &offset) in section_indices.iter().zip(&offsets) {
1024 let section = &mut sections[i];
1025 section.size = 0;
1026 data.append(&mut section.data.0);
1027 align_data_slice_to(&mut data, section.combined_alignment());
1028 section.relocations.iter_mut().for_each(|r| r.address += offset);
1029 relocations.append(&mut section.relocations);
1030 line_info.append(&mut section.line_info.iter().map(|(&a, &l)| (a + offset, l)).collect());
1031 section.line_info.clear();
1032 if offset > 0 {
1033 section.kind = SectionKind::Unknown;
1034 }
1035 }
1036 {
1037 let first_section = &mut sections[first_section_idx];
1038 first_section.id = format!("{combined_name}-combined");
1039 first_section.name = combined_name;
1040 first_section.size = current_offset;
1041 first_section.data = SectionData(data);
1042 first_section.flags |= SectionFlag::Combined;
1043 first_section.relocations = relocations;
1044 first_section.line_info = line_info;
1045 }
1046
1047 let mut section_symbols = symbols
1049 .iter()
1050 .enumerate()
1051 .filter(|&(_, s)| {
1052 s.kind == SymbolKind::Section && s.section.is_some_and(|i| section_indices.contains(&i))
1053 })
1054 .map(|(i, _)| i)
1055 .collect::<Vec<_>>();
1056 section_symbols.sort_by_key(|&i| symbols[i].section.unwrap());
1057 let target_section_symbol = section_symbols.first().copied();
1058
1059 for symbol in symbols.iter_mut() {
1061 let Some(section_index) = symbol.section else {
1062 continue;
1063 };
1064 let Some(merge_index) = section_indices.iter().position(|&i| i == section_index) else {
1065 continue;
1066 };
1067 symbol.address += offsets[merge_index];
1068 symbol.section = Some(first_section_idx);
1069 }
1070
1071 for relocation in sections.iter_mut().flat_map(|s| s.relocations.iter_mut()) {
1073 let target_symbol = &symbols[relocation.target_symbol];
1074 if target_symbol.kind != SymbolKind::Section {
1075 continue;
1076 }
1077 if !target_symbol.section.is_some_and(|i| section_indices.contains(&i)) {
1078 continue;
1079 }
1080 relocation.target_symbol = target_section_symbol.context("No target section symbol")?;
1082 relocation.addend = relocation
1083 .addend
1084 .checked_add_unsigned(target_symbol.address)
1085 .context("Relocation addend overflow")?;
1086 }
1087
1088 for (i, &symbol_index) in section_symbols.iter().enumerate() {
1090 let symbol = &mut symbols[symbol_index];
1091 symbol.address = 0;
1092 if i > 0 {
1093 symbol.kind = SymbolKind::Unknown;
1095 symbol.section = None;
1096 }
1097 }
1098
1099 Ok(())
1100}
1101
1102#[cfg(feature = "std")]
1103pub fn read(
1104 obj_path: &std::path::Path,
1105 config: &DiffObjConfig,
1106 diff_side: DiffSide,
1107) -> Result<Object> {
1108 let (data, timestamp) = {
1109 let file = std::fs::File::open(obj_path)?;
1110 let timestamp = filetime::FileTime::from_last_modification_time(&file.metadata()?);
1111 (unsafe { memmap2::Mmap::map(&file) }?, timestamp)
1112 };
1113 let mut obj = parse(&data, config, diff_side)?;
1114 obj.path = Some(obj_path.to_path_buf());
1115 obj.timestamp = Some(timestamp);
1116 Ok(obj)
1117}
1118
1119pub fn parse(data: &[u8], config: &DiffObjConfig, diff_side: DiffSide) -> Result<Object> {
1120 let obj_file = object::File::parse(data)?;
1121 let mut arch = new_arch(&obj_file, diff_side)?;
1122 let split_meta = parse_split_meta(&obj_file)?;
1123 let comment_syms = parse_mw_comment_syms(&obj_file)?;
1124 let (mut sections, section_indices) =
1125 map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?;
1126 let (mut symbols, symbol_indices) = map_symbols(
1127 arch.as_ref(),
1128 &obj_file,
1129 §ion_indices,
1130 split_meta.as_ref(),
1131 comment_syms.as_ref(),
1132 config,
1133 )?;
1134 map_relocations(arch.as_ref(), &obj_file, &mut sections, §ion_indices, &symbol_indices)?;
1135 infer_symbol_sizes(arch.as_ref(), &mut symbols, §ions)?;
1137 parse_line_info(&obj_file, &mut sections, §ion_indices, data)?;
1138 if config.combine_data_sections || config.combine_text_sections {
1139 combine_sections(&mut sections, &mut symbols, config)?;
1140 }
1141 add_section_symbols(§ions, &mut symbols);
1142 arch.post_init(§ions, &symbols, &symbol_indices);
1143 let mut obj = Object {
1144 arch,
1145 endianness: obj_file.endianness(),
1146 symbols,
1147 sections,
1148 split_meta,
1149 #[cfg(feature = "std")]
1150 path: None,
1151 #[cfg(feature = "std")]
1152 timestamp: None,
1153 flow_analysis_results: Default::default(),
1154 };
1155
1156 perform_data_flow_analysis(&mut obj, config)?;
1160 Ok(obj)
1161}
1162
1163#[cfg(feature = "std")]
1164pub fn has_function(obj_path: &std::path::Path, symbol_name: &str) -> Result<bool> {
1165 let data = {
1166 let file = std::fs::File::open(obj_path)?;
1167 unsafe { memmap2::Mmap::map(&file) }?
1168 };
1169 Ok(object::File::parse(&*data)?
1170 .symbol_by_name(symbol_name)
1171 .filter(|o| o.kind() == object::SymbolKind::Text)
1172 .is_some())
1173}
1174
1175fn parse_split_meta(obj_file: &object::File) -> Result<Option<SplitMeta>> {
1176 Ok(if let Some(section) = obj_file.section_by_name(SPLITMETA_SECTION) {
1177 Some(SplitMeta::from_section(section, obj_file.endianness(), obj_file.is_64())?)
1178 } else {
1179 None
1180 })
1181}
1182
1183fn parse_mw_comment_syms(obj_file: &object::File) -> Result<Option<Vec<CommentSym>>> {
1184 Ok(if let Some(section) = obj_file.section_by_name(COMMENT_SECTION) {
1185 let data = section.uncompressed_data()?;
1186 let mut reader: &[u8] = data.as_ref();
1187 if let Ok(_header) = MWComment::from_reader(obj_file, &mut reader) {
1188 CommentSym::from_reader(obj_file, &mut reader)?; let mut comment_syms = Vec::with_capacity(obj_file.symbols().count());
1190 for _symbol in obj_file.symbols() {
1191 let comment_sym = CommentSym::from_reader(obj_file, &mut reader)?;
1192 comment_syms.push(comment_sym);
1193 }
1194 Some(comment_syms)
1195 } else {
1196 None
1198 }
1199 } else {
1200 None
1201 })
1202}
1203
1204#[cfg(test)]
1205mod test {
1206 use super::*;
1207
1208 #[test]
1209 fn test_combine_sections() {
1210 let mut sections = vec![
1211 Section {
1212 id: ".text-0".to_string(),
1213 name: ".text".to_string(),
1214 size: 8,
1215 kind: SectionKind::Code,
1216 data: SectionData(vec![0; 8]),
1217 relocations: vec![
1218 Relocation {
1219 address: 0,
1220 flags: RelocationFlags::Elf(0),
1221 target_symbol: 0,
1222 addend: 0,
1223 },
1224 Relocation {
1225 address: 2,
1226 flags: RelocationFlags::Elf(0),
1227 target_symbol: 1,
1228 addend: 0,
1229 },
1230 Relocation {
1231 address: 4,
1232 flags: RelocationFlags::Elf(0),
1233 target_symbol: 3,
1234 addend: 2,
1235 },
1236 ],
1237 ..Default::default()
1238 },
1239 Section {
1240 id: ".data-0".to_string(),
1241 name: ".data".to_string(),
1242 size: 4,
1243 kind: SectionKind::Data,
1244 data: SectionData(vec![1, 2, 3, 4]),
1245 relocations: vec![Relocation {
1246 address: 0,
1247 flags: RelocationFlags::Elf(0),
1248 target_symbol: 2,
1249 addend: 0,
1250 }],
1251 line_info: [(0, 1)].into_iter().collect(),
1252 ..Default::default()
1253 },
1254 Section {
1255 id: ".data-1".to_string(),
1256 name: ".data".to_string(),
1257 size: 4,
1258 kind: SectionKind::Data,
1259 data: SectionData(vec![5, 6, 7, 8]),
1260 relocations: vec![Relocation {
1261 address: 0,
1262 flags: RelocationFlags::Elf(0),
1263 target_symbol: 2,
1264 addend: 0,
1265 }],
1266 ..Default::default()
1267 },
1268 Section {
1269 id: ".data-2".to_string(),
1270 name: ".data".to_string(),
1271 size: 4,
1272 kind: SectionKind::Data,
1273 data: SectionData(vec![9, 10, 11, 12]),
1274 line_info: [(0, 2)].into_iter().collect(),
1275 ..Default::default()
1276 },
1277 ];
1278 let mut symbols = vec![
1279 Symbol {
1280 name: ".data".to_string(),
1281 address: 0,
1282 kind: SymbolKind::Section,
1283 section: Some(2),
1284 ..Default::default()
1285 },
1286 Symbol {
1287 name: "symbol".to_string(),
1288 address: 0,
1289 kind: SymbolKind::Object,
1290 size: 4,
1291 section: Some(2),
1292 ..Default::default()
1293 },
1294 Symbol {
1295 name: "function".to_string(),
1296 address: 0,
1297 size: 8,
1298 kind: SymbolKind::Function,
1299 section: Some(0),
1300 ..Default::default()
1301 },
1302 Symbol {
1303 name: ".data".to_string(),
1304 address: 0,
1305 kind: SymbolKind::Section,
1306 section: Some(3),
1307 ..Default::default()
1308 },
1309 ];
1310 do_combine_sections(&mut sections, &mut symbols, &mut [1, 2, 3], ".data".to_string())
1311 .unwrap();
1312 assert_eq!(sections[1].data.0, (1..=12).collect::<Vec<_>>());
1313 insta::assert_debug_snapshot!((sections, symbols));
1314 }
1315}