Skip to main content

macho_core/model/
header.rs

1use crate::error::{Error, Result};
2use crate::format::constants::*;
3use crate::format::io::endian::Endian;
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7/// The Bitness type.
8pub enum Bitness {
9    /// The Bits32 variant.
10    Bits32,
11    /// The Bits64 variant.
12    Bits64,
13}
14
15impl Bitness {
16    /// Performs header_size.
17    pub fn header_size(self) -> usize {
18        match self {
19            Bitness::Bits32 => 28,
20            Bitness::Bits64 => 32,
21        }
22    }
23}
24
25impl fmt::Display for Bitness {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Bitness::Bits32 => write!(f, "32-bit"),
29            Bitness::Bits64 => write!(f, "64-bit"),
30        }
31    }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35/// The MagicNumber type.
36pub enum MagicNumber {
37    /// The MachO32 variant.
38    MachO32,
39    /// The MachO64 variant.
40    MachO64,
41    /// The MachO32Swapped variant.
42    MachO32Swapped,
43    /// The MachO64Swapped variant.
44    MachO64Swapped,
45}
46
47impl MagicNumber {
48    /// Performs from_u32.
49    pub fn from_u32(v: u32) -> Result<Self> {
50        match v {
51            MH_MAGIC => Ok(Self::MachO32),
52            MH_MAGIC_64 => Ok(Self::MachO64),
53            MH_CIGAM => Ok(Self::MachO32Swapped),
54            MH_CIGAM_64 => Ok(Self::MachO64Swapped),
55            _ => Err(Error::format(format!(
56                "unrecognized Mach-O magic: {v:#010x}"
57            ))),
58        }
59    }
60
61    /// Performs endian.
62    pub fn endian(self) -> Endian {
63        match self {
64            Self::MachO32 | Self::MachO64 => {
65                // Native byte order magic means the file matches the host.
66                // On LE hosts (all modern Apple), non-swapped = Little.
67                // On BE hosts, non-swapped = Big.
68                if cfg!(target_endian = "little") {
69                    Endian::Little
70                } else {
71                    Endian::Big
72                }
73            }
74            Self::MachO32Swapped | Self::MachO64Swapped => {
75                if cfg!(target_endian = "little") {
76                    Endian::Big
77                } else {
78                    Endian::Little
79                }
80            }
81        }
82    }
83
84    /// Performs bitness.
85    pub fn bitness(self) -> Bitness {
86        match self {
87            Self::MachO32 | Self::MachO32Swapped => Bitness::Bits32,
88            Self::MachO64 | Self::MachO64Swapped => Bitness::Bits64,
89        }
90    }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94/// The FatMagic type.
95pub enum FatMagic {
96    /// The Fat32 variant.
97    Fat32,
98    /// The Fat64 variant.
99    Fat64,
100}
101
102impl FatMagic {
103    /// Performs from_u32.
104    pub fn from_u32(v: u32) -> Result<Self> {
105        match v {
106            FAT_MAGIC => Ok(Self::Fat32),
107            FAT_MAGIC_64 => Ok(Self::Fat64),
108            _ => Err(Error::format(format!("unrecognized fat magic: {v:#010x}"))),
109        }
110    }
111
112    /// Performs is_64bit.
113    pub fn is_64bit(self) -> bool {
114        matches!(self, Self::Fat64)
115    }
116}
117
118#[derive(Debug, Clone)]
119/// The FatHeader type.
120pub struct FatHeader {
121    magic: FatMagic,
122    nfat_arch: u32,
123}
124
125impl FatHeader {
126    pub(crate) const fn new(magic: FatMagic, nfat_arch: u32) -> Self {
127        Self { magic, nfat_arch }
128    }
129
130    /// Fat container encoding width.
131    pub const fn magic(&self) -> FatMagic {
132        self.magic
133    }
134
135    /// Number of architecture entries declared by the validated table.
136    pub const fn architecture_count(&self) -> u32 {
137        self.nfat_arch
138    }
139}
140
141#[derive(Clone, Copy, PartialEq, Eq, Hash)]
142/// The CpuType type.
143pub struct CpuType(pub i32);
144
145impl CpuType {
146    /// Performs name.
147    pub fn name(self) -> &'static str {
148        match self.0 {
149            CPU_TYPE_X86 => "x86",
150            CPU_TYPE_X86_64 => "x86_64",
151            CPU_TYPE_ARM => "arm",
152            CPU_TYPE_ARM64 => "arm64",
153            CPU_TYPE_ARM64_32 => "arm64_32",
154            CPU_TYPE_POWERPC => "ppc",
155            CPU_TYPE_POWERPC64 => "ppc64",
156            _ => "unknown",
157        }
158    }
159
160    /// Performs is_64bit.
161    pub fn is_64bit(self) -> bool {
162        (self.0 & CPU_ARCH_ABI64) != 0
163    }
164}
165
166impl fmt::Debug for CpuType {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        write!(f, "CpuType({}, {})", self.name(), self.0)
169    }
170}
171
172impl fmt::Display for CpuType {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(f, "{}", self.name())
175    }
176}
177
178#[derive(Clone, Copy, PartialEq, Eq, Hash)]
179/// The CpuSubtype type.
180pub struct CpuSubtype(pub i32);
181
182impl CpuSubtype {
183    /// Returns the subtype with capability bits stripped (high byte masked off).
184    pub fn masked(self) -> i32 {
185        self.0 & CPU_SUBTYPE_MASK
186    }
187
188    /// Performs name.
189    pub fn name(self, cpu_type: CpuType) -> &'static str {
190        let masked = self.masked();
191        match (cpu_type.0, masked) {
192            (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL) => "all",
193            (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8) => "v8",
194            (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E) => "arm64e",
195            (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL) => "all",
196            (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H) => "haswell",
197            (_, CPU_SUBTYPE_ALL) => "all",
198            _ => "unknown",
199        }
200    }
201}
202
203impl fmt::Debug for CpuSubtype {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        write!(f, "CpuSubtype({:#x})", self.0)
206    }
207}
208
209#[derive(Clone, Copy, PartialEq, Eq, Hash)]
210/// The ArchSpec type.
211pub struct ArchSpec {
212    /// The cpu_type field.
213    pub cpu_type: CpuType,
214    /// The cpu_subtype field.
215    pub cpu_subtype: CpuSubtype,
216}
217
218impl ArchSpec {
219    /// Returns whether a user-facing architecture selector names this slice.
220    ///
221    /// A CPU-family name selects every subtype in that family, while a known
222    /// qualified slice name selects only that subtype. For example, `arm64`
223    /// matches both plain arm64 and arm64e slices, whereas `arm64e` matches
224    /// only arm64e. Names are matched case-insensitively to preserve the CLI's
225    /// established behavior. Unknown subtype names retain the family spelling
226    /// and require an exact raw tuple where unambiguous identity matters.
227    pub fn matches_selector(&self, selector: &str) -> bool {
228        self.cpu_type.name().eq_ignore_ascii_case(selector)
229            || self.name().eq_ignore_ascii_case(selector)
230    }
231
232    /// Performs is_x86_64.
233    pub fn is_x86_64(&self) -> bool {
234        self.cpu_type.0 == CPU_TYPE_X86_64
235    }
236
237    /// Whether this is the Haswell-qualified x86-64 slice known as `x86_64h`.
238    pub fn is_x86_64h(&self) -> bool {
239        self.cpu_type.0 == CPU_TYPE_X86_64 && self.cpu_subtype.masked() == CPU_SUBTYPE_X86_64_H
240    }
241
242    /// Performs is_arm64.
243    pub fn is_arm64(&self) -> bool {
244        self.cpu_type.0 == CPU_TYPE_ARM64 && self.cpu_subtype.masked() != CPU_SUBTYPE_ARM64E
245    }
246
247    /// Performs is_arm64e.
248    pub fn is_arm64e(&self) -> bool {
249        self.cpu_type.0 == CPU_TYPE_ARM64 && self.cpu_subtype.masked() == CPU_SUBTYPE_ARM64E
250    }
251
252    /// Performs name.
253    pub fn name(&self) -> String {
254        if self.is_arm64e() {
255            "arm64e".to_string()
256        } else if self.is_x86_64h() {
257            "x86_64h".to_string()
258        } else {
259            self.cpu_type.name().to_string()
260        }
261    }
262}
263
264impl fmt::Debug for ArchSpec {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        write!(f, "ArchSpec({})", self.name())
267    }
268}
269
270impl fmt::Display for ArchSpec {
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        write!(f, "{}", self.name())
273    }
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277/// The FileType type.
278pub enum FileType {
279    /// The Object variant.
280    Object,
281    /// The Execute variant.
282    Execute,
283    /// The Fvmlib variant.
284    Fvmlib,
285    /// The Core variant.
286    Core,
287    /// The Preload variant.
288    Preload,
289    /// The Dylib variant.
290    Dylib,
291    /// The Dylinker variant.
292    Dylinker,
293    /// The Bundle variant.
294    Bundle,
295    /// The DylibStub variant.
296    DylibStub,
297    /// The Dsym variant.
298    Dsym,
299    /// The KextBundle variant.
300    KextBundle,
301    /// The Fileset variant.
302    Fileset,
303    /// The GpuExecute variant.
304    GpuExecute,
305    /// The GpuDylib variant.
306    GpuDylib,
307    /// The Unknown variant.
308    Unknown(u32),
309}
310
311impl FileType {
312    /// Performs from_u32.
313    pub fn from_u32(v: u32) -> Self {
314        match v {
315            MH_OBJECT => Self::Object,
316            MH_EXECUTE => Self::Execute,
317            MH_FVMLIB => Self::Fvmlib,
318            MH_CORE => Self::Core,
319            MH_PRELOAD => Self::Preload,
320            MH_DYLIB => Self::Dylib,
321            MH_DYLINKER => Self::Dylinker,
322            MH_BUNDLE => Self::Bundle,
323            MH_DYLIB_STUB => Self::DylibStub,
324            MH_DSYM => Self::Dsym,
325            MH_KEXT_BUNDLE => Self::KextBundle,
326            MH_FILESET => Self::Fileset,
327            MH_GPU_EXECUTE => Self::GpuExecute,
328            MH_GPU_DYLIB => Self::GpuDylib,
329            _ => Self::Unknown(v),
330        }
331    }
332
333    /// Performs name.
334    pub fn name(&self) -> &'static str {
335        match self {
336            Self::Object => "MH_OBJECT",
337            Self::Execute => "MH_EXECUTE",
338            Self::Fvmlib => "MH_FVMLIB",
339            Self::Core => "MH_CORE",
340            Self::Preload => "MH_PRELOAD",
341            Self::Dylib => "MH_DYLIB",
342            Self::Dylinker => "MH_DYLINKER",
343            Self::Bundle => "MH_BUNDLE",
344            Self::DylibStub => "MH_DYLIB_STUB",
345            Self::Dsym => "MH_DSYM",
346            Self::KextBundle => "MH_KEXT_BUNDLE",
347            Self::Fileset => "MH_FILESET",
348            Self::GpuExecute => "MH_GPU_EXECUTE",
349            Self::GpuDylib => "MH_GPU_DYLIB",
350            Self::Unknown(_) => "MH_UNKNOWN",
351        }
352    }
353}
354
355#[derive(Debug, Clone)]
356/// The MachoHeader type.
357pub struct MachoHeader {
358    pub(crate) magic: MagicNumber,
359    pub(crate) cpu_type: CpuType,
360    pub(crate) cpu_subtype: CpuSubtype,
361    pub(crate) file_type: FileType,
362    pub(crate) ncmds: u32,
363    pub(crate) sizeofcmds: u32,
364    pub(crate) flags: MachoHeaderFlags,
365    pub(crate) reserved: u32,
366}
367
368impl MachoHeader {
369    /// Parsed Mach-O magic and byte-order encoding.
370    pub const fn magic(&self) -> MagicNumber {
371        self.magic
372    }
373    /// Declared CPU family.
374    pub const fn cpu_type(&self) -> CpuType {
375        self.cpu_type
376    }
377    /// Declared CPU subtype.
378    pub const fn cpu_subtype(&self) -> CpuSubtype {
379        self.cpu_subtype
380    }
381    /// Qualified architecture identity for this Mach-O slice.
382    pub const fn arch_spec(&self) -> ArchSpec {
383        ArchSpec {
384            cpu_type: self.cpu_type,
385            cpu_subtype: self.cpu_subtype,
386        }
387    }
388    /// Declared Mach-O file type.
389    pub const fn file_type(&self) -> FileType {
390        self.file_type
391    }
392    /// Number of validated load commands.
393    pub const fn load_command_count(&self) -> u32 {
394        self.ncmds
395    }
396    /// Declared load-command byte size.
397    pub const fn load_commands_size(&self) -> u32 {
398        self.sizeofcmds
399    }
400    /// Validated header flags.
401    pub const fn flags(&self) -> MachoHeaderFlags {
402        self.flags
403    }
404    /// Reserved 64-bit header word.
405    pub const fn reserved(&self) -> u32 {
406        self.reserved
407    }
408}