std\backtrace\src\symbolize\gimli/
coff.rs

1use super::mystd::path::Path;
2use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash};
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5use core::convert::TryFrom;
6use object::pe::{ImageDosHeader, ImageSymbol};
7use object::read::coff::ImageSymbol as _;
8use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
9use object::read::StringTable;
10use object::LittleEndian as LE;
11
12#[cfg(target_pointer_width = "32")]
13type Pe = object::pe::ImageNtHeaders32;
14#[cfg(target_pointer_width = "64")]
15type Pe = object::pe::ImageNtHeaders64;
16
17impl Mapping {
18    pub fn new(path: &Path) -> Option<Mapping> {
19        let map = super::mmap(path)?;
20        Mapping::mk(map, |data, stash| {
21            Context::new(stash, Object::parse(data)?, None, None)
22        })
23    }
24}
25
26pub struct Object<'a> {
27    data: &'a [u8],
28    sections: SectionTable<'a>,
29    symbols: Vec<(usize, &'a ImageSymbol)>,
30    strings: StringTable<'a>,
31}
32
33pub fn get_image_base(data: &[u8]) -> Option<usize> {
34    let dos_header = ImageDosHeader::parse(data).ok()?;
35    let mut offset = dos_header.nt_headers_offset().into();
36    let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
37    usize::try_from(nt_headers.optional_header().image_base()).ok()
38}
39
40impl<'a> Object<'a> {
41    fn parse(data: &'a [u8]) -> Option<Object<'a>> {
42        let dos_header = ImageDosHeader::parse(data).ok()?;
43        let mut offset = dos_header.nt_headers_offset().into();
44        let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
45        let sections = nt_headers.sections(data, offset).ok()?;
46        let symtab = nt_headers.symbols(data).ok()?;
47        let strings = symtab.strings();
48        let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?;
49
50        // Collect all the symbols into a local vector which is sorted
51        // by address and contains enough data to learn about the symbol
52        // name. Note that we only look at function symbols and also
53        // note that the sections are 1-indexed because the zero section
54        // is special (apparently).
55        let mut symbols = Vec::new();
56        for (_, sym) in symtab.iter() {
57            if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION {
58                continue;
59            }
60            let Some(section_index) = sym.section() else {
61                continue;
62            };
63            let addr = usize::try_from(sym.value.get(LE)).ok()?;
64            let section = sections.section(section_index).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}
109
110pub(super) fn handle_split_dwarf<'data>(
111    _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
112    _stash: &'data Stash,
113    _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
114) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
115    None
116}