use std::borrow::Cow;
use std::fmt;
use std::io::Cursor;
use std::marker::PhantomData;
use failure::Fail;
use goblin::{error::Error as GoblinError, pe};
use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Uuid};
use crate::base::*;
use crate::private::Parse;
pub use goblin::pe::exception::*;
pub use goblin::pe::section_table::SectionTable;
#[derive(Debug, Fail)]
pub enum PeError {
#[fail(display = "invalid PE file")]
BadObject(#[fail(cause)] GoblinError),
}
pub struct PeObject<'d> {
pe: pe::PE<'d>,
data: &'d [u8],
}
impl<'d> PeObject<'d> {
pub fn test(data: &[u8]) -> bool {
match goblin::peek(&mut Cursor::new(data)) {
Ok(goblin::Hint::PE) => true,
_ => false,
}
}
pub fn parse(data: &'d [u8]) -> Result<Self, PeError> {
pe::PE::parse(data)
.map(|pe| PeObject { pe, data })
.map_err(PeError::BadObject)
}
pub fn file_format(&self) -> FileFormat {
FileFormat::Pe
}
pub fn code_id(&self) -> Option<CodeId> {
let header = &self.pe.header;
let optional_header = header.optional_header.as_ref()?;
let timestamp = header.coff_header.time_date_stamp;
let size_of_image = optional_header.windows_fields.size_of_image;
let string = format!("{:08x}{:x}", timestamp, size_of_image);
Some(CodeId::new(string))
}
pub fn debug_id(&self) -> DebugId {
self.pe
.debug_data
.as_ref()
.and_then(|debug_data| debug_data.codeview_pdb70_debug_info.as_ref())
.and_then(|debug_info| {
let mut data = debug_info.signature;
data[0..4].reverse(); data[4..6].reverse(); data[6..8].reverse();
let uuid = Uuid::from_slice(&data).ok()?;
Some(DebugId::from_parts(uuid, debug_info.age))
})
.unwrap_or_default()
}
pub fn debug_file_name(&self) -> Option<Cow<'_, str>> {
self.pe
.debug_data
.as_ref()
.and_then(|debug_data| debug_data.codeview_pdb70_debug_info.as_ref())
.map(|debug_info| {
String::from_utf8_lossy(&debug_info.filename[..debug_info.filename.len() - 1])
})
}
pub fn arch(&self) -> Arch {
let machine = self.pe.header.coff_header.machine;
crate::pdb::arch_from_machine(machine.into())
}
pub fn kind(&self) -> ObjectKind {
if self.pe.is_lib {
ObjectKind::Library
} else {
ObjectKind::Executable
}
}
pub fn load_address(&self) -> u64 {
self.pe.image_base as u64
}
pub fn has_symbols(&self) -> bool {
!self.pe.exports.is_empty()
}
pub fn symbols(&self) -> PeSymbolIterator<'d, '_> {
PeSymbolIterator {
exports: self.pe.exports.iter(),
}
}
pub fn symbol_map(&self) -> SymbolMap<'d> {
self.symbols().collect()
}
pub fn has_debug_info(&self) -> bool {
false
}
pub fn debug_session(&self) -> Result<PeDebugSession<'d>, PeError> {
Ok(PeDebugSession { _ph: PhantomData })
}
pub fn has_unwind_info(&self) -> bool {
self.exception_data().map_or(false, |e| !e.is_empty())
}
pub fn data(&self) -> &'d [u8] {
self.data
}
pub fn sections(&self) -> &[SectionTable] {
&self.pe.sections
}
pub fn exception_data(&self) -> Option<&ExceptionData> {
self.pe.exception_data.as_ref()
}
}
impl fmt::Debug for PeObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PeObject")
.field("code_id", &self.code_id())
.field("debug_id", &self.debug_id())
.field("debug_file_name", &self.debug_file_name())
.field("arch", &self.arch())
.field("kind", &self.kind())
.field("load_address", &format_args!("{:#x}", self.load_address()))
.field("has_symbols", &self.has_symbols())
.field("has_debug_info", &self.has_debug_info())
.field("has_unwind_info", &self.has_unwind_info())
.finish()
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for PeObject<'d> {
type Ref = PeObject<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
self
}
}
impl<'d> Parse<'d> for PeObject<'d> {
type Error = PeError;
fn test(data: &[u8]) -> bool {
Self::test(data)
}
fn parse(data: &'d [u8]) -> Result<Self, PeError> {
Self::parse(data)
}
}
impl<'d> ObjectLike for PeObject<'d> {
type Error = PeError;
type Session = PeDebugSession<'d>;
fn file_format(&self) -> FileFormat {
self.file_format()
}
fn code_id(&self) -> Option<CodeId> {
self.code_id()
}
fn debug_id(&self) -> DebugId {
self.debug_id()
}
fn debug_file_name(&self) -> Option<Cow<'_, str>> {
self.debug_file_name()
}
fn arch(&self) -> Arch {
self.arch()
}
fn kind(&self) -> ObjectKind {
self.kind()
}
fn load_address(&self) -> u64 {
self.load_address()
}
fn has_symbols(&self) -> bool {
self.has_symbols()
}
fn symbols(&self) -> DynIterator<'_, Symbol<'_>> {
Box::new(self.symbols())
}
fn symbol_map(&self) -> SymbolMap<'_> {
self.symbol_map()
}
fn has_debug_info(&self) -> bool {
self.has_debug_info()
}
fn debug_session(&self) -> Result<Self::Session, Self::Error> {
self.debug_session()
}
fn has_unwind_info(&self) -> bool {
self.has_unwind_info()
}
}
pub struct PeSymbolIterator<'d, 'o> {
exports: std::slice::Iter<'o, pe::export::Export<'d>>,
}
impl<'d, 'o> Iterator for PeSymbolIterator<'d, 'o> {
type Item = Symbol<'d>;
fn next(&mut self) -> Option<Self::Item> {
self.exports.next().map(|export| Symbol {
name: export.name.map(Cow::Borrowed),
address: export.rva as u64,
size: export.size as u64,
})
}
}
#[derive(Debug)]
pub struct PeDebugSession<'d> {
_ph: PhantomData<&'d ()>,
}
impl<'d> PeDebugSession<'d> {
pub fn functions(&mut self) -> PeFunctionIterator<'_> {
std::iter::empty()
}
}
impl DebugSession for PeDebugSession<'_> {
type Error = PeError;
fn functions(&mut self) -> DynIterator<'_, Result<Function<'_>, Self::Error>> {
Box::new(std::iter::empty())
}
}
pub type PeFunctionIterator<'s> = std::iter::Empty<Result<Function<'s>, PeError>>;