use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet};
use std::iter::{IntoIterator, Peekable};
use std::slice;
use goblin::mach;
use regex::Regex;
use symbolic_common::{ErrorKind, Name, Result};
use object::{Object, ObjectTarget};
lazy_static! {
static ref HIDDEN_SYMBOL_RE: Regex = Regex::new("__?hidden#\\d+_").unwrap();
}
#[derive(Debug)]
pub struct Symbol<'data> {
name: Cow<'data, str>,
addr: u64,
len: Option<u64>,
}
impl<'data> Symbol<'data> {
pub fn name(&self) -> &Cow<'data, str> {
&self.name
}
pub fn addr(&self) -> u64 {
self.addr
}
pub fn len(&self) -> Option<u64> {
self.len
}
pub fn as_str(&self) -> &str {
self.name().as_ref()
}
}
impl<'data> Into<Name<'data>> for Symbol<'data> {
fn into(self) -> Name<'data> {
Name::new(self.name)
}
}
impl<'data> Into<Cow<'data, str>> for Symbol<'data> {
fn into(self) -> Cow<'data, str> {
self.name
}
}
impl<'data> Into<String> for Symbol<'data> {
fn into(self) -> String {
self.name.into()
}
}
#[derive(Clone, Copy, Debug)]
enum SymbolsInternal<'data> {
MachO(&'data mach::symbols::Symbols<'data>),
}
impl<'data> SymbolsInternal<'data> {
pub fn get(&self, index: usize, next: Option<usize>) -> Result<Option<Symbol<'data>>> {
Ok(Some(match *self {
SymbolsInternal::MachO(symbols) => {
let (name, nlist) = symbols.get(index)?;
let stripped = if name.starts_with("_") {
&name[1..]
} else {
name
};
let addr = nlist.n_value;
let len = next.and_then(|index| symbols.get(index).ok())
.map(|(_, nlist)| nlist.n_value - addr);
Symbol {
name: Cow::Borrowed(stripped),
addr: addr,
len: len,
}
}
}))
}
}
type IndexMapping = (u64, usize);
pub struct SymbolIterator<'data, 'sym>
where
'data: 'sym,
{
symbols: &'sym Symbols<'data>,
iter: Peekable<slice::Iter<'sym, IndexMapping>>,
}
impl<'data, 'sym> Iterator for SymbolIterator<'data, 'sym> {
type Item = Result<Symbol<'data>>;
fn next(&mut self) -> Option<Self::Item> {
let index = match self.iter.next() {
Some(map) => map.1,
None => return None,
};
let next = self.iter.peek().map(|mapping| mapping.1);
match self.symbols.internal.get(index, next) {
Ok(Some(symbol)) => Some(Ok(symbol)),
Ok(None) => None,
Err(err) => Some(Err(err)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
fn count(self) -> usize {
self.iter.count()
}
}
pub struct Symbols<'data> {
internal: SymbolsInternal<'data>,
mappings: Vec<IndexMapping>,
}
impl<'data> Symbols<'data> {
fn from_macho(macho: &'data mach::MachO) -> Result<Symbols<'data>> {
let macho_symbols = match macho.symbols {
Some(ref symbols) => symbols,
None => return Err(ErrorKind::MissingDebugInfo("symbol table missing").into()),
};
let mut sections = HashSet::new();
let mut section_index = 0;
for segment in &macho.segments {
for section_rv in segment {
let (section, _) = section_rv?;
let name = section.name()?;
if name == "__stubs" || name == "__text" {
sections.insert(section_index);
}
section_index += 1;
}
}
let mut symbol_map = BTreeMap::new();
for (symbol_index, symbol_result) in macho.symbols().enumerate() {
let (_, nlist) = symbol_result?;
let in_valid_section = nlist.get_type() == mach::symbols::N_SECT
&& nlist.n_sect != (mach::symbols::NO_SECT as usize)
&& sections.contains(&(nlist.n_sect - 1));
if in_valid_section {
symbol_map.insert(nlist.n_value, symbol_index);
}
}
Ok(Symbols {
internal: SymbolsInternal::MachO(macho_symbols),
mappings: symbol_map.into_iter().collect(),
})
}
pub fn lookup(&self, addr: u64) -> Result<Option<Symbol<'data>>> {
let found = match self.mappings.binary_search_by_key(&addr, |&x| x.0) {
Ok(idx) => idx,
Err(0) => return Ok(None),
Err(next_idx) => next_idx - 1,
};
let index = self.mappings[found].1;
let next = self.mappings.get(found + 1).map(|mapping| mapping.1);
self.internal.get(index, next)
}
pub fn requires_symbolmap(&self) -> Result<bool> {
match self.internal {
SymbolsInternal::MachO(..) => (),
};
for symbol in self.iter() {
if HIDDEN_SYMBOL_RE.is_match(symbol?.as_str()) {
return Ok(true);
}
}
Ok(false)
}
pub fn iter<'sym>(&'sym self) -> SymbolIterator<'data, 'sym> {
SymbolIterator {
symbols: self,
iter: self.mappings.iter().peekable(),
}
}
}
pub trait SymbolTable {
fn symbols(&self) -> Result<Symbols>;
}
impl<'data> SymbolTable for Object<'data> {
fn symbols(&self) -> Result<Symbols> {
match self.target {
ObjectTarget::MachOSingle(macho) => Symbols::from_macho(macho),
ObjectTarget::MachOFat(_, ref macho) => Symbols::from_macho(macho),
_ => Err(ErrorKind::Internal("symbol table not implemented").into()),
}
}
}