use crate::container::{self, Container};
use crate::error;
use crate::mach::load_command;
use core::fmt::{self, Debug};
use scroll::ctx;
use scroll::ctx::SizeWith;
use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith};
pub const N_STAB: u8 = 0xe0;
pub const N_PEXT: u8 = 0x10;
pub const N_TYPE: u8 = 0x0e;
pub const N_EXT: u8 = 0x01;
pub const NO_SECT: u8 = 0;
pub const MAX_SECT: u8 = 255;
pub const N_UNDF: u8 = 0x0;
pub const N_ABS: u8 = 0x2;
pub const N_SECT: u8 = 0xe;
pub const N_PBUD: u8 = 0xc;
pub const N_INDR: u8 = 0xa;
pub const N_GSYM: u8 = 0x20;
pub const N_FNAME: u8 = 0x22;
pub const N_FUN: u8 = 0x24;
pub const N_STSYM: u8 = 0x26;
pub const N_LCSYM: u8 = 0x28;
pub const N_BNSYM: u8 = 0x2e;
pub const N_PC: u8 = 0x30;
pub const N_AST: u8 = 0x32;
pub const N_OPT: u8 = 0x3c;
pub const N_RSYM: u8 = 0x40;
pub const N_SLINE: u8 = 0x44;
pub const N_ENSYM: u8 = 0x4e;
pub const N_SSYM: u8 = 0x60;
pub const N_SO: u8 = 0x64;
pub const N_OSO: u8 = 0x66;
pub const N_LSYM: u8 = 0x80;
pub const N_BINCL: u8 = 0x82;
pub const N_SOL: u8 = 0x84;
pub const N_PARAMS: u8 = 0x86;
pub const N_VERSION: u8 = 0x88;
pub const N_OLEVEL: u8 = 0x8a;
pub const N_PSYM: u8 = 0xa0;
pub const N_EINCL: u8 = 0xa2;
pub const N_ENTRY: u8 = 0xa4;
pub const N_LBRAC: u8 = 0xc0;
pub const N_EXCL: u8 = 0xc2;
pub const N_RBRAC: u8 = 0xe0;
pub const N_BCOMM: u8 = 0xe2;
pub const N_ECOMM: u8 = 0xe4;
pub const N_ECOML: u8 = 0xe8;
pub const N_LENG: u8 = 0xfe;
pub const NLIST_TYPE_MASK: u8 = 0xe;
pub const NLIST_TYPE_GLOBAL: u8 = 0x1;
pub const NLIST_TYPE_LOCAL: u8 = 0x0;
pub const REFERENCE_TYPE: u16 = 0xf;
pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 0x3;
pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 0x4;
pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 0x5;
pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
pub const N_DESC_DISCARDED: u16 = 0x20;
pub const N_NO_DEAD_STRIP: u16 = 0x20;
pub const N_WEAK_REF: u16 = 0x40;
pub const N_WEAK_DEF: u16 = 0x80;
pub fn n_type_to_str(n_type: u8) -> &'static str {
match n_type {
N_UNDF => "N_UNDF",
N_ABS => "N_ABS",
N_SECT => "N_SECT",
N_PBUD => "N_PBUD",
N_INDR => "N_INDR",
_ => "UNKNOWN_N_TYPE",
}
}
#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)]
pub struct Nlist32 {
pub n_strx: u32,
pub n_type: u8,
pub n_sect: u8,
pub n_desc: u16,
pub n_value: u32,
}
pub const SIZEOF_NLIST_32: usize = 12;
impl Debug for Nlist32 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Nlist32")
.field("n_strx", &format_args!("{:04}", self.n_strx))
.field("n_type", &format_args!("{:#02x}", self.n_type))
.field("n_sect", &format_args!("{:#x}", self.n_sect))
.field("n_desc", &format_args!("{:#03x}", self.n_desc))
.field("n_value", &format_args!("{:#x}", self.n_value))
.finish()
}
}
#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)]
pub struct Nlist64 {
pub n_strx: u32,
pub n_type: u8,
pub n_sect: u8,
pub n_desc: u16,
pub n_value: u64,
}
pub const SIZEOF_NLIST_64: usize = 16;
impl Debug for Nlist64 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Nlist64")
.field("n_strx", &format_args!("{:04}", self.n_strx))
.field("n_type", &format_args!("{:#02x}", self.n_type))
.field("n_sect", &format_args!("{:#x}", self.n_sect))
.field("n_desc", &format_args!("{:#03x}", self.n_desc))
.field("n_value", &format_args!("{:#x}", self.n_value))
.finish()
}
}
#[derive(Debug, Clone)]
pub struct Nlist {
pub n_strx: usize,
pub n_type: u8,
pub n_sect: usize,
pub n_desc: u16,
pub n_value: u64,
}
impl Nlist {
pub fn get_type(&self) -> u8 {
self.n_type & N_TYPE
}
pub fn type_str(&self) -> &'static str {
n_type_to_str(self.get_type())
}
pub fn is_global(&self) -> bool {
self.n_type & N_EXT != 0
}
pub fn is_weak(&self) -> bool {
self.n_desc & (N_WEAK_REF | N_WEAK_DEF) != 0
}
pub fn is_undefined(&self) -> bool {
self.n_sect == 0 && self.n_type & N_TYPE == N_UNDF
}
pub fn is_stab(&self) -> bool {
self.n_type & N_STAB != 0
}
}
impl ctx::SizeWith<container::Ctx> for Nlist {
fn size_with(ctx: &container::Ctx) -> usize {
match ctx.container {
Container::Little => SIZEOF_NLIST_32,
Container::Big => SIZEOF_NLIST_64,
}
}
}
impl From<Nlist32> for Nlist {
fn from(nlist: Nlist32) -> Self {
Nlist {
n_strx: nlist.n_strx as usize,
n_type: nlist.n_type,
n_sect: nlist.n_sect as usize,
n_desc: nlist.n_desc,
n_value: u64::from(nlist.n_value),
}
}
}
impl From<Nlist64> for Nlist {
fn from(nlist: Nlist64) -> Self {
Nlist {
n_strx: nlist.n_strx as usize,
n_type: nlist.n_type,
n_sect: nlist.n_sect as usize,
n_desc: nlist.n_desc,
n_value: nlist.n_value,
}
}
}
impl From<Nlist> for Nlist32 {
fn from(nlist: Nlist) -> Self {
Nlist32 {
n_strx: nlist.n_strx as u32,
n_type: nlist.n_type,
n_sect: nlist.n_sect as u8,
n_desc: nlist.n_desc,
n_value: nlist.n_value as u32,
}
}
}
impl From<Nlist> for Nlist64 {
fn from(nlist: Nlist) -> Self {
Nlist64 {
n_strx: nlist.n_strx as u32,
n_type: nlist.n_type,
n_sect: nlist.n_sect as u8,
n_desc: nlist.n_desc,
n_value: nlist.n_value,
}
}
}
impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Nlist {
type Error = crate::error::Error;
fn try_from_ctx(
bytes: &'a [u8],
container::Ctx { container, le }: container::Ctx,
) -> crate::error::Result<(Self, usize)> {
let nlist = match container {
Container::Little => (bytes.pread_with::<Nlist32>(0, le)?.into(), SIZEOF_NLIST_32),
Container::Big => (bytes.pread_with::<Nlist64>(0, le)?.into(), SIZEOF_NLIST_64),
};
Ok(nlist)
}
}
impl ctx::TryIntoCtx<container::Ctx> for Nlist {
type Error = crate::error::Error;
fn try_into_ctx(
self,
bytes: &mut [u8],
container::Ctx { container, le }: container::Ctx,
) -> Result<usize, Self::Error> {
let size = match container {
Container::Little => (bytes.pwrite_with::<Nlist32>(self.into(), 0, le)?),
Container::Big => (bytes.pwrite_with::<Nlist64>(self.into(), 0, le)?),
};
Ok(size)
}
}
impl ctx::IntoCtx<container::Ctx> for Nlist {
fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
bytes.pwrite_with(self, 0, ctx).unwrap();
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SymbolsCtx {
pub nsyms: usize,
pub strtab: usize,
pub ctx: container::Ctx,
}
impl<'a, T: ?Sized> ctx::TryFromCtx<'a, SymbolsCtx, T> for Symbols<'a>
where
T: AsRef<[u8]>,
{
type Error = crate::error::Error;
fn try_from_ctx(
bytes: &'a T,
SymbolsCtx { nsyms, strtab, ctx }: SymbolsCtx,
) -> crate::error::Result<(Self, usize)> {
let data = bytes.as_ref();
Ok((
Symbols {
data,
start: 0,
nsyms,
strtab,
ctx,
},
data.len(),
))
}
}
#[derive(Default)]
pub struct SymbolIterator<'a> {
data: &'a [u8],
nsyms: usize,
offset: usize,
count: usize,
ctx: container::Ctx,
strtab: usize,
}
impl<'a> Iterator for SymbolIterator<'a> {
type Item = error::Result<(&'a str, Nlist)>;
fn next(&mut self) -> Option<Self::Item> {
if self.count >= self.nsyms {
None
} else {
self.count += 1;
match self.data.gread_with::<Nlist>(&mut self.offset, self.ctx) {
Ok(symbol) => match self.data.pread(self.strtab + symbol.n_strx) {
Ok(name) => Some(Ok((name, symbol))),
Err(e) => Some(Err(e.into())),
},
Err(e) => Some(Err(e)),
}
}
}
}
pub struct Symbols<'a> {
data: &'a [u8],
start: usize,
nsyms: usize,
strtab: usize,
ctx: container::Ctx,
}
impl<'a, 'b> IntoIterator for &'b Symbols<'a> {
type Item = <SymbolIterator<'a> as Iterator>::Item;
type IntoIter = SymbolIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> Symbols<'a> {
pub fn new(
bytes: &'a [u8],
start: usize,
count: usize,
strtab: usize,
) -> error::Result<Symbols<'a>> {
let nsyms = count;
Ok(Symbols {
data: bytes,
start,
nsyms,
strtab,
ctx: container::Ctx::default(),
})
}
pub fn parse(
bytes: &'a [u8],
symtab: &load_command::SymtabCommand,
ctx: container::Ctx,
) -> error::Result<Symbols<'a>> {
let strtab = symtab.stroff - symtab.symoff;
Ok(bytes.pread_with(
symtab.symoff as usize,
SymbolsCtx {
nsyms: symtab.nsyms as usize,
strtab: strtab as usize,
ctx,
},
)?)
}
pub fn iter(&self) -> SymbolIterator<'a> {
SymbolIterator {
offset: self.start as usize,
nsyms: self.nsyms as usize,
count: 0,
data: self.data,
ctx: self.ctx,
strtab: self.strtab,
}
}
pub fn get(&self, index: usize) -> crate::error::Result<(&'a str, Nlist)> {
let sym: Nlist = self
.data
.pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)?;
let name = self.data.pread(self.strtab + sym.n_strx)?;
Ok((name, sym))
}
}
impl<'a> Debug for Symbols<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Symbols")
.field("data", &self.data.len())
.field("start", &format_args!("{:#?}", self.start))
.field("nsyms", &self.nsyms)
.field("strtab", &format_args!("{:#x}", self.strtab))
.finish()?;
writeln!(fmt, "Symbol List {{")?;
for (i, res) in self.iter().enumerate() {
match res {
Ok((name, nlist)) => writeln!(
fmt,
"{: >10x} {} sect: {:#x} type: {:#02x} desc: {:#03x}",
nlist.n_value, name, nlist.n_sect, nlist.n_type, nlist.n_desc
)?,
Err(error) => writeln!(fmt, " Bad symbol, index: {}, sym: {:?}", i, error)?,
}
}
writeln!(fmt, "}}")
}
}