use core::fmt;
use plain::Plain;
use scroll::ctx;
use scroll::ctx::SizeWith;
use scroll::{Pread, Pwrite, SizeWith};
use crate::container::{self, Container};
use crate::error;
use crate::mach::constants::cputype::{CpuSubType, CpuType, CPU_SUBTYPE_MASK};
pub const MH_NOUNDEFS: u32 = 0x1;
pub const MH_INCRLINK: u32 = 0x2;
pub const MH_DYLDLINK: u32 = 0x4;
pub const MH_BINDATLOAD: u32 = 0x8;
pub const MH_PREBOUND: u32 = 0x10;
pub const MH_SPLIT_SEGS: u32 = 0x20;
pub const MH_LAZY_INIT: u32 = 0x40;
pub const MH_TWOLEVEL: u32 = 0x80;
pub const MH_FORCE_FLAT: u32 = 0x100;
pub const MH_NOMULTIDEFS: u32 = 0x200;
pub const MH_NOFIXPREBINDING: u32 = 0x400;
pub const MH_PREBINDABLE: u32 = 0x800;
pub const MH_ALLMODSBOUND: u32 = 0x1000;
pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000;
pub const MH_CANONICAL: u32 = 0x4000;
pub const MH_WEAK_DEFINES: u32 = 0x8000;
pub const MH_BINDS_TO_WEAK: u32 = 0x10000;
pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000;
pub const MH_ROOT_SAFE: u32 = 0x40000;
pub const MH_SETUID_SAFE: u32 = 0x80000;
pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x0010_0000;
pub const MH_PIE: u32 = 0x0020_0000;
pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x0040_0000;
pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x0080_0000;
pub const MH_NO_HEAP_EXECUTION: u32 = 0x0100_0000;
pub const MH_APP_EXTENSION_SAFE: u32 = 0x0200_0000;
#[inline(always)]
pub fn flag_to_str(flag: u32) -> &'static str {
match flag {
MH_NOUNDEFS => "MH_NOUNDEFS",
MH_INCRLINK => "MH_INCRLINK",
MH_DYLDLINK => "MH_DYLDLINK",
MH_BINDATLOAD => "MH_BINDATLOAD",
MH_PREBOUND => "MH_PREBOUND",
MH_SPLIT_SEGS => "MH_SPLIT_SEGS",
MH_LAZY_INIT => "MH_LAZY_INIT",
MH_TWOLEVEL => "MH_TWOLEVEL",
MH_FORCE_FLAT => "MH_FORCE_FLAT",
MH_NOMULTIDEFS => "MH_NOMULTIDEFS",
MH_NOFIXPREBINDING => "MH_NOFIXPREBINDING",
MH_PREBINDABLE => "MH_PREBINDABLE ",
MH_ALLMODSBOUND => "MH_ALLMODSBOUND",
MH_SUBSECTIONS_VIA_SYMBOLS => "MH_SUBSECTIONS_VIA_SYMBOLS",
MH_CANONICAL => "MH_CANONICAL",
MH_WEAK_DEFINES => "MH_WEAK_DEFINES",
MH_BINDS_TO_WEAK => "MH_BINDS_TO_WEAK",
MH_ALLOW_STACK_EXECUTION => "MH_ALLOW_STACK_EXECUTION",
MH_ROOT_SAFE => "MH_ROOT_SAFE",
MH_SETUID_SAFE => "MH_SETUID_SAFE",
MH_NO_REEXPORTED_DYLIBS => "MH_NO_REEXPORTED_DYLIBS",
MH_PIE => "MH_PIE",
MH_DEAD_STRIPPABLE_DYLIB => "MH_DEAD_STRIPPABLE_DYLIB",
MH_HAS_TLV_DESCRIPTORS => "MH_HAS_TLV_DESCRIPTORS",
MH_NO_HEAP_EXECUTION => "MH_NO_HEAP_EXECUTION",
MH_APP_EXTENSION_SAFE => "MH_APP_EXTENSION_SAFE",
_ => "UNKNOWN FLAG",
}
}
pub const MH_MAGIC: u32 = 0xfeed_face;
pub const MH_CIGAM: u32 = 0xcefa_edfe;
pub const MH_MAGIC_64: u32 = 0xfeed_facf;
pub const MH_CIGAM_64: u32 = 0xcffa_edfe;
pub const MH_OBJECT: u32 = 0x1;
pub const MH_EXECUTE: u32 = 0x2;
pub const MH_FVMLIB: u32 = 0x3;
pub const MH_CORE: u32 = 0x4;
pub const MH_PRELOAD: u32 = 0x5;
pub const MH_DYLIB: u32 = 0x6;
pub const MH_DYLINKER: u32 = 0x7;
pub const MH_BUNDLE: u32 = 0x8;
pub const MH_DYLIB_STUB: u32 = 0x9;
pub const MH_DSYM: u32 = 0xa;
pub const MH_KEXT_BUNDLE: u32 = 0xb;
pub fn filetype_to_str(filetype: u32) -> &'static str {
match filetype {
MH_OBJECT => "OBJECT",
MH_EXECUTE => "EXECUTE",
MH_FVMLIB => "FVMLIB",
MH_CORE => "CORE",
MH_PRELOAD => "PRELOAD",
MH_DYLIB => "DYLIB",
MH_DYLINKER => "DYLINKER",
MH_BUNDLE => "BUNDLE",
MH_DYLIB_STUB => "DYLIB_STUB",
MH_DSYM => "DSYM",
MH_KEXT_BUNDLE => "KEXT_BUNDLE",
_ => "UNKNOWN FILETYPE",
}
}
#[repr(C)]
#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)]
pub struct Header32 {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
}
pub const SIZEOF_HEADER_32: usize = 0x1c;
unsafe impl Plain for Header32 {}
impl Header32 {
pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_32]) -> &Self {
plain::from_bytes(bytes).unwrap()
}
pub fn size(&self) -> usize {
SIZEOF_HEADER_32
}
}
#[repr(C)]
#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)]
pub struct Header64 {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
pub reserved: u32,
}
unsafe impl Plain for Header64 {}
pub const SIZEOF_HEADER_64: usize = 32;
impl Header64 {
pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_64]) -> &Self {
plain::from_bytes(bytes).unwrap()
}
pub fn size(&self) -> usize {
SIZEOF_HEADER_64
}
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct Header {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: usize,
pub sizeofcmds: u32,
pub flags: u32,
pub reserved: u32,
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Header")
.field("magic", &format_args!("0x{:x}", self.magic))
.field("cputype", &self.cputype())
.field("cpusubtype", &format_args!("0x{:x}", self.cpusubtype()))
.field("filetype", &filetype_to_str(self.filetype))
.field("ncmds", &self.ncmds)
.field("sizeofcmds", &self.sizeofcmds)
.field("flags", &format_args!("0x{:x}", self.flags))
.field("reserved", &format_args!("0x{:x}", self.reserved))
.finish()
}
}
impl From<Header32> for Header {
fn from(header: Header32) -> Self {
Header {
magic: header.magic,
cputype: header.cputype,
cpusubtype: header.cpusubtype,
filetype: header.filetype,
ncmds: header.ncmds as usize,
sizeofcmds: header.sizeofcmds,
flags: header.flags,
reserved: 0,
}
}
}
impl From<Header> for Header32 {
fn from(header: Header) -> Self {
Header32 {
magic: header.magic,
cputype: header.cputype,
cpusubtype: header.cpusubtype,
filetype: header.filetype,
ncmds: header.ncmds as u32,
sizeofcmds: header.sizeofcmds,
flags: header.flags,
}
}
}
impl From<Header64> for Header {
fn from(header: Header64) -> Self {
Header {
magic: header.magic,
cputype: header.cputype,
cpusubtype: header.cpusubtype,
filetype: header.filetype,
ncmds: header.ncmds as usize,
sizeofcmds: header.sizeofcmds,
flags: header.flags,
reserved: header.reserved,
}
}
}
impl From<Header> for Header64 {
fn from(header: Header) -> Self {
Header64 {
magic: header.magic,
cputype: header.cputype,
cpusubtype: header.cpusubtype,
filetype: header.filetype,
ncmds: header.ncmds as u32,
sizeofcmds: header.sizeofcmds,
flags: header.flags,
reserved: header.reserved,
}
}
}
impl Header {
pub fn new(ctx: container::Ctx) -> Self {
let mut header = Header::default();
header.magic = if ctx.is_big() { MH_MAGIC_64 } else { MH_MAGIC };
header
}
pub fn cputype(&self) -> CpuType {
self.cputype
}
pub fn cpusubtype(&self) -> CpuSubType {
self.cpusubtype & !CPU_SUBTYPE_MASK
}
pub fn cpu_caps(&self) -> u32 {
(self.cpusubtype & CPU_SUBTYPE_MASK) >> 24
}
}
impl ctx::SizeWith<container::Ctx> for Header {
fn size_with(container: &container::Ctx) -> usize {
match container.container {
Container::Little => SIZEOF_HEADER_32,
Container::Big => SIZEOF_HEADER_64,
}
}
}
impl ctx::SizeWith<Container> for Header {
fn size_with(container: &Container) -> usize {
match container {
Container::Little => SIZEOF_HEADER_32,
Container::Big => SIZEOF_HEADER_64,
}
}
}
impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Header {
type Error = crate::error::Error;
fn try_from_ctx(
bytes: &'a [u8],
container::Ctx { le, container }: container::Ctx,
) -> error::Result<(Self, usize)> {
let size = bytes.len();
if size < SIZEOF_HEADER_32 || size < SIZEOF_HEADER_64 {
let error =
error::Error::Malformed("bytes size is smaller than a Mach-o header".into());
Err(error)
} else {
match container {
Container::Little => {
let header = bytes.pread_with::<Header32>(0, le)?;
Ok((Header::from(header), SIZEOF_HEADER_32))
}
Container::Big => {
let header = bytes.pread_with::<Header64>(0, le)?;
Ok((Header::from(header), SIZEOF_HEADER_64))
}
}
}
}
}
impl ctx::TryIntoCtx<container::Ctx> for Header {
type Error = crate::error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> error::Result<usize> {
match ctx.container {
Container::Little => {
bytes.pwrite_with(Header32::from(self), 0, ctx.le)?;
}
Container::Big => {
bytes.pwrite_with(Header64::from(self), 0, ctx.le)?;
}
};
Ok(Header::size_with(&ctx))
}
}
impl ctx::IntoCtx<container::Ctx> for Header {
fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
bytes.pwrite_with(self, 0, ctx).unwrap();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of;
#[test]
fn test_parse_armv7_header() {
use crate::mach::constants::cputype::CPU_TYPE_ARM;
const CPU_SUBTYPE_ARM_V7: u32 = 9;
use super::Header;
use crate::container::{Container, Ctx, Endian};
use scroll::Pread;
let bytes = b"\xce\xfa\xed\xfe\x0c\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x06\x00\x00\x00\x8c\r\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x18\x00\x00\x00\xe0\xf7B\xbb\x1c\xf50w\xa6\xf7u\xa3\xba(";
let header: Header = bytes
.pread_with(0, Ctx::new(Container::Little, Endian::Little))
.unwrap();
assert_eq!(header.cputype, CPU_TYPE_ARM);
assert_eq!(header.cpusubtype, CPU_SUBTYPE_ARM_V7);
}
#[test]
fn sizeof_header32() {
assert_eq!(SIZEOF_HEADER_32, size_of::<Header32>());
}
#[test]
fn sizeof_header64() {
assert_eq!(SIZEOF_HEADER_64, size_of::<Header64>());
}
}