use core::marker::PhantomData;
use crate::endian::Endian;
use crate::macho;
use crate::pod::Bytes;
use crate::read::macho::{MachHeader, SymbolTable};
use crate::read::{ReadError, Result, StringTable};
#[derive(Debug, Default, Clone, Copy)]
pub struct MachOLoadCommandIterator<'data, E: Endian> {
endian: E,
data: Bytes<'data>,
ncmds: u32,
}
impl<'data, E: Endian> MachOLoadCommandIterator<'data, E> {
pub(super) fn new(endian: E, data: Bytes<'data>, ncmds: u32) -> Self {
MachOLoadCommandIterator {
endian,
data,
ncmds,
}
}
pub fn next(&mut self) -> Result<Option<MachOLoadCommand<'data, E>>> {
if self.ncmds == 0 {
return Ok(None);
}
let header = self
.data
.read_at::<macho::LoadCommand<E>>(0)
.read_error("Invalid Mach-O load command header")?;
let cmd = header.cmd.get(self.endian);
let cmdsize = header.cmdsize.get(self.endian) as usize;
let data = self
.data
.read_bytes(cmdsize)
.read_error("Invalid Mach-O load command size")?;
self.ncmds -= 1;
Ok(Some(MachOLoadCommand {
cmd,
data,
marker: Default::default(),
}))
}
}
#[derive(Debug, Clone, Copy)]
pub struct MachOLoadCommand<'data, E: Endian> {
cmd: u32,
data: Bytes<'data>,
marker: PhantomData<E>,
}
impl<'data, E: Endian> MachOLoadCommand<'data, E> {
pub fn segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, Bytes<'data>)>> {
if self.cmd == macho::LC_SEGMENT {
let mut data = self.data;
let command = data
.read()
.read_error("Invalid Mach-O LC_SEGMENT command size")?;
Ok(Some((command, data)))
} else {
Ok(None)
}
}
pub fn symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>> {
if self.cmd == macho::LC_SYMTAB {
Some(
self.data
.clone()
.read()
.read_error("Invalid Mach-O LC_SYMTAB command size"),
)
.transpose()
} else {
Ok(None)
}
}
pub fn dysymtab(self) -> Result<Option<&'data macho::DysymtabCommand<E>>> {
if self.cmd == macho::LC_DYSYMTAB {
Some(
self.data
.clone()
.read()
.read_error("Invalid Mach-O LC_DYSYMTAB command size"),
)
.transpose()
} else {
Ok(None)
}
}
pub fn dylib(self) -> Result<Option<&'data macho::Dylib<E>>> {
if self.cmd == macho::LC_LOAD_DYLIB
|| self.cmd == macho::LC_LOAD_WEAK_DYLIB
|| self.cmd == macho::LC_REEXPORT_DYLIB
|| self.cmd == macho::LC_LAZY_LOAD_DYLIB
|| self.cmd == macho::LC_LOAD_UPWARD_DYLIB
{
let command = self
.data
.clone()
.read::<macho::DylibCommand<_>>()
.read_error("Invalid Mach-O LC_*_DYLIB command size")?;
Ok(Some(&command.dylib))
} else {
Ok(None)
}
}
pub fn uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>> {
if self.cmd == macho::LC_UUID {
Some(
self.data
.clone()
.read()
.read_error("Invalid Mach-O LC_UUID command size"),
)
.transpose()
} else {
Ok(None)
}
}
pub fn segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, Bytes<'data>)>> {
if self.cmd == macho::LC_SEGMENT_64 {
let mut data = self.data;
let command = data
.read()
.read_error("Invalid Mach-O LC_SEGMENT_64 command size")?;
Ok(Some((command, data)))
} else {
Ok(None)
}
}
pub fn dyld_info(self) -> Result<Option<&'data macho::DyldInfoCommand<E>>> {
if self.cmd == macho::LC_DYLD_INFO || self.cmd == macho::LC_DYLD_INFO_ONLY {
Some(
self.data
.clone()
.read()
.read_error("Invalid Mach-O LC_DYLD_INFO command size"),
)
.transpose()
} else {
Ok(None)
}
}
pub fn entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>> {
if self.cmd == macho::LC_MAIN {
Some(
self.data
.clone()
.read()
.read_error("Invalid Mach-O LC_MAIN command size"),
)
.transpose()
} else {
Ok(None)
}
}
pub fn string(&self, endian: E, s: macho::LcStr<E>) -> Result<&'data [u8]> {
self.data
.read_string_at(s.offset.get(endian) as usize)
.read_error("Invalid load command string offset")
}
}
impl<E: Endian> macho::SymtabCommand<E> {
pub fn symbols<'data, Mach: MachHeader<Endian = E>>(
&self,
endian: E,
data: Bytes<'data>,
) -> Result<SymbolTable<'data, Mach>> {
let symbols = data
.read_slice_at(
self.symoff.get(endian) as usize,
self.nsyms.get(endian) as usize,
)
.read_error("Invalid Mach-O symbol table offset or size")?;
let strings = data
.read_bytes_at(
self.stroff.get(endian) as usize,
self.strsize.get(endian) as usize,
)
.read_error("Invalid Mach-O string table offset or size")?;
let strings = StringTable::new(strings);
Ok(SymbolTable::new(symbols, strings))
}
}