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