use std::borrow::Cow;
use std::fmt;
use std::io::Cursor;
use std::sync::Arc;
use failure::Fail;
use lazycell::LazyCell;
use parking_lot::RwLock;
use pdb::{AddressMap, FallibleIterator, MachineType, Module, ModuleInfo, SymbolData};
use symbolic_common::{
derive_failure, split_path_bytes, Arch, AsSelf, CodeId, CpuFamily, DebugId, Name, SelfCell,
Uuid,
};
use crate::base::*;
use crate::private::Parse;
type Pdb<'d> = pdb::PDB<'d, Cursor<&'d [u8]>>;
const MAGIC_BIG: &[u8] = b"Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
#[doc(hidden)]
pub use pdb;
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum PdbErrorKind {
#[fail(display = "invalid pdb file")]
BadObject,
}
derive_failure!(
PdbError,
PdbErrorKind,
doc = "An error when dealing with [`PdbObject`](struct.PdbObject.html)."
);
impl From<pdb::Error> for PdbError {
fn from(error: pdb::Error) -> Self {
error.context(PdbErrorKind::BadObject).into()
}
}
pub struct PdbObject<'d> {
pdb: Arc<RwLock<Pdb<'d>>>,
debug_info: Arc<pdb::DebugInformation<'d>>,
pdb_info: pdb::PDBInformation<'d>,
public_syms: pdb::SymbolTable<'d>,
data: &'d [u8],
}
unsafe impl Send for PdbObject<'_> {}
unsafe impl Sync for PdbObject<'_> {}
impl<'d> PdbObject<'d> {
pub fn test(data: &[u8]) -> bool {
data.starts_with(MAGIC_BIG)
}
pub fn parse(data: &'d [u8]) -> Result<Self, PdbError> {
let mut pdb = Pdb::open(Cursor::new(data))?;
let dbi = pdb.debug_information()?;
let pdbi = pdb.pdb_information()?;
let pubi = pdb.global_symbols()?;
Ok(PdbObject {
pdb: Arc::new(RwLock::new(pdb)),
debug_info: Arc::new(dbi),
pdb_info: pdbi,
public_syms: pubi,
data,
})
}
pub fn file_format(&self) -> FileFormat {
FileFormat::Pdb
}
pub fn code_id(&self) -> Option<CodeId> {
None
}
pub fn debug_id(&self) -> DebugId {
match Uuid::from_slice(&self.pdb_info.guid.as_bytes()[..]) {
Ok(uuid) => DebugId::from_parts(uuid, self.pdb_info.age),
Err(_) => DebugId::default(),
}
}
pub fn arch(&self) -> Arch {
self.debug_info
.machine_type()
.ok()
.map(arch_from_machine)
.unwrap_or_default()
}
pub fn kind(&self) -> ObjectKind {
ObjectKind::Debug
}
pub fn load_address(&self) -> u64 {
0
}
pub fn has_symbols(&self) -> bool {
true
}
pub fn symbols(&self) -> PdbSymbolIterator<'d, '_> {
PdbSymbolIterator {
symbols: self.public_syms.iter(),
address_map: self.pdb.write().address_map().ok(),
}
}
pub fn symbol_map(&self) -> SymbolMap<'d> {
self.symbols().collect()
}
pub fn has_debug_info(&self) -> bool {
true
}
pub fn debug_session(&self) -> Result<PdbDebugSession<'d>, PdbError> {
PdbDebugSession::build(self, self.debug_info.clone())
}
pub fn has_unwind_info(&self) -> bool {
self.arch().cpu_family() == CpuFamily::Intel32
}
pub fn data(&self) -> &'d [u8] {
self.data
}
#[doc(hidden)]
pub fn inner(&self) -> &RwLock<Pdb<'d>> {
&self.pdb
}
}
impl fmt::Debug for PdbObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PdbObject")
.field("debug_id", &self.debug_id())
.field("arch", &self.arch())
.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 PdbObject<'d> {
type Ref = PdbObject<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
unsafe { std::mem::transmute(self) }
}
}
impl<'d> Parse<'d> for PdbObject<'d> {
type Error = PdbError;
fn test(data: &[u8]) -> bool {
Self::test(data)
}
fn parse(data: &'d [u8]) -> Result<Self, PdbError> {
Self::parse(data)
}
}
impl<'d> ObjectLike for PdbObject<'d> {
type Error = PdbError;
type Session = PdbDebugSession<'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 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(crate) fn arch_from_machine(machine: MachineType) -> Arch {
match machine {
MachineType::X86 => Arch::X86,
MachineType::Amd64 => Arch::Amd64,
MachineType::Arm => Arch::Arm,
MachineType::Arm64 => Arch::Arm64,
MachineType::PowerPC => Arch::Ppc,
_ => Arch::Unknown,
}
}
pub struct PdbSymbolIterator<'d, 'o> {
symbols: pdb::SymbolIter<'o>,
address_map: Option<AddressMap<'d>>,
}
impl<'d, 'o> Iterator for PdbSymbolIterator<'d, 'o> {
type Item = Symbol<'d>;
fn next(&mut self) -> Option<Self::Item> {
let address_map = self.address_map.as_ref()?;
while let Ok(Some(symbol)) = self.symbols.next() {
if let Ok(SymbolData::PublicSymbol(public)) = symbol.parse() {
if !public.function {
continue;
}
let address = match public.offset.to_rva(address_map) {
Some(address) => address,
None => continue,
};
let name = symbol.name().ok().map(|name| {
let cow = name.to_string();
Cow::from(String::from(if cow.starts_with('_') {
&cow[1..]
} else {
&cow
}))
});
return Some(Symbol {
name,
address: u64::from(address.0),
size: 0,
});
}
}
None
}
}
struct PdbDebugInfo<'d> {
pdb: Arc<RwLock<Pdb<'d>>>,
modules: Vec<Module<'d>>,
module_infos: Vec<LazyCell<Option<ModuleInfo<'d>>>>,
address_map: pdb::AddressMap<'d>,
string_table: Option<pdb::StringTable<'d>>,
symbol_map: SymbolMap<'d>,
}
impl<'d> PdbDebugInfo<'d> {
fn build(
pdb: &PdbObject<'d>,
debug_info: &'d pdb::DebugInformation<'d>,
) -> Result<Self, PdbError> {
let symbol_map = pdb.symbol_map();
let modules = debug_info.modules()?.collect::<Vec<_>>()?;
let module_infos = modules.iter().map(|_| LazyCell::new()).collect();
let mut p = pdb.pdb.write();
let address_map = p.address_map()?;
let string_table = match p.string_table() {
Ok(string_table) => Some(string_table),
Err(pdb::Error::StreamNameNotFound) => None,
Err(e) => return Err(e.into()),
};
drop(p);
Ok(PdbDebugInfo {
pdb: pdb.pdb.clone(),
modules,
module_infos,
address_map,
string_table,
symbol_map,
})
}
fn units(&'d self) -> PdbUnitIterator<'_> {
PdbUnitIterator {
debug_info: self,
index: 0,
}
}
fn get_module(&'d self, index: usize) -> Result<Option<&ModuleInfo<'_>>, PdbError> {
let cell = match self.module_infos.get(index) {
Some(cell) => cell,
None => return Ok(None),
};
let module_opt = cell.try_borrow_with(|| {
let module = &self.modules[index];
self.pdb.write().module_info(module)
})?;
Ok(module_opt.as_ref())
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for PdbDebugInfo<'d> {
type Ref = PdbDebugInfo<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
unsafe { std::mem::transmute(self) }
}
}
pub struct PdbDebugSession<'d> {
cell: SelfCell<Arc<pdb::DebugInformation<'d>>, PdbDebugInfo<'d>>,
}
impl<'d> PdbDebugSession<'d> {
fn build(
pdb: &PdbObject<'d>,
debug_info: Arc<pdb::DebugInformation<'d>>,
) -> Result<Self, PdbError> {
let cell = SelfCell::try_new(debug_info, |debug_info| {
PdbDebugInfo::build(pdb, unsafe { &*debug_info })
})?;
Ok(PdbDebugSession { cell })
}
pub fn functions(&mut self) -> PdbFunctionIterator<'_> {
PdbFunctionIterator {
units: self.cell.get().units(),
functions: Vec::new().into_iter(),
finished: false,
}
}
}
impl DebugSession for PdbDebugSession<'_> {
type Error = PdbError;
fn functions(&mut self) -> DynIterator<'_, Result<Function<'_>, Self::Error>> {
Box::new(self.functions())
}
}
struct Unit<'s> {
debug_info: &'s PdbDebugInfo<'s>,
module: &'s pdb::ModuleInfo<'s>,
}
impl<'s> Unit<'s> {
fn functions(&self) -> Result<Vec<Function<'s>>, PdbError> {
let address_map = &self.debug_info.address_map;
let string_table = &self.debug_info.string_table;
let symbol_map = &self.debug_info.symbol_map;
let program = self.module.line_program()?;
let mut symbols = self.module.symbols()?;
let mut functions = Vec::new();
while let Some(symbol) = symbols.next()? {
let proc = match symbol.parse() {
Ok(SymbolData::Procedure(proc)) => proc,
_ => continue,
};
let address = match proc.offset.to_rva(&address_map) {
Some(addr) => u64::from(addr.0),
None => continue,
};
let name = match symbol_map.lookup(address) {
Some(symbol) => Name::new(symbol.name().unwrap_or_default()),
None => Name::new(symbol.name()?.to_string()),
};
let mut lines = Vec::new();
let mut line_iter = program.lines_at_offset(proc.offset);
while let Some(line_info) = line_iter.next()? {
let rva = match line_info.offset.to_rva(&address_map) {
Some(rva) => u64::from(rva.0),
None => continue,
};
let file_info = program.get_file_info(line_info.file_index)?;
let file_path = match string_table {
Some(string_table) => file_info.name.to_raw_string(&string_table)?,
None => "".into(),
};
let (dir, name) = split_path_bytes(file_path.as_bytes());
lines.push(LineInfo {
address: rva,
file: FileInfo {
dir: dir.unwrap_or_default(),
name,
},
line: line_info.line_start.into(),
});
}
let func = Function {
address,
size: proc.len.into(),
name,
compilation_dir: &[],
lines,
inlinees: Vec::new(),
inline: false,
};
functions.push(func);
}
dmsort::sort_by_key(&mut functions, |f| f.address);
Ok(functions)
}
}
struct PdbUnitIterator<'s> {
debug_info: &'s PdbDebugInfo<'s>,
index: usize,
}
impl<'s> Iterator for PdbUnitIterator<'s> {
type Item = Result<Unit<'s>, PdbError>;
fn next(&mut self) -> Option<Self::Item> {
let debug_info = self.debug_info;
while self.index < debug_info.modules.len() {
let result = debug_info.get_module(self.index);
self.index += 1;
let module = match result {
Ok(Some(module)) => module,
Ok(None) => continue,
Err(error) => return Some(Err(error)),
};
return Some(Ok(Unit { debug_info, module }));
}
None
}
}
pub struct PdbFunctionIterator<'s> {
units: PdbUnitIterator<'s>,
functions: std::vec::IntoIter<Function<'s>>,
finished: bool,
}
impl<'s> Iterator for PdbFunctionIterator<'s> {
type Item = Result<Function<'s>, PdbError>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
if let Some(func) = self.functions.next() {
return Some(Ok(func));
}
let unit = match self.units.next() {
Some(Ok(unit)) => unit,
Some(Err(error)) => return Some(Err(error)),
None => break,
};
self.functions = match unit.functions() {
Ok(functions) => functions.into_iter(),
Err(error) => return Some(Err(error)),
};
}
self.finished = true;
None
}
}
impl std::iter::FusedIterator for PdbFunctionIterator<'_> {}