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