use std::fmt;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::constants::*;
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct MachHeader64 {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
pub reserved: u32,
}
impl MachHeader64 {
pub const SIZE: usize = 32;
#[inline]
pub fn is_valid(&self) -> bool {
self.magic == MH_MAGIC_64
}
#[inline]
pub fn is_arm64(&self) -> bool {
self.cputype == CPU_TYPE_ARM64
}
#[inline]
pub fn is_arm64e(&self) -> bool {
self.is_arm64() && (self.cpusubtype & 0xFF) == CPU_SUBTYPE_ARM64E
}
#[inline]
pub fn is_x86_64(&self) -> bool {
self.cputype == CPU_TYPE_X86_64
}
#[inline]
pub fn is_dylib(&self) -> bool {
self.filetype == MH_DYLIB
}
pub fn arch_name(&self) -> &'static str {
match self.cputype {
CPU_TYPE_ARM64 => {
if self.is_arm64e() {
"arm64e"
} else {
"arm64"
}
}
CPU_TYPE_X86_64 => "x86_64",
CPU_TYPE_ARM => "arm",
CPU_TYPE_X86 => "i386",
_ => "unknown",
}
}
}
impl Default for MachHeader64 {
fn default() -> Self {
Self {
magic: MH_MAGIC_64,
cputype: 0,
cpusubtype: 0,
filetype: 0,
ncmds: 0,
sizeofcmds: 0,
flags: 0,
reserved: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct LoadCommand {
pub cmd: u32,
pub cmdsize: u32,
}
impl LoadCommand {
pub const SIZE: usize = 8;
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
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,
}
impl SegmentCommand64 {
pub const SIZE: usize = 72;
pub fn name(&self) -> &str {
let end = self.segname.iter().position(|&b| b == 0).unwrap_or(16);
std::str::from_utf8(&self.segname[..end]).unwrap_or("")
}
pub fn set_name(&mut self, name: &str) {
self.segname = [0u8; 16];
let bytes = name.as_bytes();
let len = bytes.len().min(16);
self.segname[..len].copy_from_slice(&bytes[..len]);
}
#[inline]
pub fn is_text(&self) -> bool {
&self.segname[..7] == b"__TEXT\0"
}
#[inline]
pub fn is_data(&self) -> bool {
&self.segname[..7] == b"__DATA\0"
}
#[inline]
pub fn is_linkedit(&self) -> bool {
&self.segname[..11] == b"__LINKEDIT\0"
}
}
impl Default for SegmentCommand64 {
fn default() -> Self {
Self {
cmd: LC_SEGMENT_64,
cmdsize: Self::SIZE as u32,
segname: [0u8; 16],
vmaddr: 0,
vmsize: 0,
fileoff: 0,
filesize: 0,
maxprot: 0,
initprot: 0,
nsects: 0,
flags: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
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,
}
impl Section64 {
pub const SIZE: usize = 80;
pub fn name(&self) -> &str {
let end = self.sectname.iter().position(|&b| b == 0).unwrap_or(16);
std::str::from_utf8(&self.sectname[..end]).unwrap_or("")
}
pub fn segment_name(&self) -> &str {
let end = self.segname.iter().position(|&b| b == 0).unwrap_or(16);
std::str::from_utf8(&self.segname[..end]).unwrap_or("")
}
pub fn set_name(&mut self, name: &str) {
self.sectname = [0u8; 16];
let bytes = name.as_bytes();
let len = bytes.len().min(16);
self.sectname[..len].copy_from_slice(&bytes[..len]);
}
#[inline]
pub fn section_type(&self) -> u32 {
self.flags & SECTION_TYPE
}
#[inline]
pub fn has_indirect_symbols(&self) -> bool {
matches!(
self.section_type(),
S_NON_LAZY_SYMBOL_POINTERS
| S_LAZY_SYMBOL_POINTERS
| S_SYMBOL_STUBS
| S_LAZY_DYLIB_SYMBOL_POINTERS
)
}
#[inline]
pub fn indirect_symbol_index(&self) -> u32 {
self.reserved1
}
#[inline]
pub fn stub_size(&self) -> u32 {
self.reserved2
}
}
impl Default for Section64 {
fn default() -> Self {
Self {
sectname: [0u8; 16],
segname: [0u8; 16],
addr: 0,
size: 0,
offset: 0,
align: 0,
reloff: 0,
nreloc: 0,
flags: 0,
reserved1: 0,
reserved2: 0,
reserved3: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct SymtabCommand {
pub cmd: u32,
pub cmdsize: u32,
pub symoff: u32,
pub nsyms: u32,
pub stroff: u32,
pub strsize: u32,
}
impl SymtabCommand {
pub const SIZE: usize = 24;
}
impl Default for SymtabCommand {
fn default() -> Self {
Self {
cmd: LC_SYMTAB,
cmdsize: Self::SIZE as u32,
symoff: 0,
nsyms: 0,
stroff: 0,
strsize: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
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 DysymtabCommand {
pub const SIZE: usize = 80;
}
impl Default for DysymtabCommand {
fn default() -> Self {
Self {
cmd: LC_DYSYMTAB,
cmdsize: Self::SIZE 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,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct Nlist64 {
pub n_strx: u32,
pub n_type: u8,
pub n_sect: u8,
pub n_desc: u16,
pub n_value: u64,
}
impl Nlist64 {
pub const SIZE: usize = 16;
#[inline]
pub fn is_external(&self) -> bool {
(self.n_type & N_EXT) != 0
}
#[inline]
pub fn is_undefined(&self) -> bool {
(self.n_type & N_TYPE) == N_UNDF
}
#[inline]
pub fn is_defined(&self) -> bool {
(self.n_type & N_TYPE) == N_SECT
}
#[inline]
pub fn is_debug(&self) -> bool {
(self.n_type & N_STAB) != 0
}
}
impl Default for Nlist64 {
fn default() -> Self {
Self {
n_strx: 0,
n_type: 0,
n_sect: 0,
n_desc: 0,
n_value: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
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,
}
impl DyldInfoCommand {
pub const SIZE: usize = 48;
}
impl Default for DyldInfoCommand {
fn default() -> Self {
Self {
cmd: LC_DYLD_INFO_ONLY,
cmdsize: Self::SIZE as u32,
rebase_off: 0,
rebase_size: 0,
bind_off: 0,
bind_size: 0,
weak_bind_off: 0,
weak_bind_size: 0,
lazy_bind_off: 0,
lazy_bind_size: 0,
export_off: 0,
export_size: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct LinkeditDataCommand {
pub cmd: u32,
pub cmdsize: u32,
pub dataoff: u32,
pub datasize: u32,
}
impl LinkeditDataCommand {
pub const SIZE: usize = 16;
}
impl Default for LinkeditDataCommand {
fn default() -> Self {
Self {
cmd: 0,
cmdsize: Self::SIZE as u32,
dataoff: 0,
datasize: 0,
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct Dylib {
pub name_offset: u32,
pub timestamp: u32,
pub current_version: u32,
pub compatibility_version: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DylibCommand {
pub cmd: u32,
pub cmdsize: u32,
pub dylib: Dylib,
}
impl DylibCommand {
pub const SIZE: usize = 24;
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct UuidCommand {
pub cmd: u32,
pub cmdsize: u32,
pub uuid: [u8; 16],
}
impl UuidCommand {
pub const SIZE: usize = 24;
}
impl Default for UuidCommand {
fn default() -> Self {
Self {
cmd: LC_UUID,
cmdsize: Self::SIZE as u32,
uuid: [0u8; 16],
}
}
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct BuildVersionCommand {
pub cmd: u32,
pub cmdsize: u32,
pub platform: u32,
pub minos: u32,
pub sdk: u32,
pub ntools: u32,
}
impl BuildVersionCommand {
pub const SIZE: usize = 24;
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct FilesetEntryCommand {
pub cmd: u32,
pub cmdsize: u32,
pub vmaddr: u64,
pub fileoff: u64,
pub entry_id_offset: u32,
pub reserved: u32,
}
impl FilesetEntryCommand {
pub const SIZE: usize = 32;
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct EncryptionInfoCommand64 {
pub cmd: u32,
pub cmdsize: u32,
pub cryptoff: u32,
pub cryptsize: u32,
pub cryptid: u32,
pub pad: u32,
}
impl EncryptionInfoCommand64 {
pub const SIZE: usize = 24;
}
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct SourceVersionCommand {
pub cmd: u32,
pub cmdsize: u32,
pub version: u64,
}
impl SourceVersionCommand {
pub const SIZE: usize = 16;
}
impl fmt::Display for MachHeader64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"MachO {{ arch: {}, type: {:#x}, cmds: {}, flags: {:#x} }}",
self.arch_name(),
self.filetype,
self.ncmds,
self.flags
)
}
}
impl fmt::Display for SegmentCommand64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Segment {{ name: \"{}\", vm: {:#x}+{:#x}, file: {:#x}+{:#x}, sects: {} }}",
self.name(),
self.vmaddr,
self.vmsize,
self.fileoff,
self.filesize,
self.nsects
)
}
}
impl fmt::Display for Section64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Section {{ name: \"{},{}\", addr: {:#x}+{:#x}, offset: {:#x} }}",
self.segment_name(),
self.name(),
self.addr,
self.size,
self.offset
)
}
}