use crate::error;
use core::convert::TryFrom;
use core::fmt::{self, Display};
use scroll::{ctx, Endian};
use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith};
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct LoadCommandHeader {
pub cmd: u32,
pub cmdsize: u32,
}
impl Display for LoadCommandHeader {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"LoadCommandHeader: {} size: {}",
cmd_to_str(self.cmd),
self.cmdsize
)
}
}
pub const SIZEOF_LOAD_COMMAND: usize = 8;
pub type LcStr = u32;
pub const SIZEOF_LC_STR: usize = 4;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct Section32 {
pub sectname: [u8; 16],
pub segname: [u8; 16],
pub addr: u32,
pub size: u32,
pub offset: u32,
pub align: u32,
pub reloff: u32,
pub nreloc: u32,
pub flags: u32,
pub reserved1: u32,
pub reserved2: u32,
}
pub const SIZEOF_SECTION_32: usize = 68;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct Section64 {
pub sectname: [u8; 16],
pub segname: [u8; 16],
pub addr: u64,
pub size: u64,
pub offset: u32,
pub align: u32,
pub reloff: u32,
pub nreloc: u32,
pub flags: u32,
pub reserved1: u32,
pub reserved2: u32,
pub reserved3: u32,
}
pub const SIZEOF_SECTION_64: usize = 80;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SegmentCommand32 {
pub cmd: u32,
pub cmdsize: u32,
pub segname: [u8; 16],
pub vmaddr: u32,
pub vmsize: u32,
pub fileoff: u32,
pub filesize: u32,
pub maxprot: u32,
pub initprot: u32,
pub nsects: u32,
pub flags: u32,
}
pub const SIZEOF_SEGMENT_COMMAND_32: usize = 56;
impl SegmentCommand32 {
pub fn name(&self) -> error::Result<&str> {
Ok(self.segname.pread::<&str>(0)?)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SegmentCommand64 {
pub cmd: u32,
pub cmdsize: u32,
pub segname: [u8; 16],
pub vmaddr: u64,
pub vmsize: u64,
pub fileoff: u64,
pub filesize: u64,
pub maxprot: u32,
pub initprot: u32,
pub nsects: u32,
pub flags: u32,
}
pub const SIZEOF_SEGMENT_COMMAND_64: usize = 72;
impl SegmentCommand64 {
pub fn name(&self) -> error::Result<&str> {
Ok(self.segname.pread::<&str>(0)?)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct Fvmlib {
pub name: u32,
pub minor_version: u32,
pub header_addr: u32,
}
pub const SIZEOF_FVMLIB: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct FvmlibCommand {
pub cmd: u32,
pub cmdsize: u32,
pub fvmlib: Fvmlib,
}
pub const SIZEOF_FVMLIB_COMMAND: usize = 20;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct Dylib {
pub name: LcStr,
pub timestamp: u32,
pub current_version: u32,
pub compatibility_version: u32,
}
pub const SIZEOF_DYLIB: usize = 16;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylibCommand {
pub cmd: u32,
pub cmdsize: u32,
pub dylib: Dylib,
}
pub const SIZEOF_DYLIB_COMMAND: usize = 20;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SubFrameworkCommand {
pub cmd: u32,
pub cmdsize: u32,
pub umbrella: u32,
}
pub const SIZEOF_SUB_FRAMEWORK_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SubClientCommand {
pub cmd: u32,
pub cmdsize: u32,
pub client: LcStr,
}
pub const SIZEOF_SUB_CLIENT_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SubUmbrellaCommand {
pub cmd: u32,
pub cmdsize: u32,
pub sub_umbrella: LcStr,
}
pub const SIZEOF_SUB_UMBRELLA_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SubLibraryCommand {
pub cmd: u32,
pub cmdsize: u32,
pub sub_library: LcStr,
}
pub const SIZEOF_SUB_LIBRARY_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct PreboundDylibCommand {
pub cmd: u32,
pub cmdsize: u32,
pub name: LcStr,
pub nmodules: u32,
pub linked_modules: LcStr,
}
pub const SIZEOF_PREBOUND_DYLIB_COMMAND: usize = 20;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylinkerCommand {
pub cmd: u32,
pub cmdsize: u32,
pub name: LcStr,
}
pub const SIZEOF_DYLINKER_COMMAND: usize = 12;
#[repr(C)]
#[derive(Copy)]
pub struct ThreadCommand {
pub cmd: u32,
pub cmdsize: u32,
pub flavor: u32,
pub count: u32,
pub thread_state: [u32; 70],
}
impl ThreadCommand {
pub fn instruction_pointer(&self, cputype: super::cputype::CpuType) -> error::Result<u64> {
match cputype {
super::cputype::CPU_TYPE_X86 => {
let eip: u32 = self.thread_state[10];
Ok(u64::from(eip))
}
super::cputype::CPU_TYPE_X86_64 => {
let rip: u64 =
(u64::from(self.thread_state[32])) | ((u64::from(self.thread_state[33])) << 32);
Ok(rip)
}
super::cputype::CPU_TYPE_ARM => {
let pc: u32 = self.thread_state[15];
Ok(u64::from(pc))
}
super::cputype::CPU_TYPE_ARM64 | super::cputype::CPU_TYPE_ARM64_32 => {
let pc: u64 =
(u64::from(self.thread_state[64])) | ((u64::from(self.thread_state[65])) << 32);
Ok(pc)
}
super::cputype::CPU_TYPE_POWERPC => Ok(u64::from(self.thread_state[0])),
_ => Err(error::Error::Malformed(format!(
"unable to find instruction pointer for cputype {:?}",
cputype
))),
}
}
}
impl<'a> ctx::TryFromCtx<'a, Endian> for ThreadCommand {
type Error = crate::error::Error;
fn try_from_ctx(bytes: &'a [u8], le: Endian) -> error::Result<(Self, usize)> {
let lc = bytes.pread_with::<LoadCommandHeader>(0, le)?;
let flavor: u32 = bytes.pread_with(8, le)?;
let count: u32 = bytes.pread_with(12, le)?;
if count > 70 {
return Err(error::Error::Malformed(format!(
"thread command specifies {} longs for thread state but we handle only 70",
count
)));
}
let thread_state_byte_length = count as usize * 4;
if bytes.len() < 16 + thread_state_byte_length {
return Err(error::Error::Malformed(format!(
"thread command specifies {} bytes for thread state but has only {}",
thread_state_byte_length,
bytes.len()
)));
}
let thread_state_bytes = &bytes[16..16 + thread_state_byte_length];
let mut thread_state: [u32; 70] = [0; 70];
for (i, state) in thread_state.iter_mut().enumerate().take(count as usize) {
*state = thread_state_bytes.pread_with(i * 4, le)?;
}
Ok((
ThreadCommand {
cmd: lc.cmd,
cmdsize: lc.cmdsize,
flavor,
count,
thread_state,
},
lc.cmdsize as _,
))
}
}
impl Clone for ThreadCommand {
fn clone(&self) -> Self {
*self
}
}
impl fmt::Debug for ThreadCommand {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("ThreadCommand")
.field("cmd", &self.cmd)
.field("cmdsize", &self.cmdsize)
.field("flavor", &self.flavor)
.field("count", &self.count)
.field("thread_state", &&self.thread_state[..])
.finish()
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct RoutinesCommand32 {
pub cmd: u32,
pub cmdsize: u32,
pub init_address: u32,
pub init_module: u32,
pub reserved1: u32,
pub reserved2: u32,
pub reserved3: u32,
pub reserved4: u32,
pub reserved5: u32,
pub reserved6: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct RoutinesCommand64 {
pub cmd: u32,
pub cmdsize: u32,
pub init_address: u64,
pub init_module: u64,
pub reserved1: u64,
pub reserved2: u64,
pub reserved3: u64,
pub reserved4: u64,
pub reserved5: u64,
pub reserved6: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SymtabCommand {
pub cmd: u32,
pub cmdsize: u32,
pub symoff: u32,
pub nsyms: u32,
pub stroff: u32,
pub strsize: u32,
}
impl Default for SymtabCommand {
fn default() -> Self {
SymtabCommand {
cmd: LC_SYMTAB,
cmdsize: SIZEOF_SYMTAB_COMMAND as u32,
symoff: 0,
nsyms: 0,
stroff: 0,
strsize: 0,
}
}
}
impl SymtabCommand {
pub fn new() -> Self {
Default::default()
}
}
pub const SIZEOF_SYMTAB_COMMAND: usize = 24;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DysymtabCommand {
pub cmd: u32,
pub cmdsize: u32,
pub ilocalsym: u32,
pub nlocalsym: u32,
pub iextdefsym: u32,
pub nextdefsym: u32,
pub iundefsym: u32,
pub nundefsym: u32,
pub tocoff: u32,
pub ntoc: u32,
pub modtaboff: u32,
pub nmodtab: u32,
pub extrefsymoff: u32,
pub nextrefsyms: u32,
pub indirectsymoff: u32,
pub nindirectsyms: u32,
pub extreloff: u32,
pub nextrel: u32,
pub locreloff: u32,
pub nlocrel: u32,
}
impl Default for DysymtabCommand {
fn default() -> Self {
DysymtabCommand {
cmd: LC_DYSYMTAB,
cmdsize: SIZEOF_DYSYMTAB_COMMAND as u32,
ilocalsym: 0,
nlocalsym: 0,
iextdefsym: 0,
nextdefsym: 0,
iundefsym: 0,
nundefsym: 0,
tocoff: 0,
ntoc: 0,
modtaboff: 0,
nmodtab: 0,
extrefsymoff: 0,
nextrefsyms: 0,
indirectsymoff: 0,
nindirectsyms: 0,
extreloff: 0,
nextrel: 0,
locreloff: 0,
nlocrel: 0,
}
}
}
impl DysymtabCommand {
pub fn new() -> Self {
Default::default()
}
}
pub const SIZEOF_DYSYMTAB_COMMAND: usize = 80;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylibTableOfContents {
pub symbol_index: u32,
pub module_index: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylibModule {
pub module_name: u32,
pub iextdefsym: u32,
pub nextdefsym: u32,
pub irefsym: u32,
pub nrefsym: u32,
pub ilocalsym: u32,
pub nlocalsym: u32,
pub iextrel: u32,
pub nextrel: u32,
pub iinit_iterm: u32,
pub ninit_nterm: u32,
pub objc_module_info_addr: u32,
pub objc_module_info_size: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylibModule64 {
pub module_name: u32,
pub iextdefsym: u32,
pub nextdefsym: u32,
pub irefsym: u32,
pub nrefsym: u32,
pub ilocalsym: u32,
pub nlocalsym: u32,
pub iextrel: u32,
pub nextrel: u32,
pub iinit_iterm: u32,
pub ninit_nterm: u32,
pub objc_module_info_size: u32,
pub objc_module_info_addr: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DylibReference {
pub isym: [u8; 24],
pub flags: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct TwolevelHintsCommand {
pub cmd: u32,
pub cmdsize: u32,
pub offset: u32,
pub nhints: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct TwolevelHint {
pub isub_image: u64,
pub itoc: [u8; 24],
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct PrebindCksumCommand {
pub cmd: u32,
pub cmdsize: u32,
pub cksum: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct UuidCommand {
pub cmd: u32,
pub cmdsize: u32,
pub uuid: [u8; 16],
}
pub const SIZEOF_UUID_COMMAND: usize = 24;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct RpathCommand {
pub cmd: u32,
pub cmdsize: u32,
pub path: LcStr,
}
pub const SIZEOF_RPATH_COMMAND: usize = 12;
#[repr(C)]
#[derive(Default, Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct LinkeditDataCommand {
pub cmd: u32,
pub cmdsize: u32,
pub dataoff: u32,
pub datasize: u32,
}
pub const SIZEOF_LINKEDIT_DATA_COMMAND: usize = 16;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct EncryptionInfoCommand32 {
pub cmd: u32,
pub cmdsize: u32,
pub cryptoff: u32,
pub cryptsize: u32,
pub cryptid: u32,
}
pub const SIZEOF_ENCRYPTION_INFO_COMMAND_32: usize = 20;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct EncryptionInfoCommand64 {
pub cmd: u32,
pub cmdsize: u32,
pub cryptoff: u32,
pub cryptsize: u32,
pub cryptid: u32,
pub pad: u32,
}
pub const SIZEOF_ENCRYPTION_INFO_COMMAND_64: usize = 24;
#[non_exhaustive]
#[repr(u32)]
#[derive(Debug)]
pub enum Platform {
Macos = LC_VERSION_MIN_MACOSX,
Iphoneos = LC_VERSION_MIN_IPHONEOS,
Tvos = LC_VERSION_MIN_TVOS,
Watchos = LC_VERSION_MIN_WATCHOS,
}
impl TryFrom<u32> for Platform {
type Error = error::Error;
fn try_from(cmd: u32) -> Result<Self, Self::Error> {
Ok(match cmd {
LC_VERSION_MIN_MACOSX => Platform::Macos,
LC_VERSION_MIN_IPHONEOS => Platform::Iphoneos,
LC_VERSION_MIN_TVOS => Platform::Tvos,
LC_VERSION_MIN_WATCHOS => Platform::Watchos,
_ => {
return Err(error::Error::Malformed(format!(
"unknown platform for load command: {:x}",
cmd
)))
}
})
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct VersionMinCommand {
pub cmd: u32,
pub cmdsize: u32,
pub version: u32,
pub sdk: u32,
}
impl VersionMinCommand {
pub fn new(platform: Platform) -> Self {
VersionMinCommand {
cmd: platform as u32,
cmdsize: SIZEOF_VERSION_MIN_COMMAND as u32,
version: 0,
sdk: 0,
}
}
pub fn platform(&self) -> Platform {
Platform::try_from(self.cmd).expect("impossible platform (implementation error)")
}
}
pub const SIZEOF_VERSION_MIN_COMMAND: usize = 16;
#[repr(C)]
#[derive(Default, Debug, Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct DyldInfoCommand {
pub cmd: u32,
pub cmdsize: u32,
pub rebase_off: u32,
pub rebase_size: u32,
pub bind_off: u32,
pub bind_size: u32,
pub weak_bind_off: u32,
pub weak_bind_size: u32,
pub lazy_bind_off: u32,
pub lazy_bind_size: u32,
pub export_off: u32,
pub export_size: u32,
}
pub const SIZEOF_DYLIB_INFO_COMMAND: usize = 48;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct LinkerOptionCommand {
pub cmd: u32,
pub cmdsize: u32,
pub count: u32,
}
pub const SIZEOF_LINKER_OPTION_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SymsegCommand {
pub cmd: u32,
pub cmdsize: u32,
pub offset: u32,
pub size: u32,
}
pub const SIZEOF_SYMSEG_COMMAND: usize = 16;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct IdentCommand {
pub cmd: u32,
pub cmdsize: u32,
}
pub const SIZEOF_IDENT_COMMAND: usize = 8;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct FvmfileCommand {
pub cmd: u32,
pub cmdsize: u32,
pub name: LcStr,
pub header_addr: u32,
}
pub const SIZEOF_FVMFILE_COMMAND: usize = 16;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct EntryPointCommand {
pub cmd: u32,
pub cmdsize: u32,
pub entryoff: u64,
pub stacksize: u64,
}
pub const SIZEOF_ENTRY_POINT_COMMAND: usize = 24;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct SourceVersionCommand {
pub cmd: u32,
pub cmdsize: u32,
pub version: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct DataInCodeEntry {
pub offset: u32,
pub length: u16,
pub kind: u16,
}
pub const LC_REQ_DYLD: u32 = 0x8000_0000;
pub const LC_LOAD_WEAK_DYLIB: u32 = 0x18 | LC_REQ_DYLD;
pub const LC_RPATH: u32 = 0x1c | LC_REQ_DYLD;
pub const LC_REEXPORT_DYLIB: u32 = 0x1f | LC_REQ_DYLD;
pub const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD;
pub const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD;
pub const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD;
pub const LC_DYLD_EXPORTS_TRIE: u32 = 0x33 | LC_REQ_DYLD;
pub const LC_DYLD_CHAINED_FIXUPS: u32 = 0x34 | LC_REQ_DYLD;
pub const LC_SEGMENT: u32 = 0x1;
pub const LC_SYMTAB: u32 = 0x2;
pub const LC_SYMSEG: u32 = 0x3;
pub const LC_THREAD: u32 = 0x4;
pub const LC_UNIXTHREAD: u32 = 0x5;
pub const LC_LOADFVMLIB: u32 = 0x6;
pub const LC_IDFVMLIB: u32 = 0x7;
pub const LC_IDENT: u32 = 0x8;
pub const LC_FVMFILE: u32 = 0x9;
pub const LC_PREPAGE: u32 = 0xa;
pub const LC_DYSYMTAB: u32 = 0xb;
pub const LC_LOAD_DYLIB: u32 = 0xc;
pub const LC_ID_DYLIB: u32 = 0xd;
pub const LC_LOAD_DYLINKER: u32 = 0xe;
pub const LC_ID_DYLINKER: u32 = 0xf;
pub const LC_PREBOUND_DYLIB: u32 = 0x10;
pub const LC_ROUTINES: u32 = 0x11;
pub const LC_SUB_FRAMEWORK: u32 = 0x12;
pub const LC_SUB_UMBRELLA: u32 = 0x13;
pub const LC_SUB_CLIENT: u32 = 0x14;
pub const LC_SUB_LIBRARY: u32 = 0x15;
pub const LC_TWOLEVEL_HINTS: u32 = 0x16;
pub const LC_PREBIND_CKSUM: u32 = 0x17;
pub const LC_SEGMENT_64: u32 = 0x19;
pub const LC_ROUTINES_64: u32 = 0x1a;
pub const LC_UUID: u32 = 0x1b;
pub const LC_CODE_SIGNATURE: u32 = 0x1d;
pub const LC_SEGMENT_SPLIT_INFO: u32 = 0x1e;
pub const LC_LAZY_LOAD_DYLIB: u32 = 0x20;
pub const LC_ENCRYPTION_INFO: u32 = 0x21;
pub const LC_DYLD_INFO: u32 = 0x22;
pub const LC_VERSION_MIN_MACOSX: u32 = 0x24;
pub const LC_VERSION_MIN_IPHONEOS: u32 = 0x25;
pub const LC_FUNCTION_STARTS: u32 = 0x26;
pub const LC_DYLD_ENVIRONMENT: u32 = 0x27;
pub const LC_DATA_IN_CODE: u32 = 0x29;
pub const LC_SOURCE_VERSION: u32 = 0x2A;
pub const LC_DYLIB_CODE_SIGN_DRS: u32 = 0x2B;
pub const LC_ENCRYPTION_INFO_64: u32 = 0x2C;
pub const LC_LINKER_OPTION: u32 = 0x2D;
pub const LC_LINKER_OPTIMIZATION_HINT: u32 = 0x2E;
pub const LC_VERSION_MIN_TVOS: u32 = 0x2F;
pub const LC_VERSION_MIN_WATCHOS: u32 = 0x30;
pub const LC_NOTE: u32 = 0x31;
pub const LC_BUILD_VERSION: u32 = 0x32;
pub fn cmd_to_str(cmd: u32) -> &'static str {
match cmd {
LC_SEGMENT => "LC_SEGMENT",
LC_SYMTAB => "LC_SYMTAB",
LC_SYMSEG => "LC_SYMSEG",
LC_THREAD => "LC_THREAD",
LC_UNIXTHREAD => "LC_UNIXTHREAD",
LC_LOADFVMLIB => "LC_LOADFVMLIB",
LC_IDFVMLIB => "LC_IDFVMLIB",
LC_IDENT => "LC_IDENT",
LC_FVMFILE => "LC_FVMFILE",
LC_PREPAGE => "LC_PREPAGE",
LC_DYSYMTAB => "LC_DYSYMTAB",
LC_LOAD_DYLIB => "LC_LOAD_DYLIB",
LC_ID_DYLIB => "LC_ID_DYLIB",
LC_LOAD_DYLINKER => "LC_LOAD_DYLINKER",
LC_ID_DYLINKER => "LC_ID_DYLINKER",
LC_PREBOUND_DYLIB => "LC_PREBOUND_DYLIB",
LC_ROUTINES => "LC_ROUTINES",
LC_SUB_FRAMEWORK => "LC_SUB_FRAMEWORK",
LC_SUB_UMBRELLA => "LC_SUB_UMBRELLA",
LC_SUB_CLIENT => "LC_SUB_CLIENT",
LC_SUB_LIBRARY => "LC_SUB_LIBRARY",
LC_TWOLEVEL_HINTS => "LC_TWOLEVEL_HINTS",
LC_PREBIND_CKSUM => "LC_PREBIND_CKSUM",
LC_LOAD_WEAK_DYLIB => "LC_LOAD_WEAK_DYLIB",
LC_SEGMENT_64 => "LC_SEGMENT_64",
LC_ROUTINES_64 => "LC_ROUTINES_64",
LC_UUID => "LC_UUID",
LC_RPATH => "LC_RPATH",
LC_CODE_SIGNATURE => "LC_CODE_SIGNATURE",
LC_SEGMENT_SPLIT_INFO => "LC_SEGMENT_SPLIT_INFO",
LC_REEXPORT_DYLIB => "LC_REEXPORT_DYLIB",
LC_LAZY_LOAD_DYLIB => "LC_LAZY_LOAD_DYLIB",
LC_ENCRYPTION_INFO => "LC_ENCRYPTION_INFO",
LC_DYLD_INFO => "LC_DYLD_INFO",
LC_DYLD_INFO_ONLY => "LC_DYLD_INFO_ONLY",
LC_LOAD_UPWARD_DYLIB => "LC_LOAD_UPWARD_DYLIB",
LC_VERSION_MIN_MACOSX => "LC_VERSION_MIN_MACOSX",
LC_VERSION_MIN_IPHONEOS => "LC_VERSION_MIN_IPHONEOS",
LC_FUNCTION_STARTS => "LC_FUNCTION_STARTS",
LC_DYLD_ENVIRONMENT => "LC_DYLD_ENVIRONMENT",
LC_MAIN => "LC_MAIN",
LC_DATA_IN_CODE => "LC_DATA_IN_CODE",
LC_SOURCE_VERSION => "LC_SOURCE_VERSION",
LC_DYLIB_CODE_SIGN_DRS => "LC_DYLIB_CODE_SIGN_DRS",
LC_ENCRYPTION_INFO_64 => "LC_ENCRYPTION_INFO_64",
LC_LINKER_OPTION => "LC_LINKER_OPTION",
LC_LINKER_OPTIMIZATION_HINT => "LC_LINKER_OPTIMIZATION_HINT",
LC_VERSION_MIN_TVOS => "LC_VERSION_MIN_TVOS",
LC_VERSION_MIN_WATCHOS => "LC_VERSION_MIN_WATCHOS",
LC_NOTE => "LC_NOTE",
LC_BUILD_VERSION => "LC_BUILD_VERSION",
LC_DYLD_EXPORTS_TRIE => "LC_DYLD_EXPORTS_TRIE",
LC_DYLD_CHAINED_FIXUPS => "LC_DYLD_CHAINED_FIXUPS",
_ => "LC_UNKNOWN",
}
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum CommandVariant {
Segment32(SegmentCommand32),
Segment64(SegmentCommand64),
Uuid(UuidCommand),
Symtab(SymtabCommand),
Symseg(SymsegCommand),
Thread(ThreadCommand),
Unixthread(ThreadCommand),
LoadFvmlib(FvmlibCommand),
IdFvmlib(FvmlibCommand),
Ident(IdentCommand),
Fvmfile(FvmfileCommand),
Prepage(LoadCommandHeader),
Dysymtab(DysymtabCommand),
LoadDylib(DylibCommand),
IdDylib(DylibCommand),
LoadDylinker(DylinkerCommand),
IdDylinker(DylinkerCommand),
PreboundDylib(PreboundDylibCommand),
Routines32(RoutinesCommand32),
Routines64(RoutinesCommand64),
SubFramework(SubFrameworkCommand),
SubUmbrella(SubUmbrellaCommand),
SubClient(SubClientCommand),
SubLibrary(SubLibraryCommand),
TwolevelHints(TwolevelHintsCommand),
PrebindCksum(PrebindCksumCommand),
LoadWeakDylib(DylibCommand),
Rpath(RpathCommand),
CodeSignature(LinkeditDataCommand),
SegmentSplitInfo(LinkeditDataCommand),
ReexportDylib(DylibCommand),
LazyLoadDylib(DylibCommand),
EncryptionInfo32(EncryptionInfoCommand32),
EncryptionInfo64(EncryptionInfoCommand64),
DyldInfo(DyldInfoCommand),
DyldInfoOnly(DyldInfoCommand),
LoadUpwardDylib(DylibCommand),
VersionMinMacosx(VersionMinCommand),
VersionMinIphoneos(VersionMinCommand),
FunctionStarts(LinkeditDataCommand),
DyldEnvironment(DylinkerCommand),
Main(EntryPointCommand),
DataInCode(LinkeditDataCommand),
SourceVersion(SourceVersionCommand),
DylibCodeSignDrs(LinkeditDataCommand),
LinkerOption(LinkeditDataCommand),
LinkerOptimizationHint(LinkeditDataCommand),
VersionMinTvos(VersionMinCommand),
VersionMinWatchos(VersionMinCommand),
DyldExportsTrie(LinkeditDataCommand),
DyldChainedFixups(LinkeditDataCommand),
Unimplemented(LoadCommandHeader),
}
impl<'a> ctx::TryFromCtx<'a, Endian> for CommandVariant {
type Error = crate::error::Error;
fn try_from_ctx(bytes: &'a [u8], le: Endian) -> error::Result<(Self, usize)> {
use self::CommandVariant::*;
let lc = bytes.pread_with::<LoadCommandHeader>(0, le)?;
let size = lc.cmdsize as usize;
if size > bytes.len() {
return Err(error::Error::Malformed(format!(
"{} has size larger than remainder of binary: {:?}",
&lc,
bytes.len()
)));
}
match lc.cmd {
LC_SEGMENT => {
let comm = bytes.pread_with::<SegmentCommand32>(0, le)?;
Ok((Segment32(comm), size))
}
LC_SEGMENT_64 => {
let comm = bytes.pread_with::<SegmentCommand64>(0, le)?;
Ok((Segment64(comm), size))
}
LC_DYSYMTAB => {
let comm = bytes.pread_with::<DysymtabCommand>(0, le)?;
Ok((Dysymtab(comm), size))
}
LC_LOAD_DYLINKER => {
let comm = bytes.pread_with::<DylinkerCommand>(0, le)?;
Ok((LoadDylinker(comm), size))
}
LC_ID_DYLINKER => {
let comm = bytes.pread_with::<DylinkerCommand>(0, le)?;
Ok((IdDylinker(comm), size))
}
LC_UUID => {
let comm = bytes.pread_with::<UuidCommand>(0, le)?;
Ok((Uuid(comm), size))
}
LC_SYMTAB => {
let comm = bytes.pread_with::<SymtabCommand>(0, le)?;
Ok((Symtab(comm), size))
}
LC_SYMSEG => {
let comm = bytes.pread_with::<SymsegCommand>(0, le)?;
Ok((Symseg(comm), size))
}
LC_THREAD => {
let comm = bytes.pread_with::<ThreadCommand>(0, le)?;
Ok((Thread(comm), size))
}
LC_UNIXTHREAD => {
let comm = bytes.pread_with::<ThreadCommand>(0, le)?;
Ok((Unixthread(comm), size))
}
LC_LOADFVMLIB => {
let comm = bytes.pread_with::<FvmlibCommand>(0, le)?;
Ok((LoadFvmlib(comm), size))
}
LC_IDFVMLIB => {
let comm = bytes.pread_with::<FvmlibCommand>(0, le)?;
Ok((IdFvmlib(comm), size))
}
LC_IDENT => {
let comm = bytes.pread_with::<IdentCommand>(0, le)?;
Ok((Ident(comm), size))
}
LC_FVMFILE => {
let comm = bytes.pread_with::<FvmfileCommand>(0, le)?;
Ok((Fvmfile(comm), size))
}
LC_PREPAGE => {
let comm = bytes.pread_with::<LoadCommandHeader>(0, le)?;
Ok((Prepage(comm), size))
}
LC_LOAD_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((LoadDylib(comm), size))
}
LC_ID_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((IdDylib(comm), size))
}
LC_PREBOUND_DYLIB => {
let comm = bytes.pread_with::<PreboundDylibCommand>(0, le)?;
Ok((PreboundDylib(comm), size))
}
LC_ROUTINES => {
let comm = bytes.pread_with::<RoutinesCommand32>(0, le)?;
Ok((Routines32(comm), size))
}
LC_ROUTINES_64 => {
let comm = bytes.pread_with::<RoutinesCommand64>(0, le)?;
Ok((Routines64(comm), size))
}
LC_SUB_FRAMEWORK => {
let comm = bytes.pread_with::<SubFrameworkCommand>(0, le)?;
Ok((SubFramework(comm), size))
}
LC_SUB_UMBRELLA => {
let comm = bytes.pread_with::<SubUmbrellaCommand>(0, le)?;
Ok((SubUmbrella(comm), size))
}
LC_SUB_CLIENT => {
let comm = bytes.pread_with::<SubClientCommand>(0, le)?;
Ok((SubClient(comm), size))
}
LC_SUB_LIBRARY => {
let comm = bytes.pread_with::<SubLibraryCommand>(0, le)?;
Ok((SubLibrary(comm), size))
}
LC_TWOLEVEL_HINTS => {
let comm = bytes.pread_with::<TwolevelHintsCommand>(0, le)?;
Ok((TwolevelHints(comm), size))
}
LC_PREBIND_CKSUM => {
let comm = bytes.pread_with::<PrebindCksumCommand>(0, le)?;
Ok((PrebindCksum(comm), size))
}
LC_LOAD_WEAK_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((LoadWeakDylib(comm), size))
}
LC_RPATH => {
let comm = bytes.pread_with::<RpathCommand>(0, le)?;
Ok((Rpath(comm), size))
}
LC_CODE_SIGNATURE => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((CodeSignature(comm), size))
}
LC_SEGMENT_SPLIT_INFO => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((SegmentSplitInfo(comm), size))
}
LC_REEXPORT_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((ReexportDylib(comm), size))
}
LC_LAZY_LOAD_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((LazyLoadDylib(comm), size))
}
LC_ENCRYPTION_INFO => {
let comm = bytes.pread_with::<EncryptionInfoCommand32>(0, le)?;
Ok((EncryptionInfo32(comm), size))
}
LC_ENCRYPTION_INFO_64 => {
let comm = bytes.pread_with::<EncryptionInfoCommand64>(0, le)?;
Ok((EncryptionInfo64(comm), size))
}
LC_DYLD_INFO => {
let comm = bytes.pread_with::<DyldInfoCommand>(0, le)?;
Ok((DyldInfo(comm), size))
}
LC_DYLD_INFO_ONLY => {
let comm = bytes.pread_with::<DyldInfoCommand>(0, le)?;
Ok((DyldInfoOnly(comm), size))
}
LC_LOAD_UPWARD_DYLIB => {
let comm = bytes.pread_with::<DylibCommand>(0, le)?;
Ok((LoadUpwardDylib(comm), size))
}
LC_VERSION_MIN_MACOSX => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinMacosx(comm), size))
}
LC_VERSION_MIN_IPHONEOS => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinIphoneos(comm), size))
}
LC_FUNCTION_STARTS => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((FunctionStarts(comm), size))
}
LC_DYLD_ENVIRONMENT => {
let comm = bytes.pread_with::<DylinkerCommand>(0, le)?;
Ok((DyldEnvironment(comm), size))
}
LC_MAIN => {
let comm = bytes.pread_with::<EntryPointCommand>(0, le)?;
Ok((Main(comm), size))
}
LC_DATA_IN_CODE => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((DataInCode(comm), size))
}
LC_SOURCE_VERSION => {
let comm = bytes.pread_with::<SourceVersionCommand>(0, le)?;
Ok((SourceVersion(comm), size))
}
LC_DYLIB_CODE_SIGN_DRS => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((DylibCodeSignDrs(comm), size))
}
LC_LINKER_OPTION => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((LinkerOption(comm), size))
}
LC_LINKER_OPTIMIZATION_HINT => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((LinkerOptimizationHint(comm), size))
}
LC_VERSION_MIN_TVOS => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinTvos(comm), size))
}
LC_VERSION_MIN_WATCHOS => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinWatchos(comm), size))
}
LC_DYLD_EXPORTS_TRIE => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((DyldExportsTrie(comm), size))
}
LC_DYLD_CHAINED_FIXUPS => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((DyldChainedFixups(comm), size))
}
LC_NOTE | LC_BUILD_VERSION | _ => Ok((Unimplemented(lc), size)),
}
}
}
impl CommandVariant {
pub fn cmdsize(&self) -> usize {
use self::CommandVariant::*;
let cmdsize = match *self {
Segment32(comm) => comm.cmdsize,
Segment64(comm) => comm.cmdsize,
Uuid(comm) => comm.cmdsize,
Symtab(comm) => comm.cmdsize,
Symseg(comm) => comm.cmdsize,
Thread(comm) => comm.cmdsize,
Unixthread(comm) => comm.cmdsize,
LoadFvmlib(comm) => comm.cmdsize,
IdFvmlib(comm) => comm.cmdsize,
Ident(comm) => comm.cmdsize,
Fvmfile(comm) => comm.cmdsize,
Prepage(comm) => comm.cmdsize,
Dysymtab(comm) => comm.cmdsize,
LoadDylib(comm) => comm.cmdsize,
IdDylib(comm) => comm.cmdsize,
LoadDylinker(comm) => comm.cmdsize,
IdDylinker(comm) => comm.cmdsize,
PreboundDylib(comm) => comm.cmdsize,
Routines32(comm) => comm.cmdsize,
Routines64(comm) => comm.cmdsize,
SubFramework(comm) => comm.cmdsize,
SubUmbrella(comm) => comm.cmdsize,
SubClient(comm) => comm.cmdsize,
SubLibrary(comm) => comm.cmdsize,
TwolevelHints(comm) => comm.cmdsize,
PrebindCksum(comm) => comm.cmdsize,
LoadWeakDylib(comm) => comm.cmdsize,
Rpath(comm) => comm.cmdsize,
CodeSignature(comm) => comm.cmdsize,
SegmentSplitInfo(comm) => comm.cmdsize,
ReexportDylib(comm) => comm.cmdsize,
LazyLoadDylib(comm) => comm.cmdsize,
EncryptionInfo32(comm) => comm.cmdsize,
EncryptionInfo64(comm) => comm.cmdsize,
DyldInfo(comm) => comm.cmdsize,
DyldInfoOnly(comm) => comm.cmdsize,
LoadUpwardDylib(comm) => comm.cmdsize,
VersionMinMacosx(comm) => comm.cmdsize,
VersionMinIphoneos(comm) => comm.cmdsize,
FunctionStarts(comm) => comm.cmdsize,
DyldEnvironment(comm) => comm.cmdsize,
Main(comm) => comm.cmdsize,
DataInCode(comm) => comm.cmdsize,
SourceVersion(comm) => comm.cmdsize,
DylibCodeSignDrs(comm) => comm.cmdsize,
LinkerOption(comm) => comm.cmdsize,
LinkerOptimizationHint(comm) => comm.cmdsize,
VersionMinTvos(comm) => comm.cmdsize,
VersionMinWatchos(comm) => comm.cmdsize,
DyldExportsTrie(comm) => comm.cmdsize,
DyldChainedFixups(comm) => comm.cmdsize,
Unimplemented(comm) => comm.cmdsize,
};
cmdsize as usize
}
pub fn cmd(&self) -> u32 {
use self::CommandVariant::*;
match *self {
Segment32(comm) => comm.cmd,
Segment64(comm) => comm.cmd,
Uuid(comm) => comm.cmd,
Symtab(comm) => comm.cmd,
Symseg(comm) => comm.cmd,
Thread(comm) => comm.cmd,
Unixthread(comm) => comm.cmd,
LoadFvmlib(comm) => comm.cmd,
IdFvmlib(comm) => comm.cmd,
Ident(comm) => comm.cmd,
Fvmfile(comm) => comm.cmd,
Prepage(comm) => comm.cmd,
Dysymtab(comm) => comm.cmd,
LoadDylib(comm) => comm.cmd,
IdDylib(comm) => comm.cmd,
LoadDylinker(comm) => comm.cmd,
IdDylinker(comm) => comm.cmd,
PreboundDylib(comm) => comm.cmd,
Routines32(comm) => comm.cmd,
Routines64(comm) => comm.cmd,
SubFramework(comm) => comm.cmd,
SubUmbrella(comm) => comm.cmd,
SubClient(comm) => comm.cmd,
SubLibrary(comm) => comm.cmd,
TwolevelHints(comm) => comm.cmd,
PrebindCksum(comm) => comm.cmd,
LoadWeakDylib(comm) => comm.cmd,
Rpath(comm) => comm.cmd,
CodeSignature(comm) => comm.cmd,
SegmentSplitInfo(comm) => comm.cmd,
ReexportDylib(comm) => comm.cmd,
LazyLoadDylib(comm) => comm.cmd,
EncryptionInfo32(comm) => comm.cmd,
EncryptionInfo64(comm) => comm.cmd,
DyldInfo(comm) => comm.cmd,
DyldInfoOnly(comm) => comm.cmd,
LoadUpwardDylib(comm) => comm.cmd,
VersionMinMacosx(comm) => comm.cmd,
VersionMinIphoneos(comm) => comm.cmd,
FunctionStarts(comm) => comm.cmd,
DyldEnvironment(comm) => comm.cmd,
Main(comm) => comm.cmd,
DataInCode(comm) => comm.cmd,
SourceVersion(comm) => comm.cmd,
DylibCodeSignDrs(comm) => comm.cmd,
LinkerOption(comm) => comm.cmd,
LinkerOptimizationHint(comm) => comm.cmd,
VersionMinTvos(comm) => comm.cmd,
VersionMinWatchos(comm) => comm.cmd,
DyldExportsTrie(comm) => comm.cmd,
DyldChainedFixups(comm) => comm.cmd,
Unimplemented(comm) => comm.cmd,
}
}
}
#[derive(Debug)]
pub struct LoadCommand {
pub offset: usize,
pub command: CommandVariant,
}
impl LoadCommand {
pub fn parse(bytes: &[u8], offset: &mut usize, le: scroll::Endian) -> error::Result<Self> {
let start = *offset;
let command = bytes.pread_with::<CommandVariant>(start, le)?;
let size = command.cmdsize();
*offset = start + size;
Ok(LoadCommand {
offset: start,
command,
})
}
}