use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::{cmp, fmt, result};
use crate::common::*;
use crate::Bytes;
mod util;
pub use util::StringTable;
mod any;
pub use any::*;
#[cfg(feature = "coff")]
pub mod coff;
#[cfg(feature = "elf")]
pub mod elf;
#[cfg(feature = "macho")]
pub mod macho;
#[cfg(feature = "pe")]
pub mod pe;
mod traits;
pub use traits::*;
#[cfg(feature = "wasm")]
pub mod wasm;
mod private {
pub trait Sealed {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Error(&'static str);
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
pub type Result<T> = result::Result<T, Error>;
trait ReadError<T> {
fn read_error(self, error: &'static str) -> Result<T>;
}
impl<T> ReadError<T> for result::Result<T, ()> {
fn read_error(self, error: &'static str) -> Result<T> {
self.map_err(|()| Error(error))
}
}
impl<T> ReadError<T> for Option<T> {
fn read_error(self, error: &'static str) -> Result<T> {
self.ok_or(Error(error))
}
}
#[cfg(all(
unix,
not(target_os = "macos"),
target_pointer_width = "32",
feature = "elf"
))]
pub type NativeFile<'data> = elf::ElfFile32<'data>;
#[cfg(all(
unix,
not(target_os = "macos"),
target_pointer_width = "64",
feature = "elf"
))]
pub type NativeFile<'data> = elf::ElfFile64<'data>;
#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile32<'data>;
#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile64<'data>;
#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile32<'data>;
#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile64<'data>;
#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
pub type NativeFile<'data> = wasm::WasmFile<'data>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SectionIndex(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolIndex(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolSection {
Unknown,
None,
Undefined,
Absolute,
Common,
Section(SectionIndex),
}
impl SymbolSection {
#[inline]
pub fn index(self) -> Option<SectionIndex> {
if let SymbolSection::Section(index) = self {
Some(index)
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct Symbol<'data> {
name: Option<&'data str>,
address: u64,
size: u64,
kind: SymbolKind,
section: SymbolSection,
weak: bool,
scope: SymbolScope,
flags: SymbolFlags<SectionIndex>,
}
impl<'data> Symbol<'data> {
#[inline]
pub fn kind(&self) -> SymbolKind {
self.kind
}
#[inline]
pub fn section(&self) -> SymbolSection {
self.section
}
#[inline]
pub fn section_index(&self) -> Option<SectionIndex> {
self.section.index()
}
#[inline]
pub fn is_undefined(&self) -> bool {
self.section == SymbolSection::Undefined
}
#[inline]
fn is_common(&self) -> bool {
self.section == SymbolSection::Common
}
#[inline]
pub fn is_weak(&self) -> bool {
self.weak
}
#[inline]
pub fn is_global(&self) -> bool {
!self.is_local()
}
#[inline]
pub fn is_local(&self) -> bool {
self.scope == SymbolScope::Compilation
}
#[inline]
pub fn scope(&self) -> SymbolScope {
self.scope
}
#[inline]
pub fn flags(&self) -> SymbolFlags<SectionIndex> {
self.flags
}
#[inline]
pub fn name(&self) -> Option<&'data str> {
self.name
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
#[inline]
pub fn size(&self) -> u64 {
self.size
}
}
#[derive(Debug)]
pub struct SymbolMap<'data> {
symbols: Vec<Symbol<'data>>,
}
impl<'data> SymbolMap<'data> {
pub fn get(&self, address: u64) -> Option<&Symbol<'data>> {
self.symbols
.binary_search_by(|symbol| {
if address < symbol.address {
cmp::Ordering::Greater
} else if address < symbol.address + symbol.size {
cmp::Ordering::Equal
} else {
cmp::Ordering::Less
}
})
.ok()
.and_then(|index| self.symbols.get(index))
}
#[inline]
pub fn symbols(&self) -> &[Symbol<'data>] {
&self.symbols
}
fn filter(symbol: &Symbol<'_>) -> bool {
match symbol.kind() {
SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {}
SymbolKind::Null
| SymbolKind::Section
| SymbolKind::File
| SymbolKind::Label
| SymbolKind::Tls => {
return false;
}
}
!symbol.is_undefined() && !symbol.is_common() && symbol.size() > 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
Symbol(SymbolIndex),
Section(SectionIndex),
}
#[derive(Debug)]
pub struct Relocation {
kind: RelocationKind,
encoding: RelocationEncoding,
size: u8,
target: RelocationTarget,
addend: i64,
implicit_addend: bool,
}
impl Relocation {
#[inline]
pub fn kind(&self) -> RelocationKind {
self.kind
}
#[inline]
pub fn encoding(&self) -> RelocationEncoding {
self.encoding
}
#[inline]
pub fn size(&self) -> u8 {
self.size
}
#[inline]
pub fn target(&self) -> RelocationTarget {
self.target
}
#[inline]
pub fn addend(&self) -> i64 {
self.addend
}
#[inline]
pub fn set_addend(&mut self, addend: i64) {
self.addend = addend
}
#[inline]
pub fn has_implicit_addend(&self) -> bool {
self.implicit_addend
}
}
fn data_range(data: Bytes, data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> {
let offset = range_address.checked_sub(data_address)?;
let data = data.read_bytes_at(offset as usize, size as usize).ok()?;
Some(data.0)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CompressedData<'data> {
pub format: CompressionFormat,
pub data: &'data [u8],
pub uncompressed_size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompressionFormat {
None,
Unknown,
Zlib,
}
impl<'data> CompressedData<'data> {
#[inline]
pub fn none(data: &'data [u8]) -> Self {
CompressedData {
format: CompressionFormat::None,
data,
uncompressed_size: data.len(),
}
}
pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
match self.format {
CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
#[cfg(feature = "compression")]
CompressionFormat::Zlib => {
let mut decompressed = Vec::with_capacity(self.uncompressed_size);
let mut decompress = flate2::Decompress::new(true);
decompress
.decompress_vec(
self.data,
&mut decompressed,
flate2::FlushDecompress::Finish,
)
.ok()
.read_error("Invalid zlib compressed data")?;
Ok(Cow::Owned(decompressed))
}
_ => Err(Error("Unsupported compressed data.")),
}
}
}