blob: 84d334207fb27cb9117cafeb8ca622d8942d5582 [file] [log] [blame]
use super::{Context, Mapping, Path, Stash, Vec};
use core::convert::TryFrom;
use object::pe::{ImageDosHeader, ImageSymbol};
use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
use object::read::StringTable;
use object::LittleEndian as LE;
#[cfg(target_pointer_width = "32")]
type Pe = object::pe::ImageNtHeaders32;
#[cfg(target_pointer_width = "64")]
type Pe = object::pe::ImageNtHeaders64;
impl Mapping {
pub fn new(path: &Path) -> Option<Mapping> {
let map = super::mmap(path)?;
Mapping::mk(map, |data, stash| {
Context::new(stash, Object::parse(data)?, None)
})
}
}
pub struct Object<'a> {
data: &'a [u8],
sections: SectionTable<'a>,
symbols: Vec<(usize, &'a ImageSymbol)>,
strings: StringTable<'a>,
}
pub fn get_image_base(data: &[u8]) -> Option<usize> {
let dos_header = ImageDosHeader::parse(data).ok()?;
let mut offset = dos_header.nt_headers_offset().into();
let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
usize::try_from(nt_headers.optional_header().image_base()).ok()
}
impl<'a> Object<'a> {
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
let dos_header = ImageDosHeader::parse(data).ok()?;
let mut offset = dos_header.nt_headers_offset().into();
let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
let sections = nt_headers.sections(data, offset).ok()?;
let symtab = nt_headers.symbols(data).ok()?;
let strings = symtab.strings();
let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?;
// Collect all the symbols into a local vector which is sorted
// by address and contains enough data to learn about the symbol
// name. Note that we only look at function symbols and also
// note that the sections are 1-indexed because the zero section
// is special (apparently).
let mut symbols = Vec::new();
let mut i = 0;
let len = symtab.len();
while i < len {
let sym = symtab.symbol(i).ok()?;
i += 1 + sym.number_of_aux_symbols as usize;
let section_number = sym.section_number.get(LE);
if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION || section_number == 0 {
continue;
}
let addr = usize::try_from(sym.value.get(LE)).ok()?;
let section = sections
.section(usize::try_from(section_number).ok()?)
.ok()?;
let va = usize::try_from(section.virtual_address.get(LE)).ok()?;
symbols.push((addr + va + image_base, sym));
}
symbols.sort_unstable_by_key(|x| x.0);
Some(Object {
data,
sections,
strings,
symbols,
})
}
pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
Some(
self.sections
.section_by_name(self.strings, name.as_bytes())?
.1
.pe_data(self.data)
.ok()?,
)
}
pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
// Note that unlike other formats COFF doesn't embed the size of
// each symbol. As a last ditch effort search for the *closest*
// symbol to a particular address and return that one. This gets
// really wonky once symbols start getting removed because the
// symbols returned here can be totally incorrect, but we have
// no idea of knowing how to detect that.
let addr = usize::try_from(addr).ok()?;
let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
Ok(i) => i,
// typically `addr` isn't in the array, but `i` is where
// we'd insert it, so the previous position must be the
// greatest less than `addr`
Err(i) => i.checked_sub(1)?,
};
self.symbols[i].1.name(self.strings).ok()
}
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
None
}
}