]> git.proxmox.com Git - rustc.git/blob - library/backtrace/src/symbolize/gimli/coff.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / library / backtrace / src / symbolize / gimli / coff.rs
1 use super::{Context, Mapping, Path, Stash, Vec};
2 use core::convert::TryFrom;
3 use object::pe::{ImageDosHeader, ImageSymbol};
4 use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
5 use object::read::StringTable;
6 use object::LittleEndian as LE;
7
8 #[cfg(target_pointer_width = "32")]
9 type Pe = object::pe::ImageNtHeaders32;
10 #[cfg(target_pointer_width = "64")]
11 type Pe = object::pe::ImageNtHeaders64;
12
13 impl Mapping {
14 pub fn new(path: &Path) -> Option<Mapping> {
15 let map = super::mmap(path)?;
16 Mapping::mk(map, |data, stash| {
17 Context::new(stash, Object::parse(data)?, None)
18 })
19 }
20 }
21
22 pub struct Object<'a> {
23 data: &'a [u8],
24 sections: SectionTable<'a>,
25 symbols: Vec<(usize, &'a ImageSymbol)>,
26 strings: StringTable<'a>,
27 }
28
29 pub fn get_image_base(data: &[u8]) -> Option<usize> {
30 let dos_header = ImageDosHeader::parse(data).ok()?;
31 let mut offset = dos_header.nt_headers_offset().into();
32 let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
33 usize::try_from(nt_headers.optional_header().image_base()).ok()
34 }
35
36 impl<'a> Object<'a> {
37 fn parse(data: &'a [u8]) -> Option<Object<'a>> {
38 let dos_header = ImageDosHeader::parse(data).ok()?;
39 let mut offset = dos_header.nt_headers_offset().into();
40 let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
41 let sections = nt_headers.sections(data, offset).ok()?;
42 let symtab = nt_headers.symbols(data).ok()?;
43 let strings = symtab.strings();
44 let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?;
45
46 // Collect all the symbols into a local vector which is sorted
47 // by address and contains enough data to learn about the symbol
48 // name. Note that we only look at function symbols and also
49 // note that the sections are 1-indexed because the zero section
50 // is special (apparently).
51 let mut symbols = Vec::new();
52 let mut i = 0;
53 let len = symtab.len();
54 while i < len {
55 let sym = symtab.symbol(i).ok()?;
56 i += 1 + sym.number_of_aux_symbols as usize;
57 let section_number = sym.section_number.get(LE);
58 if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION || section_number == 0 {
59 continue;
60 }
61 let addr = usize::try_from(sym.value.get(LE)).ok()?;
62 let section = sections
63 .section(usize::try_from(section_number).ok()?)
64 .ok()?;
65 let va = usize::try_from(section.virtual_address.get(LE)).ok()?;
66 symbols.push((addr + va + image_base, sym));
67 }
68 symbols.sort_unstable_by_key(|x| x.0);
69 Some(Object {
70 data,
71 sections,
72 strings,
73 symbols,
74 })
75 }
76
77 pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
78 Some(
79 self.sections
80 .section_by_name(self.strings, name.as_bytes())?
81 .1
82 .pe_data(self.data)
83 .ok()?,
84 )
85 }
86
87 pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
88 // Note that unlike other formats COFF doesn't embed the size of
89 // each symbol. As a last ditch effort search for the *closest*
90 // symbol to a particular address and return that one. This gets
91 // really wonky once symbols start getting removed because the
92 // symbols returned here can be totally incorrect, but we have
93 // no idea of knowing how to detect that.
94 let addr = usize::try_from(addr).ok()?;
95 let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
96 Ok(i) => i,
97 // typically `addr` isn't in the array, but `i` is where
98 // we'd insert it, so the previous position must be the
99 // greatest less than `addr`
100 Err(i) => i.checked_sub(1)?,
101 };
102 self.symbols[i].1.name(self.strings).ok()
103 }
104
105 pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
106 None
107 }
108 }