use alloc::vec::Vec;
use crate::read::{Object, ObjectSection, ObjectSymbol, ObjectSymbolTable};
use crate::{SymbolKind, SymbolScope};
pub trait SymbolMapEntry {
fn address(&self) -> u64;
fn size(&self) -> u64 {
0
}
}
#[derive(Debug, Default, Clone)]
pub struct SymbolMap<T: SymbolMapEntry> {
symbols: Vec<T>,
}
impl<T: SymbolMapEntry> SymbolMap<T> {
pub fn new(mut symbols: Vec<T>) -> Self {
symbols.sort_by_key(|s| s.address());
SymbolMap { symbols }
}
#[deprecated = "use before or containing"]
pub fn get(&self, address: u64) -> Option<&T> {
self.before(address)
}
pub fn before(&self, address: u64) -> Option<&T> {
let index = match self
.symbols
.binary_search_by_key(&address, |symbol| symbol.address())
{
Ok(index) => index,
Err(index) => index.checked_sub(1)?,
};
self.symbols.get(index)
}
pub fn containing(&self, address: u64) -> Option<&T> {
self.before(address).filter(|entry| {
entry.size() == 0 || address.wrapping_sub(entry.address()) < entry.size()
})
}
#[inline]
pub fn symbols(&self) -> &[T] {
&self.symbols
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolMapName<'data> {
address: u64,
size: u64,
name: &'data str,
}
impl<'data> SymbolMapName<'data> {
pub fn new(address: u64, size: u64, name: &'data str) -> Self {
SymbolMapName {
address,
size,
name,
}
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
#[inline]
pub fn size(&self) -> u64 {
self.size
}
#[inline]
pub fn name(&self) -> &'data str {
self.name
}
}
impl<'data> SymbolMapEntry for SymbolMapName<'data> {
#[inline]
fn address(&self) -> u64 {
self.address
}
#[inline]
fn size(&self) -> u64 {
self.size
}
}
#[derive(Debug, Default, Clone)]
pub struct ObjectMap<'data> {
symbols: SymbolMap<ObjectMapEntry<'data>>,
objects: Vec<ObjectMapFile<'data>>,
}
impl<'data> ObjectMap<'data> {
#[cfg(feature = "macho")]
pub(super) fn new(
symbols: Vec<ObjectMapEntry<'data>>,
objects: Vec<ObjectMapFile<'data>>,
) -> Self {
ObjectMap {
symbols: SymbolMap::new(symbols),
objects,
}
}
pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
self.symbols.containing(address)
}
#[inline]
pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
self.symbols.symbols()
}
#[inline]
pub fn objects(&self) -> &[ObjectMapFile<'data>] {
&self.objects
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ObjectMapEntry<'data> {
address: u64,
size: u64,
name: &'data [u8],
object: usize,
}
impl<'data> ObjectMapEntry<'data> {
#[cfg(feature = "macho")]
pub(super) fn new(address: u64, size: u64, name: &'data [u8], object: usize) -> Self {
ObjectMapEntry {
address,
size,
name,
object,
}
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
#[inline]
pub fn size(&self) -> u64 {
self.size
}
#[inline]
pub fn name(&self) -> &'data [u8] {
self.name
}
#[inline]
pub fn object_index(&self) -> usize {
self.object
}
#[inline]
pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
&map.objects[self.object]
}
}
impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
#[inline]
fn address(&self) -> u64 {
self.address
}
#[inline]
fn size(&self) -> u64 {
self.size
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ObjectMapFile<'data> {
path: &'data [u8],
member: Option<&'data [u8]>,
}
impl<'data> ObjectMapFile<'data> {
#[cfg(feature = "macho")]
pub(super) fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
ObjectMapFile { path, member }
}
#[inline]
pub fn path(&self) -> &'data [u8] {
self.path
}
#[inline]
pub fn member(&self) -> Option<&'data [u8]> {
self.member
}
}
#[derive(Debug, Default)]
pub struct SymbolMapBuilder(());
impl SymbolMapBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build<'data, O>(self, object: &O) -> SymbolMap<SymbolMapName<'data>>
where
O: Object<'data> + ?Sized,
{
let mut symbols = Vec::new();
if let Some(table) = object
.symbol_table()
.or_else(|| object.dynamic_symbol_table())
{
let mut all_symbols = Vec::new();
for symbol in table.symbols() {
if !symbol.is_definition() {
continue;
}
let name = match symbol.name() {
Ok(name) => name,
_ => continue,
};
if name.is_empty() {
continue;
}
let address = symbol.address();
let size = symbol.size();
let mut priority = 0u32;
match symbol.kind() {
SymbolKind::Text | SymbolKind::Data => {}
SymbolKind::Unknown => priority += 1,
_ => continue,
}
priority *= 2;
#[cfg(feature = "xcoff")]
if let crate::SymbolFlags::Xcoff { x_smtyp, .. } = symbol.flags() {
priority += (x_smtyp != crate::xcoff::XTY_LD) as u32;
if size != 0 {
all_symbols.push((address.saturating_add(size), !0, !0, !0, ""));
}
}
priority *= 2;
priority += (size == 0) as u32;
priority *= 4;
priority += match symbol.scope() {
SymbolScope::Unknown => 3,
SymbolScope::Compilation => 2,
SymbolScope::Linkage => 1,
SymbolScope::Dynamic => 0,
};
let index = !0 - symbol.index().0;
all_symbols.push((address, priority, index, size, name));
}
for section in object.sections() {
let address = section.address().saturating_add(section.size());
all_symbols.push((address, !0, !0, !0, ""));
}
all_symbols.sort_unstable();
let mut previous_address = !0;
for (address, _priority, _index, size, name) in all_symbols {
if address != previous_address {
symbols.push(SymbolMapName::new(address, size, name));
previous_address = address;
}
}
let mut symbol_iter = symbols.iter_mut().rev();
let mut previous_address = symbol_iter.next().map(|s| s.address).unwrap_or(0);
for symbol in symbol_iter {
if symbol.size == 0 {
symbol.size = previous_address.saturating_sub(symbol.address);
}
previous_address = symbol.address;
}
symbols.retain(|x| !x.name.is_empty());
}
SymbolMap::new(symbols)
}
}