use std::borrow::Cow;
use std::fmt;
use std::iter::FromIterator;
use std::ops::{Bound, Deref, RangeBounds};
use std::str::FromStr;
use failure::Fail;
use symbolic_common::{join_path, Arch, CodeId, DebugId, Name};
#[derive(Debug, Fail, Clone, Copy)]
#[fail(display = "unknown object class")]
pub struct UnknownObjectKindError;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub enum ObjectKind {
None,
Relocatable,
Executable,
Library,
Dump,
Debug,
Other,
}
impl ObjectKind {
pub fn name(self) -> &'static str {
match self {
ObjectKind::None => "none",
ObjectKind::Relocatable => "rel",
ObjectKind::Executable => "exe",
ObjectKind::Library => "lib",
ObjectKind::Dump => "dump",
ObjectKind::Debug => "dbg",
ObjectKind::Other => "other",
}
}
pub fn human_name(self) -> &'static str {
match self {
ObjectKind::None => "file",
ObjectKind::Relocatable => "object",
ObjectKind::Executable => "executable",
ObjectKind::Library => "library",
ObjectKind::Dump => "memory dump",
ObjectKind::Debug => "debug companion",
ObjectKind::Other => "file",
}
}
}
impl fmt::Display for ObjectKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.write_str(self.human_name())
} else {
f.write_str(self.name())
}
}
}
impl FromStr for ObjectKind {
type Err = UnknownObjectKindError;
fn from_str(string: &str) -> Result<ObjectKind, UnknownObjectKindError> {
Ok(match string {
"none" => ObjectKind::None,
"rel" => ObjectKind::Relocatable,
"exe" => ObjectKind::Executable,
"lib" => ObjectKind::Library,
"dump" => ObjectKind::Dump,
"dbg" => ObjectKind::Debug,
"other" => ObjectKind::Other,
_ => return Err(UnknownObjectKindError),
})
}
}
#[derive(Debug, Fail, Clone, Copy)]
#[fail(display = "unknown file format")]
pub struct UnknownFileFormatError;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub enum FileFormat {
Unknown,
Breakpad,
Elf,
MachO,
Pdb,
Pe,
}
impl FileFormat {
pub fn name(self) -> &'static str {
match self {
FileFormat::Unknown => "unknown",
FileFormat::Breakpad => "breakpad",
FileFormat::Elf => "elf",
FileFormat::MachO => "macho",
FileFormat::Pdb => "pdb",
FileFormat::Pe => "pe",
}
}
}
impl fmt::Display for FileFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl FromStr for FileFormat {
type Err = UnknownFileFormatError;
fn from_str(string: &str) -> Result<FileFormat, UnknownFileFormatError> {
Ok(match string {
"breakpad" => FileFormat::Breakpad,
"elf" => FileFormat::Elf,
"macho" => FileFormat::MachO,
"pdb" => FileFormat::Pdb,
"pe" => FileFormat::Pe,
_ => return Err(UnknownFileFormatError),
})
}
}
#[derive(Clone, Default, Eq, PartialEq)]
pub struct Symbol<'data> {
pub name: Option<Cow<'data, str>>,
pub address: u64,
pub size: u64,
}
impl<'data> Symbol<'data> {
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Cow::as_ref)
}
pub fn contains(&self, address: u64) -> bool {
address >= self.address && (self.size == 0 || address < self.address + self.size)
}
}
impl<'d> fmt::Debug for Symbol<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Symbol")
.field("name", &self.name().unwrap_or("<unknown>"))
.field("address", &format_args!("{:#x}", self.address))
.field("size", &format_args!("{:#x}", self.size))
.finish()
}
}
pub type SymbolMapIter<'data> = std::vec::IntoIter<Symbol<'data>>;
#[derive(Clone, Debug, Default)]
pub struct SymbolMap<'data> {
symbols: Vec<Symbol<'data>>,
}
impl<'data> SymbolMap<'data> {
pub fn new() -> Self {
SymbolMap {
symbols: Vec::new(),
}
}
pub fn lookup(&self, address: u64) -> Option<&Symbol<'data>> {
match self.symbols.binary_search_by_key(&address, Self::key) {
Ok(index) => Some(&self.symbols[index]),
Err(0) => None,
Err(next_index) => {
let symbol = &self.symbols[next_index - 1];
if symbol.contains(address) {
Some(symbol)
} else {
None
}
}
}
}
pub fn lookup_range<R>(&self, range: R) -> Option<&Symbol<'data>>
where
R: RangeBounds<u64>,
{
let start = match range.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start + 1,
Bound::Unbounded => 0,
};
let symbol = self.lookup(start)?;
let end = match range.end_bound() {
Bound::Included(end) => *end,
Bound::Excluded(end) => *end - 1,
Bound::Unbounded => u64::max_value(),
};
if end <= start || symbol.contains(end) {
Some(symbol)
} else {
None
}
}
#[inline(always)]
fn key(symbol: &Symbol<'data>) -> u64 {
symbol.address
}
}
impl<'d> Deref for SymbolMap<'d> {
type Target = [Symbol<'d>];
fn deref(&self) -> &Self::Target {
&self.symbols
}
}
impl<'data> IntoIterator for SymbolMap<'data> {
type Item = Symbol<'data>;
type IntoIter = SymbolMapIter<'data>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.into_iter()
}
}
impl<'data, 'a> IntoIterator for &'a SymbolMap<'data> {
type Item = &'a Symbol<'data>;
type IntoIter = std::slice::Iter<'a, Symbol<'data>>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.iter()
}
}
impl<'d> AsRef<[Symbol<'d>]> for SymbolMap<'d> {
fn as_ref(&self) -> &[Symbol<'d>] {
&self.symbols
}
}
impl<'d> From<Vec<Symbol<'d>>> for SymbolMap<'d> {
fn from(mut symbols: Vec<Symbol<'d>>) -> Self {
if !symbols.is_empty() {
dmsort::sort_by_key(&mut symbols, Self::key);
symbols.dedup_by(|next, symbol| {
if symbol.size == 0 {
symbol.size = next.address - symbol.address;
}
symbol.address == next.address
})
}
SymbolMap { symbols }
}
}
impl<'d> FromIterator<Symbol<'d>> for SymbolMap<'d> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Symbol<'d>>,
{
Vec::from_iter(iter).into()
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct FileInfo<'data> {
pub name: &'data [u8],
pub dir: &'data [u8],
}
impl<'data> FileInfo<'data> {
pub fn name_str(&self) -> Cow<'data, str> {
String::from_utf8_lossy(self.name)
}
pub fn dir_str(&self) -> Cow<'data, str> {
String::from_utf8_lossy(self.dir)
}
pub fn path_str(&self) -> String {
join_path(&self.dir_str(), &self.name_str())
}
}
impl fmt::Debug for FileInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FileInfo")
.field("name", &String::from_utf8_lossy(self.name))
.field("dir", &String::from_utf8_lossy(self.dir))
.finish()
}
}
#[derive(Clone)]
pub struct LineInfo<'data> {
pub address: u64,
pub file: FileInfo<'data>,
pub line: u64,
}
impl fmt::Debug for LineInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("LineInfo")
.field("address", &format_args!("{:#x}", self.address))
.field("file", &self.file)
.field("line", &self.line)
.finish()
}
}
#[derive(Clone)]
pub struct Function<'data> {
pub address: u64,
pub size: u64,
pub name: Name<'data>,
pub compilation_dir: &'data [u8],
pub lines: Vec<LineInfo<'data>>,
pub inlinees: Vec<Function<'data>>,
pub inline: bool,
}
impl Function<'_> {
pub fn end_address(&self) -> u64 {
self.address + self.size
}
}
impl fmt::Debug for Function<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Function")
.field("address", &format_args!("{:#x}", self.address))
.field("size", &format_args!("{:#x}", self.size))
.field("name", &self.name)
.field(
"compilation_dir",
&String::from_utf8_lossy(self.compilation_dir),
)
.field("lines", &self.lines)
.field("inlinees", &self.inlinees)
.field("inline", &self.inline)
.finish()
}
}
pub type DynIterator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
pub trait DebugSession {
type Error;
fn functions(&mut self) -> DynIterator<'_, Result<Function<'_>, Self::Error>>;
}
pub trait ObjectLike {
type Error;
type Session: DebugSession<Error = Self::Error>;
fn file_format(&self) -> FileFormat;
fn code_id(&self) -> Option<CodeId>;
fn debug_id(&self) -> DebugId;
fn debug_file_name(&self) -> Option<Cow<'_, str>> {
None
}
fn arch(&self) -> Arch;
fn kind(&self) -> ObjectKind;
fn load_address(&self) -> u64;
fn has_symbols(&self) -> bool;
fn symbols(&self) -> DynIterator<'_, Symbol<'_>>;
fn symbol_map(&self) -> SymbolMap<'_>;
fn has_debug_info(&self) -> bool;
fn debug_session(&self) -> Result<Self::Session, Self::Error>;
fn has_unwind_info(&self) -> bool;
}
#[cfg(feature = "serde")]
mod derive_serde {
macro_rules! impl_str_serde {
($type:ty) => {
impl ::serde::ser::Serialize for $type {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::ser::Serializer,
{
serializer.serialize_str(self.name())
}
}
impl<'de> ::serde::de::Deserialize<'de> for $type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
<::std::borrow::Cow<str>>::deserialize(deserializer)?
.parse()
.map_err(::serde::de::Error::custom)
}
}
};
}
impl_str_serde!(super::ObjectKind);
impl_str_serde!(super::FileFormat);
}