#![allow(non_camel_case_types, clippy::many_single_char_names, clippy::large_enum_variant)]
use std::fmt;
use std::io::{BufRead, Cursor, Read};
use std::ops::Deref;
use std::rc::Rc;
use std::str::FromStr;
use byteorder::{ByteOrder, ReadBytesExt};
use uuid::Uuid;
use crate::{
consts::*,
errors::{
Error::{self, *},
Result,
},
loader::MachHeader,
};
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct VersionTag(u32);
impl VersionTag {
pub fn major(self) -> u32 {
self.0 >> 16
}
pub fn minor(self) -> u32 {
(self.0 >> 8) & 0xFF
}
pub fn release(self) -> u32 {
self.0 & 0xFF
}
}
impl From<VersionTag> for u32 {
fn from(v: VersionTag) -> Self {
v.0
}
}
impl FromStr for VersionTag {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut parts = s.split('.');
let major = match parts.next() {
Some(s) if !s.is_empty() => s.parse()?,
_ => 0,
};
let minor = match parts.next() {
Some(s) if !s.is_empty() => s.parse()?,
_ => 0,
};
let release = match parts.next() {
Some(s) if !s.is_empty() => s.parse()?,
_ => 0,
};
Ok(VersionTag((major << 16) + (minor << 8) + release))
}
}
impl fmt::Display for VersionTag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.release() == 0 {
write!(f, "{}.{}", self.major(), self.minor())
} else {
write!(f, "{}.{}.{}", self.major(), self.minor(), self.release())
}
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct SourceVersionTag(u64);
impl From<SourceVersionTag> for u64 {
fn from(v: SourceVersionTag) -> Self {
v.0
}
}
impl From<SourceVersionTag> for (u32, u32, u32, u32, u32) {
fn from(v: SourceVersionTag) -> (u32, u32, u32, u32, u32) {
(
((v.0 >> 40) & 0xFFF) as u32,
((v.0 >> 30) & 0x3FF) as u32,
((v.0 >> 20) & 0x3FF) as u32,
((v.0 >> 10) & 0x3FF) as u32,
(v.0 & 0x3FF) as u32,
)
}
}
impl fmt::Display for SourceVersionTag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (a, b, c, d, e) = Self::into(*self);
if e != 0 {
write!(f, "{}.{}.{}.{}.{}", a, b, c, d, e)
} else if d != 0 {
write!(f, "{}.{}.{}.{}", a, b, c, d)
} else if c != 0 {
write!(f, "{}.{}.{}", a, b, c)
} else {
write!(f, "{}.{}", a, b)
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BuildTarget {
MacOsX,
IPhoneOs,
WatchOs,
TvOs,
}
impl From<u32> for BuildTarget {
fn from(cmd: u32) -> Self {
match cmd {
LC_VERSION_MIN_MACOSX => BuildTarget::MacOsX,
LC_VERSION_MIN_IPHONEOS => BuildTarget::IPhoneOs,
LC_VERSION_MIN_WATCHOS => BuildTarget::WatchOs,
LC_VERSION_MIN_TVOS => BuildTarget::TvOs,
_ => unreachable!(),
}
}
}
impl From<BuildTarget> for u32 {
fn from(t: BuildTarget) -> u32 {
match t {
BuildTarget::MacOsX => LC_VERSION_MIN_MACOSX,
BuildTarget::IPhoneOs => LC_VERSION_MIN_IPHONEOS,
BuildTarget::WatchOs => LC_VERSION_MIN_WATCHOS,
BuildTarget::TvOs => LC_VERSION_MIN_TVOS,
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct LcString(pub usize, pub String);
impl LcString {
pub fn size(&self) -> usize {
self.0
}
pub fn as_str(&self) -> &str {
self.1.as_str()
}
}
impl fmt::Display for LcString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.1)
}
}
impl Deref for LcString {
type Target = str;
fn deref(&self) -> &Self::Target {
self.1.as_str()
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct FvmLib {
pub name: LcString,
pub minor_version: u32,
pub header_addr: u32,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct DyLib {
pub name: LcString,
pub timestamp: u32,
pub current_version: VersionTag,
pub compatibility_version: VersionTag,
}
pub struct DyLibTocEntry {
pub symbol_index: u32,
pub module_index: u32,
}
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: usize,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct LinkEditData {
pub off: u32,
pub size: u32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum X86ThreadFlavor {
x86_THREAD_STATE32 = 1,
x86_FLOAT_STATE32 = 2,
x86_EXCEPTION_STATE32 = 3,
x86_THREAD_STATE64 = 4,
x86_FLOAT_STATE64 = 5,
x86_EXCEPTION_STATE64 = 6,
x86_THREAD_STATE = 7,
x86_FLOAT_STATE = 8,
x86_EXCEPTION_STATE = 9,
x86_DEBUG_STATE32 = 10,
x86_DEBUG_STATE64 = 11,
x86_DEBUG_STATE = 12,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ARMThreadFlavors {
ARM_THREAD_STATE = 1,
ARM_VFP_STATE = 2,
ARM_EXCEPTION_STATE = 3,
ARM_DEBUG_STATE = 4,
ARN_THREAD_STATE_NONE = 5,
ARM_THREAD_STATE64 = 6,
ARM_EXCEPTION_STATE64 = 7,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PPCThreadFlavors {
PPC_THREAD_STATE = 1,
PPC_FLOAT_STATE = 2,
PPC_EXCEPTION_STATE = 3,
PPC_VECTOR_STATE = 4,
PPC_THREAD_STATE64 = 5,
PPC_EXCEPTION_STATE64 = 6,
PPC_THREAD_STATE_NONE = 7,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ThreadState {
I386 {
__eax: u32,
__ebx: u32,
__ecx: u32,
__edx: u32,
__edi: u32,
__esi: u32,
__ebp: u32,
__esp: u32,
__ss: u32,
__eflags: u32,
__eip: u32,
__cs: u32,
__ds: u32,
__es: u32,
__fs: u32,
__gs: u32,
},
X86_64 {
__rax: u64,
__rbx: u64,
__rcx: u64,
__rdx: u64,
__rdi: u64,
__rsi: u64,
__rbp: u64,
__rsp: u64,
__r8: u64,
__r9: u64,
__r10: u64,
__r11: u64,
__r12: u64,
__r13: u64,
__r14: u64,
__r15: u64,
__rip: u64,
__rflags: u64,
__cs: u64,
__fs: u64,
__gs: u64,
},
Arm {
__r: [u32; 13],
__sp: u32,
__lr: u32,
__pc: u32,
__cpsr: u32,
},
Arm64 {
__x: [u64; 29],
__fp: u64,
__lr: u64,
__sp: u64,
__pc: u64,
__cpsr: u32,
__pad: u32,
},
PowerPC {
__srr: [u32; 2],
__r: [u32; 32],
__ct: u32,
__xer: u32,
__lr: u32,
__ctr: u32,
__mq: u32,
__vrsave: u32,
},
PowerPC64 {
__srr: [u64; 2],
__r: [u64; 32],
__cr: u32,
__xer: u64,
__lr: u64,
__ctr: u64,
__vrsave: u32,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum Platform {
macOS,
iOS,
tvOS,
watchOS,
bridgeOS,
Other(u32),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Tool {
Clang,
Swift,
LD,
Other(u32),
}
#[derive(Debug, Clone, PartialEq)]
pub struct BuildVersion {
pub platform: u32,
pub minos: VersionTag,
pub sdk: VersionTag,
pub build_tools: Vec<BuildTool>,
}
impl BuildVersion {
pub fn platform(&self) -> Platform {
match self.platform {
PLATFORM_MACOS => Platform::macOS,
PLATFORM_IOS => Platform::iOS,
PLATFORM_TVOS => Platform::tvOS,
PLATFORM_WATCHOS => Platform::watchOS,
PLATFORM_BRIDGEOS => Platform::bridgeOS,
n => Platform::Other(n),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BuildTool {
pub tool: u32,
pub version: VersionTag,
}
impl BuildTool {
pub fn tool(&self) -> Tool {
match self.tool {
TOOL_CLANG => Tool::Clang,
TOOL_SWIFT => Tool::Swift,
TOOL_LD => Tool::LD,
n => Tool::Other(n),
}
}
}
#[derive(Debug, Clone)]
pub enum LoadCommand {
Segment {
segname: String,
vmaddr: usize,
vmsize: usize,
fileoff: usize,
filesize: usize,
maxprot: vm_prot_t,
initprot: vm_prot_t,
flags: SegmentFlags,
sections: Vec<Rc<Section>>,
},
Segment64 {
segname: String,
vmaddr: usize,
vmsize: usize,
fileoff: usize,
filesize: usize,
maxprot: vm_prot_t,
initprot: vm_prot_t,
flags: SegmentFlags,
sections: Vec<Rc<Section>>,
},
IdFvmLib(FvmLib),
LoadFvmLib(FvmLib),
IdDyLib(DyLib),
LoadDyLib(DyLib),
LoadWeakDyLib(DyLib),
ReexportDyLib(DyLib),
LoadUpwardDylib(DyLib),
LazyLoadDylib(DyLib),
Rpath(String),
IdDyLinker(LcString),
LoadDyLinker(LcString),
DyLdEnv(LcString),
SymTab {
symoff: u32,
nsyms: u32,
stroff: u32,
strsize: u32,
},
DySymTab {
ilocalsym: u32,
nlocalsym: u32,
iextdefsym: u32,
nextdefsym: u32,
iundefsym: u32,
nundefsym: u32,
tocoff: u32,
ntoc: u32,
modtaboff: u32,
nmodtab: u32,
extrefsymoff: u32,
nextrefsyms: u32,
indirectsymoff: u32,
nindirectsyms: u32,
extreloff: u32,
nextrel: u32,
locreloff: u32,
nlocrel: u32,
},
Uuid(Uuid),
CodeSignature(LinkEditData),
SegmentSplitInfo(LinkEditData),
FunctionStarts(LinkEditData),
DataInCode(LinkEditData),
DylibCodeSignDrs(LinkEditData),
LinkerOptimizationHint(LinkEditData),
DyldExportsTrie(LinkEditData),
DyldChainedFixups(LinkEditData),
VersionMin {
target: BuildTarget,
version: VersionTag,
sdk: VersionTag,
},
DyldInfo {
rebase_off: u32,
rebase_size: u32,
bind_off: u32,
bind_size: u32,
weak_bind_off: u32,
weak_bind_size: u32,
lazy_bind_off: u32,
lazy_bind_size: u32,
export_off: u32,
export_size: u32,
},
EntryPoint {
entryoff: u64,
stacksize: u64,
},
SourceVersion(SourceVersionTag),
UnixThread {
flavor: u32,
count: u32,
state: ThreadState,
},
SubFramework(LcString),
SubUmbrella(LcString),
SubClient(LcString),
SubLibrary(LcString),
LinkerOption(Vec<String>),
Routines {
init_address: u32,
init_module: u32,
},
Routines64 {
init_address: u64,
init_module: u64,
},
EncryptionInfo {
offset: u32,
size: u32,
id: u32,
},
EncryptionInfo64 {
offset: u32,
size: u32,
id: u32,
},
TwoLevelHints {
offset: u32,
nhints: u32,
},
BuildVersion(BuildVersion),
Command {
cmd: u32,
payload: Vec<u8>,
},
}
pub trait ReadStringExt: Read {
fn read_fixed_size_string(&mut self, len: usize) -> Result<String> {
let mut buf = vec![0u8; len];
self.read_exact(&mut buf)?;
Ok(String::from_utf8(buf.split(|&b| b == 0).next().unwrap().to_vec())?)
}
}
impl<R: Read + ?Sized> ReadStringExt for R {}
const LOAD_COMMAND_HEADER_SIZE: usize = 8;
impl LoadCommand {
pub fn parse<O: ByteOrder, T: AsRef<[u8]>>(
header: &MachHeader,
buf: &mut Cursor<T>,
) -> Result<(LoadCommand, usize)> {
let begin = buf.position();
let cmd = buf.read_u32::<O>()?;
let cmdsize = buf.read_u32::<O>()? as usize;
if cmdsize < LOAD_COMMAND_HEADER_SIZE || begin as usize + cmdsize > buf.get_ref().as_ref().len() {
return Err(BufferOverflow(cmdsize));
}
let cmd = match cmd {
LC_SEGMENT => {
let segname = buf.read_fixed_size_string(16)?;
let vmaddr = buf.read_u32::<O>()? as usize;
let vmsize = buf.read_u32::<O>()? as usize;
let fileoff = buf.read_u32::<O>()? as usize;
let filesize = buf.read_u32::<O>()? as usize;
let maxprot = buf.read_i32::<O>()?;
let initprot = buf.read_i32::<O>()?;
let nsects = buf.read_u32::<O>()?;
let flags = buf.read_u32::<O>()?;
let mut sections = Vec::new();
for _ in 0..nsects {
sections.push(Rc::new(Section::parse_section::<Cursor<T>, O>(buf)?));
}
LoadCommand::Segment {
segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags: SegmentFlags::from_bits_truncate(flags),
sections,
}
}
LC_SEGMENT_64 => {
let segname = buf.read_fixed_size_string(16)?;
let vmaddr = buf.read_u64::<O>()? as usize;
let vmsize = buf.read_u64::<O>()? as usize;
let fileoff = buf.read_u64::<O>()? as usize;
let filesize = buf.read_u64::<O>()? as usize;
let maxprot = buf.read_i32::<O>()?;
let initprot = buf.read_i32::<O>()?;
let nsects = buf.read_u32::<O>()?;
let flags = buf.read_u32::<O>()?;
let mut sections = Vec::new();
for _ in 0..nsects {
sections.push(Rc::new(Section::parse_section64::<Cursor<T>, O>(buf)?));
}
LoadCommand::Segment64 {
segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags: SegmentFlags::from_bits_truncate(flags),
sections,
}
}
LC_IDFVMLIB => LoadCommand::IdFvmLib(Self::read_fvmlib::<O, T>(buf)?),
LC_LOADFVMLIB => LoadCommand::LoadFvmLib(Self::read_fvmlib::<O, T>(buf)?),
LC_ID_DYLIB => LoadCommand::IdDyLib(Self::read_dylib::<O, T>(buf)?),
LC_LOAD_DYLIB => LoadCommand::LoadDyLib(Self::read_dylib::<O, T>(buf)?),
LC_LOAD_WEAK_DYLIB => LoadCommand::LoadWeakDyLib(Self::read_dylib::<O, T>(buf)?),
LC_REEXPORT_DYLIB => LoadCommand::ReexportDyLib(Self::read_dylib::<O, T>(buf)?),
LC_LOAD_UPWARD_DYLIB => LoadCommand::LoadUpwardDylib(Self::read_dylib::<O, T>(buf)?),
LC_LAZY_LOAD_DYLIB => LoadCommand::LazyLoadDylib(Self::read_dylib::<O, T>(buf)?),
LC_RPATH => LoadCommand::Rpath({
let offset = buf.read_u32::<O>()? as usize;
buf.read_fixed_size_string(cmdsize - offset)?
}),
LC_ID_DYLINKER => LoadCommand::IdDyLinker(Self::read_lcstr::<O, T>(buf)?),
LC_LOAD_DYLINKER => LoadCommand::LoadDyLinker(Self::read_lcstr::<O, T>(buf)?),
LC_DYLD_ENVIRONMENT => LoadCommand::DyLdEnv(Self::read_lcstr::<O, T>(buf)?),
LC_SYMTAB => LoadCommand::SymTab {
symoff: buf.read_u32::<O>()?,
nsyms: buf.read_u32::<O>()?,
stroff: buf.read_u32::<O>()?,
strsize: buf.read_u32::<O>()?,
},
LC_DYSYMTAB => LoadCommand::DySymTab {
ilocalsym: buf.read_u32::<O>()?,
nlocalsym: buf.read_u32::<O>()?,
iextdefsym: buf.read_u32::<O>()?,
nextdefsym: buf.read_u32::<O>()?,
iundefsym: buf.read_u32::<O>()?,
nundefsym: buf.read_u32::<O>()?,
tocoff: buf.read_u32::<O>()?,
ntoc: buf.read_u32::<O>()?,
modtaboff: buf.read_u32::<O>()?,
nmodtab: buf.read_u32::<O>()?,
extrefsymoff: buf.read_u32::<O>()?,
nextrefsyms: buf.read_u32::<O>()?,
indirectsymoff: buf.read_u32::<O>()?,
nindirectsyms: buf.read_u32::<O>()?,
extreloff: buf.read_u32::<O>()?,
nextrel: buf.read_u32::<O>()?,
locreloff: buf.read_u32::<O>()?,
nlocrel: buf.read_u32::<O>()?,
},
LC_UUID => {
let mut uuid = [0; 16];
buf.read_exact(&mut uuid[..])?;
LoadCommand::Uuid(Uuid::from_slice(&uuid)?)
}
LC_CODE_SIGNATURE => LoadCommand::CodeSignature(Self::read_linkedit_data::<O, T>(buf)?),
LC_SEGMENT_SPLIT_INFO => LoadCommand::SegmentSplitInfo(Self::read_linkedit_data::<O, T>(buf)?),
LC_FUNCTION_STARTS => LoadCommand::FunctionStarts(Self::read_linkedit_data::<O, T>(buf)?),
LC_DATA_IN_CODE => LoadCommand::DataInCode(Self::read_linkedit_data::<O, T>(buf)?),
LC_DYLIB_CODE_SIGN_DRS => LoadCommand::DylibCodeSignDrs(Self::read_linkedit_data::<O, T>(buf)?),
LC_LINKER_OPTIMIZATION_HINT => LoadCommand::LinkerOptimizationHint(Self::read_linkedit_data::<O, T>(buf)?),
LC_DYLD_EXPORTS_TRIE => LoadCommand::DyldExportsTrie(Self::read_linkedit_data::<O, T>(buf)?),
LC_DYLD_CHAINED_FIXUPS => LoadCommand::DyldChainedFixups(Self::read_linkedit_data::<O, T>(buf)?),
LC_VERSION_MIN_MACOSX | LC_VERSION_MIN_IPHONEOS | LC_VERSION_MIN_WATCHOS | LC_VERSION_MIN_TVOS => {
LoadCommand::VersionMin {
target: BuildTarget::from(cmd),
version: VersionTag(buf.read_u32::<O>()?),
sdk: VersionTag(buf.read_u32::<O>()?),
}
}
LC_DYLD_INFO | LC_DYLD_INFO_ONLY => LoadCommand::DyldInfo {
rebase_off: buf.read_u32::<O>()?,
rebase_size: buf.read_u32::<O>()?,
bind_off: buf.read_u32::<O>()?,
bind_size: buf.read_u32::<O>()?,
weak_bind_off: buf.read_u32::<O>()?,
weak_bind_size: buf.read_u32::<O>()?,
lazy_bind_off: buf.read_u32::<O>()?,
lazy_bind_size: buf.read_u32::<O>()?,
export_off: buf.read_u32::<O>()?,
export_size: buf.read_u32::<O>()?,
},
LC_MAIN => LoadCommand::EntryPoint {
entryoff: buf.read_u64::<O>()?,
stacksize: buf.read_u64::<O>()?,
},
LC_SOURCE_VERSION => LoadCommand::SourceVersion(SourceVersionTag(buf.read_u64::<O>()?)),
LC_UNIXTHREAD if header.cputype == CPU_TYPE_I386 => LoadCommand::UnixThread {
flavor: buf.read_u32::<O>()?,
count: buf.read_u32::<O>()?,
state: ThreadState::I386 {
__eax: buf.read_u32::<O>()?,
__ebx: buf.read_u32::<O>()?,
__ecx: buf.read_u32::<O>()?,
__edx: buf.read_u32::<O>()?,
__edi: buf.read_u32::<O>()?,
__esi: buf.read_u32::<O>()?,
__ebp: buf.read_u32::<O>()?,
__esp: buf.read_u32::<O>()?,
__ss: buf.read_u32::<O>()?,
__eflags: buf.read_u32::<O>()?,
__eip: buf.read_u32::<O>()?,
__cs: buf.read_u32::<O>()?,
__ds: buf.read_u32::<O>()?,
__es: buf.read_u32::<O>()?,
__fs: buf.read_u32::<O>()?,
__gs: buf.read_u32::<O>()?,
},
},
LC_UNIXTHREAD if header.cputype == CPU_TYPE_X86_64 => LoadCommand::UnixThread {
flavor: buf.read_u32::<O>()?,
count: buf.read_u32::<O>()?,
state: ThreadState::X86_64 {
__rax: buf.read_u64::<O>()?,
__rbx: buf.read_u64::<O>()?,
__rcx: buf.read_u64::<O>()?,
__rdx: buf.read_u64::<O>()?,
__rdi: buf.read_u64::<O>()?,
__rsi: buf.read_u64::<O>()?,
__rbp: buf.read_u64::<O>()?,
__rsp: buf.read_u64::<O>()?,
__r8: buf.read_u64::<O>()?,
__r9: buf.read_u64::<O>()?,
__r10: buf.read_u64::<O>()?,
__r11: buf.read_u64::<O>()?,
__r12: buf.read_u64::<O>()?,
__r13: buf.read_u64::<O>()?,
__r14: buf.read_u64::<O>()?,
__r15: buf.read_u64::<O>()?,
__rip: buf.read_u64::<O>()?,
__rflags: buf.read_u64::<O>()?,
__cs: buf.read_u64::<O>()?,
__fs: buf.read_u64::<O>()?,
__gs: buf.read_u64::<O>()?,
},
},
LC_UNIXTHREAD if header.cputype == CPU_TYPE_ARM => {
let flavor = buf.read_u32::<O>()?;
let count = buf.read_u32::<O>()?;
let mut __r = [0u32; 13];
buf.read_u32_into::<O>(&mut __r)?;
LoadCommand::UnixThread {
flavor,
count,
state: ThreadState::Arm {
__r,
__sp: buf.read_u32::<O>()?,
__lr: buf.read_u32::<O>()?,
__pc: buf.read_u32::<O>()?,
__cpsr: buf.read_u32::<O>()?,
},
}
}
LC_UNIXTHREAD if header.cputype == CPU_TYPE_ARM64 => {
let flavor = buf.read_u32::<O>()?;
let count = buf.read_u32::<O>()?;
let mut __x = [0u64; 29];
buf.read_u64_into::<O>(&mut __x)?;
LoadCommand::UnixThread {
flavor,
count,
state: ThreadState::Arm64 {
__x,
__fp: buf.read_u64::<O>()?,
__lr: buf.read_u64::<O>()?,
__sp: buf.read_u64::<O>()?,
__pc: buf.read_u64::<O>()?,
__cpsr: buf.read_u32::<O>()?,
__pad: buf.read_u32::<O>()?,
},
}
}
LC_UNIXTHREAD if header.cputype == CPU_TYPE_POWERPC => {
let flavor = buf.read_u32::<O>()?;
let count = buf.read_u32::<O>()?;
let mut __srr = [0u32; 2];
let mut __r = [0u32; 32];
buf.read_u32_into::<O>(&mut __srr)?;
buf.read_u32_into::<O>(&mut __r)?;
LoadCommand::UnixThread {
flavor,
count,
state: ThreadState::PowerPC {
__srr,
__r,
__ct: buf.read_u32::<O>()?,
__xer: buf.read_u32::<O>()?,
__lr: buf.read_u32::<O>()?,
__ctr: buf.read_u32::<O>()?,
__mq: buf.read_u32::<O>()?,
__vrsave: buf.read_u32::<O>()?,
},
}
}
LC_UNIXTHREAD if header.cputype == CPU_TYPE_POWERPC64 => {
let flavor = buf.read_u32::<O>()?;
let count = buf.read_u32::<O>()?;
let mut __srr = [0u64; 2];
let mut __r = [0u64; 32];
buf.read_u64_into::<O>(&mut __srr)?;
buf.read_u64_into::<O>(&mut __r)?;
LoadCommand::UnixThread {
flavor,
count,
state: ThreadState::PowerPC64 {
__srr,
__r,
__cr: buf.read_u32::<O>()?,
__xer: buf.read_u64::<O>()?,
__lr: buf.read_u64::<O>()?,
__ctr: buf.read_u64::<O>()?,
__vrsave: buf.read_u32::<O>()?,
},
}
}
LC_SUB_FRAMEWORK => LoadCommand::SubFramework(Self::read_lcstr::<O, T>(buf)?),
LC_SUB_UMBRELLA => LoadCommand::SubUmbrella(Self::read_lcstr::<O, T>(buf)?),
LC_SUB_CLIENT => LoadCommand::SubClient(Self::read_lcstr::<O, T>(buf)?),
LC_SUB_LIBRARY => LoadCommand::SubLibrary(Self::read_lcstr::<O, T>(buf)?),
LC_LINKER_OPTION => {
let count = buf.read_u32::<O>()?;
LoadCommand::LinkerOption(
(0..count)
.map(|_| Self::read_utf8_str(buf))
.collect::<Result<Vec<_>>>()?,
)
}
LC_ROUTINES => LoadCommand::Routines {
init_address: buf.read_u32::<O>()?,
init_module: buf.read_u32::<O>()?,
},
LC_ROUTINES_64 => LoadCommand::Routines64 {
init_address: buf.read_u64::<O>()?,
init_module: buf.read_u64::<O>()?,
},
LC_ENCRYPTION_INFO => LoadCommand::EncryptionInfo {
offset: buf.read_u32::<O>()?,
size: buf.read_u32::<O>()?,
id: buf.read_u32::<O>()?,
},
LC_ENCRYPTION_INFO_64 => LoadCommand::EncryptionInfo64 {
offset: buf.read_u32::<O>()?,
size: buf.read_u32::<O>()?,
id: buf.read_u32::<O>()?,
},
LC_TWOLEVEL_HINTS => LoadCommand::TwoLevelHints {
offset: buf.read_u32::<O>()?,
nhints: buf.read_u32::<O>()?,
},
LC_BUILD_VERSION => LoadCommand::BuildVersion(BuildVersion {
platform: buf.read_u32::<O>()?,
minos: VersionTag(buf.read_u32::<O>()?),
sdk: VersionTag(buf.read_u32::<O>()?),
build_tools: (0..buf.read_u32::<O>()?)
.map(|_| {
let tool = buf.read_u32::<O>()?;
let version = VersionTag(buf.read_u32::<O>()?);
Ok(BuildTool { tool, version })
})
.collect::<Result<Vec<_>>>()?,
}),
_ => {
let mut payload = vec![0; cmdsize as usize - LOAD_COMMAND_HEADER_SIZE];
debug!(
"load unsupported {} command with {} bytes payload",
LoadCommand::cmd_name(cmd),
payload.len()
);
buf.read_exact(&mut payload)?;
LoadCommand::Command { cmd, payload }
}
};
let read = (buf.position() - begin) as usize;
debug!(
"parsed {} command with {}/{} bytes: {:?}",
cmd.name(),
read,
cmdsize,
cmd
);
if cmdsize < read {
return Err(BufferOverflow(cmdsize));
}
if cmdsize > read {
buf.consume(cmdsize - read);
}
Ok((cmd, cmdsize))
}
fn read_lcstr<O: ByteOrder, T: AsRef<[u8]>>(buf: &mut Cursor<T>) -> Result<LcString> {
let off = buf.read_u32::<O>()? as usize;
buf.consume(off - 12);
Ok(LcString(off, buf.read_cstr()?))
}
fn read_utf8_str<T: AsRef<[u8]>>(cur: &mut Cursor<T>) -> Result<String> {
let mut buf = vec![];
cur.read_until(0, &mut buf)?;
if buf.last() == Some(&0) {
let _ = buf.pop();
}
Ok(String::from_utf8(buf)?)
}
fn read_fvmlib<O: ByteOrder, T: AsRef<[u8]>>(buf: &mut Cursor<T>) -> Result<FvmLib> {
let off = buf.read_u32::<O>()? as usize;
let minor_version = buf.read_u32::<O>()?;
let header_addr = buf.read_u32::<O>()?;
buf.consume(off - 20);
Ok(FvmLib {
name: LcString(off, buf.read_cstr()?),
minor_version,
header_addr,
})
}
fn read_dylib<O: ByteOrder, T: AsRef<[u8]>>(buf: &mut Cursor<T>) -> Result<DyLib> {
let off = buf.read_u32::<O>()? as usize;
let timestamp = buf.read_u32::<O>()?;
let current_version = buf.read_u32::<O>()?;
let compatibility_version = buf.read_u32::<O>()?;
buf.consume(off - 24);
Ok(DyLib {
name: LcString(off, buf.read_cstr()?),
timestamp,
current_version: VersionTag(current_version),
compatibility_version: VersionTag(compatibility_version),
})
}
fn read_linkedit_data<O: ByteOrder, T: AsRef<[u8]>>(buf: &mut Cursor<T>) -> Result<LinkEditData> {
Ok(LinkEditData {
off: buf.read_u32::<O>()?,
size: buf.read_u32::<O>()?,
})
}
pub fn cmd(&self) -> u32 {
match *self {
LoadCommand::Segment { .. } => LC_SEGMENT,
LoadCommand::Segment64 { .. } => LC_SEGMENT_64,
LoadCommand::IdFvmLib(_) => LC_IDFVMLIB,
LoadCommand::LoadFvmLib(_) => LC_LOADFVMLIB,
LoadCommand::IdDyLib(_) => LC_ID_DYLIB,
LoadCommand::LoadDyLib(_) => LC_LOAD_DYLIB,
LoadCommand::LoadWeakDyLib(_) => LC_LOAD_WEAK_DYLIB,
LoadCommand::ReexportDyLib(_) => LC_REEXPORT_DYLIB,
LoadCommand::LoadUpwardDylib(_) => LC_LOAD_UPWARD_DYLIB,
LoadCommand::LazyLoadDylib(_) => LC_LAZY_LOAD_DYLIB,
LoadCommand::Rpath(_) => LC_RPATH,
LoadCommand::IdDyLinker(_) => LC_ID_DYLINKER,
LoadCommand::LoadDyLinker(_) => LC_LOAD_DYLINKER,
LoadCommand::DyLdEnv(_) => LC_DYLD_ENVIRONMENT,
LoadCommand::SymTab { .. } => LC_SYMTAB,
LoadCommand::DySymTab { .. } => LC_DYSYMTAB,
LoadCommand::Uuid(_) => LC_UUID,
LoadCommand::CodeSignature(_) => LC_CODE_SIGNATURE,
LoadCommand::SegmentSplitInfo(_) => LC_SEGMENT_SPLIT_INFO,
LoadCommand::FunctionStarts(_) => LC_FUNCTION_STARTS,
LoadCommand::DataInCode(_) => LC_DATA_IN_CODE,
LoadCommand::DylibCodeSignDrs(_) => LC_DYLIB_CODE_SIGN_DRS,
LoadCommand::LinkerOptimizationHint(_) => LC_LINKER_OPTIMIZATION_HINT,
LoadCommand::VersionMin { target, .. } => BuildTarget::into(target),
LoadCommand::DyldInfo { .. } => LC_DYLD_INFO_ONLY,
LoadCommand::EntryPoint { .. } => LC_MAIN,
LoadCommand::SourceVersion(_) => LC_SOURCE_VERSION,
LoadCommand::UnixThread { .. } => LC_UNIXTHREAD,
LoadCommand::SubFramework(_) => LC_SUB_FRAMEWORK,
LoadCommand::SubUmbrella(_) => LC_SUB_UMBRELLA,
LoadCommand::SubClient(_) => LC_SUB_CLIENT,
LoadCommand::SubLibrary(_) => LC_SUB_LIBRARY,
LoadCommand::LinkerOption(_) => LC_LINKER_OPTION,
LoadCommand::Routines { .. } => LC_ROUTINES,
LoadCommand::Routines64 { .. } => LC_ROUTINES_64,
LoadCommand::EncryptionInfo { .. } => LC_ENCRYPTION_INFO,
LoadCommand::EncryptionInfo64 { .. } => LC_ENCRYPTION_INFO_64,
LoadCommand::TwoLevelHints { .. } => LC_TWOLEVEL_HINTS,
LoadCommand::BuildVersion { .. } => LC_BUILD_VERSION,
LoadCommand::DyldExportsTrie { .. } => LC_DYLD_EXPORTS_TRIE,
LoadCommand::DyldChainedFixups { .. } => LC_DYLD_CHAINED_FIXUPS,
LoadCommand::Command { cmd, .. } => cmd,
}
}
pub fn name(&self) -> &'static str {
Self::cmd_name(self.cmd())
}
fn cmd_name(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_COMMAND",
}
}
}
pub trait CursorExt<T: AsRef<[u8]>> {
fn read_uleb128(&mut self) -> Result<usize>;
fn read_cstr(&mut self) -> Result<String>;
}
impl<T> CursorExt<T> for Cursor<T>
where
T: AsRef<[u8]>,
{
fn read_uleb128(&mut self) -> Result<usize> {
let mut v = 0;
let mut bits = 0;
loop {
let b = self.read_u8()?;
let n = usize::from(b & 0x7F);
if bits > 63 {
return Err(NumberOverflow);
}
v |= n << bits;
bits += 7;
if (b & 0x80) == 0 {
break;
}
}
Ok(v)
}
fn read_cstr(&mut self) -> Result<String> {
let mut v = Vec::new();
self.read_until(0, &mut v)?;
Ok(String::from_utf8(v.split(|&b| b == 0).next().unwrap().to_vec())?)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SectionFlags(u32);
impl SectionFlags {
pub fn sect_type(self) -> u32 {
self.0 & SECTION_TYPE
}
pub fn sect_attrs(self) -> SectionAttributes {
SectionAttributes::from_bits_truncate(self.0 & SECTION_ATTRIBUTES)
}
}
impl From<SectionFlags> for u32 {
fn from(f: SectionFlags) -> u32 {
f.0
}
}
#[derive(Debug, Clone)]
pub struct Section {
pub sectname: String,
pub segname: String,
pub addr: usize,
pub size: usize,
pub offset: u32,
pub align: u32,
pub reloff: u32,
pub nreloc: u32,
pub flags: SectionFlags,
pub reserved1: u32,
pub reserved2: u32,
pub reserved3: u32,
}
impl Section {
fn parse_section<T: BufRead, O: ByteOrder>(buf: &mut T) -> Result<Section> {
let section = Section {
sectname: buf.read_fixed_size_string(16)?,
segname: buf.read_fixed_size_string(16)?,
addr: buf.read_u32::<O>()? as usize,
size: buf.read_u32::<O>()? as usize,
offset: buf.read_u32::<O>()?,
align: buf.read_u32::<O>()?,
reloff: buf.read_u32::<O>()?,
nreloc: buf.read_u32::<O>()?,
flags: SectionFlags(buf.read_u32::<O>()?),
reserved1: buf.read_u32::<O>()?,
reserved2: buf.read_u32::<O>()?,
reserved3: 0,
};
Ok(section)
}
fn parse_section64<T: BufRead, O: ByteOrder>(buf: &mut T) -> Result<Section> {
let section = Section {
sectname: buf.read_fixed_size_string(16)?,
segname: buf.read_fixed_size_string(16)?,
addr: buf.read_u64::<O>()? as usize,
size: buf.read_u64::<O>()? as usize,
offset: buf.read_u32::<O>()?,
align: buf.read_u32::<O>()?,
reloff: buf.read_u32::<O>()?,
nreloc: buf.read_u32::<O>()?,
flags: SectionFlags(buf.read_u32::<O>()?),
reserved1: buf.read_u32::<O>()?,
reserved2: buf.read_u32::<O>()?,
reserved3: buf.read_u32::<O>()?,
};
Ok(section)
}
}
pub struct DataInCodeEntry {
pub offset: u32, pub length: u16, pub kind: u16, }
#[cfg(test)]
pub mod tests {
use std::io::Cursor;
use byteorder::LittleEndian;
use super::super::*;
include!("testdata.rs");
macro_rules! parse_command {
($buf:expr) => {{
let header = MachHeader::default();
let mut buf = Vec::new();
buf.extend_from_slice(&$buf[..]);
let mut cur = Cursor::new(buf);
LoadCommand::parse::<LittleEndian, Vec<u8>>(&header, &mut cur).unwrap()
}};
}
#[test]
fn test_parse_segments() {
if let (
LoadCommand::Segment64 {
ref segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags,
ref sections,
},
cmdsize,
) = parse_command!(LC_SEGMENT_64_PAGEZERO_DATA)
{
assert_eq!(cmdsize, 72);
assert_eq!(segname, SEG_PAGEZERO);
assert_eq!(vmaddr, 0);
assert_eq!(vmsize, 0x0000000100000000);
assert_eq!(fileoff, 0);
assert_eq!(filesize, 0);
assert_eq!(maxprot, 0);
assert_eq!(initprot, 0);
assert!(flags.is_empty());
assert!(sections.is_empty());
} else {
panic!();
}
if let (
LoadCommand::Segment64 {
ref segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags,
ref sections,
},
cmdsize,
) = parse_command!(LC_SEGMENT_64_TEXT_DATA)
{
assert_eq!(cmdsize, 712);
assert_eq!(segname, SEG_TEXT);
assert_eq!(vmaddr, 0x0000000100000000);
assert_eq!(vmsize, 0x00000000001e3000);
assert_eq!(fileoff, 0);
assert_eq!(filesize, 0x1e3000);
assert_eq!(maxprot, 7);
assert_eq!(initprot, 5);
assert!(flags.is_empty());
assert_eq!(sections.len(), 8);
assert_eq!(
sections
.iter()
.map(|ref sec| (*sec).sectname.clone())
.collect::<Vec<String>>(),
vec![
SECT_TEXT,
"__stubs",
"__stub_helper",
"__gcc_except_tab",
"__const",
"__cstring",
"__unwind_info",
"__eh_frame",
]
);
} else {
panic!();
}
if let (
LoadCommand::Segment64 {
ref segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags,
ref sections,
},
cmdsize,
) = parse_command!(LC_SEGMENT_64_DATA_DATA)
{
assert_eq!(cmdsize, 872);
assert_eq!(segname, SEG_DATA);
assert_eq!(vmaddr, 0x00000001001e3000);
assert_eq!(vmsize, 0x0000000000013000);
assert_eq!(fileoff, 0x1e3000);
assert_eq!(filesize, 0x12000);
assert_eq!(maxprot, 7);
assert_eq!(initprot, 3);
assert!(flags.is_empty());
assert_eq!(sections.len(), 10);
assert_eq!(
sections
.iter()
.map(|ref sec| (*sec).sectname.clone())
.collect::<Vec<String>>(),
vec![
"__nl_symbol_ptr",
"__got",
"__la_symbol_ptr",
"__mod_init_func",
"__const",
SECT_DATA,
"__thread_vars",
"__thread_data",
SECT_COMMON,
SECT_BSS,
]
);
} else {
panic!();
}
if let (
LoadCommand::Segment64 {
ref segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
flags,
ref sections,
},
cmdsize,
) = parse_command!(LC_SEGMENT_64_LINKEDIT_DATA)
{
assert_eq!(cmdsize, 72);
assert_eq!(segname, SEG_LINKEDIT);
assert_eq!(vmaddr, 0x00000001001f6000);
assert_eq!(vmsize, 0x000000000017a000);
assert_eq!(fileoff, 0x1f5000);
assert_eq!(filesize, 0x1790b4);
assert_eq!(maxprot, 7);
assert_eq!(initprot, 1);
assert!(flags.is_empty());
assert!(sections.is_empty());
} else {
panic!();
}
}
#[test]
fn test_parse_dyld_info_command() {
if let (
LoadCommand::DyldInfo {
rebase_off,
rebase_size,
bind_off,
bind_size,
weak_bind_off,
weak_bind_size,
lazy_bind_off,
lazy_bind_size,
export_off,
export_size,
},
cmdsize,
) = parse_command!(LC_DYLD_INFO_ONLY_DATA)
{
assert_eq!(cmdsize, 48);
assert_eq!(rebase_off, 0x1f5000);
assert_eq!(rebase_size, 3368);
assert_eq!(bind_off, 0x1f5d28);
assert_eq!(bind_size, 80);
assert_eq!(weak_bind_off, 0x1f5d78);
assert_eq!(weak_bind_size, 24);
assert_eq!(lazy_bind_off, 0x1f5d90);
assert_eq!(lazy_bind_size, 1688);
assert_eq!(export_off, 0x1f6428);
assert_eq!(export_size, 34856);
} else {
panic!();
}
}
#[test]
fn test_parse_symtab_command() {
if let (
LoadCommand::SymTab {
symoff,
nsyms,
stroff,
strsize,
},
cmdsize,
) = parse_command!(LC_SYMTAB_DATA)
{
assert_eq!(cmdsize, 24);
assert_eq!(symoff, 0x200d88);
assert_eq!(nsyms, 36797);
assert_eq!(stroff, 0x290bf4);
assert_eq!(strsize, 906432);
} else {
panic!();
}
}
#[test]
fn test_parse_dysymtab_command() {
if let (
LoadCommand::DySymTab {
ilocalsym,
nlocalsym,
iextdefsym,
nextdefsym,
iundefsym,
nundefsym,
tocoff,
ntoc,
modtaboff,
nmodtab,
extrefsymoff,
nextrefsyms,
indirectsymoff,
nindirectsyms,
extreloff,
nextrel,
locreloff,
nlocrel,
},
cmdsize,
) = parse_command!(LC_DYSYMTAB_DATA)
{
assert_eq!(cmdsize, 80);
assert_eq!(ilocalsym, 0);
assert_eq!(nlocalsym, 35968);
assert_eq!(iextdefsym, 35968);
assert_eq!(nextdefsym, 746);
assert_eq!(iundefsym, 36714);
assert_eq!(nundefsym, 83);
assert_eq!(tocoff, 0);
assert_eq!(ntoc, 0);
assert_eq!(modtaboff, 0);
assert_eq!(nmodtab, 0);
assert_eq!(extrefsymoff, 0);
assert_eq!(nextrefsyms, 0);
assert_eq!(indirectsymoff, 2689368);
assert_eq!(nindirectsyms, 167);
assert_eq!(extreloff, 0);
assert_eq!(nextrel, 0);
assert_eq!(locreloff, 0);
assert_eq!(nlocrel, 0);
} else {
panic!();
}
}
#[test]
fn test_parse_load_dylinker_command() {
if let (LoadCommand::LoadDyLinker(LcString(off, ref name)), cmdsize) = parse_command!(LC_LOAD_DYLINKER_DATA) {
assert_eq!(cmdsize, 32);
assert_eq!(off, 12);
assert_eq!(name, "/usr/lib/dyld");
} else {
panic!();
}
}
#[test]
fn test_parse_uuid_command() {
if let (LoadCommand::Uuid(ref uuid), cmdsize) = parse_command!(LC_UUID_DATA) {
assert_eq!(cmdsize, 24);
assert_eq!(uuid.to_string(), "92e3cf1f-20da-3373-a98c-851366d353bf");
} else {
panic!();
}
}
#[test]
fn test_parse_min_version_command() {
if let (LoadCommand::VersionMin { target, version, sdk }, cmdsize) = parse_command!(LC_VERSION_MIN_MACOSX_DATA)
{
assert_eq!(cmdsize, 16);
assert_eq!(target, BuildTarget::MacOsX);
assert_eq!(version.to_string(), "10.11");
assert_eq!(sdk.to_string(), "10.11");
} else {
panic!();
}
}
#[test]
fn test_parse_source_version_command() {
if let (LoadCommand::SourceVersion(version), cmdsize) = parse_command!(LC_SOURCE_VERSION_DATA) {
assert_eq!(cmdsize, 16);
assert_eq!(version.to_string(), "0.0");
} else {
panic!();
}
}
#[test]
fn test_parse_main_command() {
if let (LoadCommand::EntryPoint { entryoff, stacksize }, cmdsize) = parse_command!(LC_MAIN_DATA) {
assert_eq!(cmdsize, 24);
assert_eq!(entryoff, 0x11400);
assert_eq!(stacksize, 0);
} else {
panic!();
}
}
#[test]
fn test_load_dylib_command() {
if let (LoadCommand::LoadDyLib(ref dylib), cmdsize) = parse_command!(LC_LOAD_DYLIB_DATA) {
assert_eq!(cmdsize, 56);
assert_eq!(dylib.name, LcString(24, String::from("/usr/lib/libSystem.B.dylib")));
assert_eq!(dylib.timestamp, 2);
assert_eq!(dylib.current_version.to_string(), "1226.10.1");
assert_eq!(dylib.compatibility_version.to_string(), "1.0");
} else {
panic!();
}
}
#[test]
fn test_parse_link_edit_data_command() {
if let (LoadCommand::FunctionStarts(LinkEditData { off, size }), cmdsize) =
parse_command!(LC_FUNCTION_STARTS_DATA)
{
assert_eq!(cmdsize, 16);
assert_eq!(off, 0x1fec50);
assert_eq!(size, 8504);
} else {
panic!();
}
if let (LoadCommand::DataInCode(LinkEditData { off, size }), cmdsize) = parse_command!(LC_DATA_IN_CODE_DATA) {
assert_eq!(cmdsize, 16);
assert_eq!(off, 0x200d88);
assert_eq!(size, 0);
} else {
panic!();
}
}
#[test]
fn test_parse_rpath_command() {
if let (LoadCommand::Rpath(path), cmdsize) = parse_command!(LC_RPATH_DATA) {
assert_eq!(cmdsize, 64);
assert_eq!(path, "@executable_path/../../Library/PrivateFrameworks");
} else {
panic!()
}
}
#[cfg(feature = "display")]
#[test]
fn test_build_version_command() {
let (cmd, cmdsize) = parse_command!(LC_BUILD_VERSION);
if let LoadCommand::BuildVersion(ref version) = cmd {
assert_eq!(cmdsize, 32);
assert_eq!(version.platform(), Platform::Other(8));
assert_eq!(version.minos.to_string(), "12.0");
assert_eq!(version.sdk.to_string(), "12.0");
assert_eq!(
version.build_tools,
vec![BuildTool {
tool: 3,
version: "409.10.0".parse().unwrap()
}]
);
} else {
panic!()
}
assert_eq!(
MachCommand(cmd, cmdsize).to_string(),
" cmd LC_BUILD_VERSION
cmdsize 32
platform Other(8)
minos 12.0
sdk 12.0
tools
LD 409.10
"
);
}
}