mod elf;
mod macho;
mod pe;
use crate::error::{Error, Result};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Format {
Elf,
Pe,
MachO,
}
impl Format {
pub fn as_str(&self) -> &'static str {
match self {
Self::Elf => "Elf",
Self::Pe => "Pe",
Self::MachO => "MachO",
}
}
pub fn is_elf(&self) -> bool {
matches!(self, Self::Elf)
}
pub fn is_pe(&self) -> bool {
matches!(self, Self::Pe)
}
pub fn is_macho(&self) -> bool {
matches!(self, Self::MachO)
}
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Arch {
I386,
Amd64,
Arm,
Aarch64,
Riscv32,
Riscv64,
PowerPc,
PowerPc64,
Other,
}
impl Arch {
pub fn as_str(&self) -> &'static str {
match self {
Self::I386 => "I386",
Self::Amd64 => "Amd64",
Self::Arm => "Arm",
Self::Aarch64 => "Aarch64",
Self::Riscv32 => "Riscv32",
Self::Riscv64 => "Riscv64",
Self::PowerPc => "PowerPc",
Self::PowerPc64 => "PowerPc64",
Self::Other => "Other",
}
}
pub fn bits(&self) -> Option<u8> {
match self {
Self::Amd64 | Self::Aarch64 | Self::Riscv64 | Self::PowerPc64 => Some(64),
Self::I386 | Self::Arm | Self::Riscv32 | Self::PowerPc => Some(32),
Self::Other => None,
}
}
pub fn is_64bit(&self) -> bool {
self.bits() == Some(64)
}
}
impl fmt::Display for Arch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SectionKind {
Text,
RoData,
Data,
Bss,
Other,
}
#[derive(Debug, Clone)]
pub struct Section<'a> {
pub name: String,
pub vm_addr: u64,
pub vm_size: u64,
pub data: &'a [u8],
pub kind: SectionKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolKind {
Function,
Object,
File,
Section,
Other,
}
#[derive(Debug, Clone)]
pub struct Symbol<'a> {
pub name: std::borrow::Cow<'a, str>,
pub vm_addr: u64,
pub size: u64,
pub kind: SymbolKind,
}
pub struct Container<'a> {
bytes: &'a [u8],
format: Format,
arch: Arch,
image_base: u64,
sections: Vec<Section<'a>>,
symbols: Vec<Symbol<'a>>,
}
impl<'a> Container<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self> {
let obj = goblin::Object::parse(bytes)?;
match obj {
goblin::Object::Elf(elf) => elf::build(bytes, elf),
goblin::Object::PE(pe) => pe::build(bytes, pe),
goblin::Object::Mach(mach) => macho::build(bytes, mach),
_ => Err(Error::UnsupportedFormat),
}
}
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
pub fn format(&self) -> Format {
self.format
}
pub fn arch(&self) -> Arch {
self.arch
}
pub fn image_base(&self) -> u64 {
self.image_base
}
pub fn va_to_rva(&self, va: u64) -> Option<u64> {
va.checked_sub(self.image_base)
}
pub fn sections(&self) -> &[Section<'a>] {
&self.sections
}
pub fn symbols(&self) -> &[Symbol<'a>] {
&self.symbols
}
pub fn rodata_sections(&self) -> impl Iterator<Item = &Section<'a>> + '_ {
self.sections
.iter()
.filter(|s| s.kind == SectionKind::RoData)
}
pub fn function_at_va(&self, va: u64) -> Option<&Symbol<'a>> {
if let Some(sym) = self.symbols.iter().find(|s| {
s.kind == SymbolKind::Function
&& s.size > 0
&& va >= s.vm_addr
&& s.vm_addr.checked_add(s.size).is_some_and(|end| va < end)
}) {
return Some(sym);
}
self.symbols
.iter()
.filter(|s| s.kind == SymbolKind::Function && s.vm_addr <= va && s.vm_addr > 0)
.max_by_key(|s| s.vm_addr)
}
}
impl std::fmt::Debug for Container<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Container")
.field("format", &self.format)
.field("arch", &self.arch)
.field("sections", &self.sections.len())
.field("symbols", &self.symbols.len())
.finish()
}
}
pub(crate) fn assemble<'a>(
bytes: &'a [u8],
format: Format,
arch: Arch,
image_base: u64,
sections: Vec<Section<'a>>,
symbols: Vec<Symbol<'a>>,
) -> Container<'a> {
Container {
bytes,
format,
arch,
image_base,
sections,
symbols,
}
}