use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::{fmt, result};
use crate::common::*;
use crate::{ByteString, Bytes};
mod util;
pub use util::StringTable;
mod any;
pub use any::*;
#[cfg(feature = "archive")]
pub mod archive;
#[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)]
#[non_exhaustive]
pub enum FileKind {
#[cfg(feature = "archive")]
Archive,
#[cfg(feature = "coff")]
Coff,
#[cfg(feature = "elf")]
Elf32,
#[cfg(feature = "elf")]
Elf64,
#[cfg(feature = "macho")]
MachO32,
#[cfg(feature = "macho")]
MachO64,
#[cfg(feature = "macho")]
MachOFat32,
#[cfg(feature = "macho")]
MachOFat64,
#[cfg(feature = "pe")]
Pe32,
#[cfg(feature = "pe")]
Pe64,
#[cfg(feature = "wasm")]
Wasm,
}
impl FileKind {
pub fn parse(data: &[u8]) -> Result<FileKind> {
if data.len() < 16 {
return Err(Error("File too short"));
}
let kind = match [data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]] {
#[cfg(feature = "archive")]
[b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n'] => FileKind::Archive,
#[cfg(feature = "elf")]
[0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
#[cfg(feature = "elf")]
[0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
#[cfg(feature = "macho")]
[0xfe, 0xed, 0xfa, 0xce, ..]
| [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
#[cfg(feature = "macho")]
| [0xfe, 0xed, 0xfa, 0xcf, ..]
| [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
#[cfg(feature = "macho")]
[0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
#[cfg(feature = "macho")]
[0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64,
#[cfg(feature = "wasm")]
[0x00, b'a', b's', b'm', ..] => FileKind::Wasm,
#[cfg(feature = "pe")]
[b'M', b'Z', ..] => {
match pe::PeFile64::optional_header_magic(data) {
Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
FileKind::Pe32
}
Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
FileKind::Pe64
}
_ => return Err(Error("Unknown MS-DOS file")),
}
}
#[cfg(feature = "coff")]
[0x4c, 0x01, ..]
| [0x64, 0x86, ..] => FileKind::Coff,
_ => return Err(Error("Unknown file magic")),
};
Ok(kind)
}
}
#[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
}
}
}
pub trait SymbolMapEntry {
fn address(&self) -> u64;
}
#[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_unstable_by_key(|s| s.address());
SymbolMap { symbols }
}
pub fn get(&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)
}
#[inline]
pub fn symbols(&self) -> &[T] {
&self.symbols
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolMapName<'data> {
address: u64,
name: &'data str,
}
impl<'data> SymbolMapName<'data> {
pub fn new(address: u64, name: &'data str) -> Self {
SymbolMapName { address, name }
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
#[inline]
pub fn name(&self) -> &'data str {
self.name
}
}
impl<'data> SymbolMapEntry for SymbolMapName<'data> {
#[inline]
fn address(&self) -> u64 {
self.address
}
}
#[derive(Debug, Default, Clone)]
pub struct ObjectMap<'data> {
symbols: SymbolMap<ObjectMapEntry<'data>>,
objects: Vec<&'data [u8]>,
}
impl<'data> ObjectMap<'data> {
pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
self.symbols
.get(address)
.filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
}
#[inline]
pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
self.symbols.symbols()
}
#[inline]
pub fn objects(&self) -> &[&'data [u8]] {
&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> {
#[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(&self, map: &ObjectMap<'data>) -> &'data [u8] {
map.objects[self.object]
}
}
impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
#[inline]
fn address(&self) -> u64 {
self.address
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Import<'data> {
name: ByteString<'data>,
library: ByteString<'data>,
}
impl<'data> Import<'data> {
#[inline]
pub fn name(&self) -> &'data [u8] {
self.name.0
}
#[inline]
pub fn library(&self) -> &'data [u8] {
self.library.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Export<'data> {
name: ByteString<'data>,
address: u64,
}
impl<'data> Export<'data> {
#[inline]
pub fn name(&self) -> &'data [u8] {
self.name.0
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
Symbol(SymbolIndex),
Section(SectionIndex),
Absolute,
}
#[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.")),
}
}
}