1 use super::{Mapping, Path, Stash, Vec};
2 use object::pe::{ImageDosHeader, ImageSymbol};
3 use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
4 use object::read::StringTable;
5 use object::{Bytes, LittleEndian as LE};
6 
7 #[cfg(target_pointer_width = "32")]
8 type Pe = object::pe::ImageNtHeaders32;
9 #[cfg(target_pointer_width = "64")]
10 type Pe = object::pe::ImageNtHeaders64;
11 use std::convert::TryFrom;
12 
13 impl Mapping {
new(path: &Path) -> Option<Mapping>14     pub fn new(path: &Path) -> Option<Mapping> {
15         let map = super::mmap(path)?;
16         let stash = Stash::new();
17         let cx = super::cx(&stash, Object::parse(&map)?)?;
18         Some(mk!(Mapping { map, cx, stash }))
19     }
20 }
21 
22 pub struct Object<'a> {
23     data: Bytes<'a>,
24     sections: SectionTable<'a>,
25     symbols: Vec<(usize, &'a ImageSymbol)>,
26     strings: StringTable<'a>,
27 }
28 
get_image_base(data: &[u8]) -> Option<usize>29 pub fn get_image_base(data: &[u8]) -> Option<usize> {
30     let data = Bytes(data);
31     let dos_header = ImageDosHeader::parse(data).ok()?;
32     let (nt_headers, _, _) = dos_header.nt_headers::<Pe>(data).ok()?;
33     usize::try_from(nt_headers.optional_header().image_base()).ok()
34 }
35 
36 impl<'a> Object<'a> {
parse(data: &'a [u8]) -> Option<Object<'a>>37     fn parse(data: &'a [u8]) -> Option<Object<'a>> {
38         let data = Bytes(data);
39         let dos_header = ImageDosHeader::parse(data).ok()?;
40         let (nt_headers, _, nt_tail) = dos_header.nt_headers::<Pe>(data).ok()?;
41         let sections = nt_headers.sections(nt_tail).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)?;
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 
section(&self, _: &Stash, name: &str) -> Option<&'a [u8]>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                 .0,
85         )
86     }
87 
search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]>88     pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
89         // Note that unlike other formats COFF doesn't embed the size of
90         // each symbol. As a last ditch effort search for the *closest*
91         // symbol to a particular address and return that one. This gets
92         // really wonky once symbols start getting removed because the
93         // symbols returned here can be totally incorrect, but we have
94         // no idea of knowing how to detect that.
95         let addr = usize::try_from(addr).ok()?;
96         let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
97             Ok(i) => i,
98             // typically `addr` isn't in the array, but `i` is where
99             // we'd insert it, so the previous position must be the
100             // greatest less than `addr`
101             Err(i) => i.checked_sub(1)?,
102         };
103         self.symbols[i].1.name(self.strings).ok()
104     }
105 }
106