atom_macho/
header.rs

1use crate::io::{Endian, ReadExt as _, WriteExt as _};
2use byteorder::{NativeEndian, ReadBytesExt as _};
3use num_derive::FromPrimitive;
4use num_traits::FromPrimitive;
5use std::{
6    fmt,
7    io::{Read, Write},
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Header64 {
12    pub magic: Magic,
13    pub cpu_type: CpuType,
14    pub file_type: FileType,
15    pub n_cmds: u32,
16    pub size_of_cmds: u32,
17    pub flags: Flags,
18    pub reserved: u32,
19}
20
21impl Header64 {
22    pub const SIZE: u32 = 0x20; // 32 bytes
23
24    pub fn read_from<R: Read>(read: &mut R) -> Self {
25        let magic_n = read.read_u32::<NativeEndian>().unwrap();
26        let magic = Magic::from_u32(magic_n);
27        let endian = magic.endian();
28
29        let cpu_type_n = read.read_i32_in(endian);
30        let cpu_subtype_n = read.read_i32_in(endian);
31        let cpu_type = CpuType::from_i32_i32(cpu_type_n, cpu_subtype_n);
32
33        let file_type_n = read.read_u32_in(endian);
34        let file_type = FileType::from_u32(file_type_n);
35
36        let n_cmds = read.read_u32_in(endian);
37
38        let size_of_cmds = read.read_u32_in(endian);
39
40        let flags_n = read.read_u32_in(endian);
41        let flags = Flags::from_u32(flags_n);
42
43        let reserved = read.read_u32_in(endian);
44
45        let header = Header64 {
46            magic,
47            cpu_type,
48            file_type,
49            n_cmds,
50            size_of_cmds,
51            flags,
52            reserved,
53        };
54
55        header
56    }
57
58    pub fn write_into<W: Write>(&self, write: &mut W) {
59        write.write_u32_native(self.magic.to_u32());
60        let (cpu_type_n, cpu_subtype_n) = self.cpu_type.to_i32_i32();
61        write.write_i32_native(cpu_type_n);
62        write.write_i32_native(cpu_subtype_n);
63        write.write_u32_native(self.file_type.to_u32());
64        write.write_u32_native(self.n_cmds);
65        write.write_u32_native(self.size_of_cmds);
66        write.write_u32_native(self.flags.to_u32());
67        write.write_u32_native(self.reserved);
68    }
69
70    pub fn endian(&self) -> Endian {
71        self.magic.endian()
72    }
73}
74
75/// An integer containing a value identifying this file as a Mach-O file.
76#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Magic {
78    /// use if the file is intended for use on a 64bit CPU with the **same** endianness as the host
79    /// computer.
80    Magic64 = 0xfeedfacf,
81    /// use if the file is intended for use on a 64bit CPU with the **reverse** endianness as the
82    /// host computer.
83    Cigam64 = 0xcffaedfe,
84    /// use if the file is intended for use on a 32bit CPU with the **same** endianness as the host
85    /// computer.
86    Magic = 0xfeedface,
87    /// use if the file is intended for use on a 32bit CPU with the **reverse** endianness as the
88    /// host computer.
89    Cigam = 0xcefaedfe,
90    /// use if the file contains code for more than one architecture and is intended for use on a
91    /// CPU with the **same** endianness as the host computer.
92    FatMagic = 0xcafebabe,
93    /// use if the file contains code for more than one architecture and is intended for use on a
94    /// CPU with the **reverse** endianness as the host computer.
95    FatCigam = 0xbebafeca,
96}
97
98impl Magic {
99    pub fn from_u32_checked(n: u32) -> Option<Self> {
100        FromPrimitive::from_u32(n)
101    }
102
103    pub fn from_u32(n: u32) -> Self {
104        Magic::from_u32_checked(n).unwrap()
105    }
106
107    pub fn to_u32(&self) -> u32 {
108        *self as u32
109    }
110
111    pub fn endian(&self) -> Endian {
112        match self {
113            Magic::Magic64 | Magic::Magic | Magic::FatMagic => Endian::NATIVE,
114            Magic::Cigam64 | Magic::Cigam | Magic::FatCigam => Endian::REVERSE,
115        }
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum CpuType {
121    X86(CpuSubTypeX86),
122    X86_64(CpuSubTypeX86_64),
123}
124
125#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
126pub enum CpuSubTypeX86 {
127    All = 0x3,
128}
129
130#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
131pub enum CpuSubTypeX86_64 {
132    All = 0x3,
133}
134
135impl CpuType {
136    const CPU_ARCH_ABI64: i32 = 0x01000000;
137    const CPU_TYPE_X86: i32 = 0x7;
138    const CPU_TYPE_X86_64: i32 = Self::CPU_TYPE_X86 | Self::CPU_ARCH_ABI64;
139
140    pub fn from_i32_i32(cpu_type_n: i32, cpu_subtype_n: i32) -> Self {
141        // x86
142        if cpu_type_n == Self::CPU_TYPE_X86 {
143            let cpu_subtype = CpuSubTypeX86::from_i32(cpu_subtype_n).unwrap();
144            CpuType::X86(cpu_subtype)
145        // x86_64
146        } else if cpu_type_n == Self::CPU_TYPE_X86_64 {
147            let cpu_subtype = CpuSubTypeX86_64::from_i32(cpu_subtype_n).unwrap();
148            CpuType::X86_64(cpu_subtype)
149        } else {
150            panic!("Unsupported cpu type")
151        }
152    }
153
154    pub fn to_i32_i32(&self) -> (i32, i32) {
155        match self {
156            CpuType::X86(sub) => (CpuType::CPU_TYPE_X86, *sub as i32),
157            CpuType::X86_64(sub) => (CpuType::CPU_TYPE_X86_64, *sub as i32),
158        }
159    }
160}
161
162#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
163/// Declared in /usr/include/mach-o/loader.h
164pub enum FileType {
165    Object = 0x1,
166    Execute = 0x2,
167    FVMLib = 0x3,
168    Core = 0x4,
169    Preload = 0x5,
170    Dylib = 0x6,
171    Dylinker = 0x7,
172    Bundle = 0x8,
173    Dsym = 0xA,
174}
175
176impl FileType {
177    pub fn from_u32(n: u32) -> Self {
178        FromPrimitive::from_u32(n).unwrap()
179    }
180
181    pub fn to_u32(self) -> u32 {
182        self as u32
183    }
184}
185
186#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
187#[rustfmt::skip]
188pub enum Flag {
189    NoUndefs                = 0x000001,
190    IncrLink                = 0x000002,
191    DyldLink                = 0x000004,
192    BindAtLoad              = 0x000008,
193    PreBound                = 0x000010,
194    SplitSegs               = 0x000020,
195    TwoLevel                = 0x000080,
196    ForceFlat               = 0x000100,
197    NoMultiDefs             = 0x000200,
198    NoFixPreBinding         = 0x000400,
199    PreBindable             = 0x000800,
200    AllModsBound            = 0x001000,
201    SubsectionsViaSymbols   = 0x002000,
202    Canonical               = 0x004000,
203    Pie                     = 0x200000,
204    HasTlvDescriptors       = 0x800000,
205}
206
207impl Flag {
208    pub fn from_u32(n: u32) -> Self {
209        FromPrimitive::from_u32(n).unwrap()
210    }
211
212    pub fn to_u32(self) -> u32 {
213        self as u32
214    }
215}
216
217#[derive(Clone, PartialEq, Eq)]
218pub struct Flags {
219    flags: Vec<Flag>,
220}
221
222impl Flags {
223    pub fn new() -> Flags {
224        Flags { flags: Vec::new() }
225    }
226
227    pub fn push(&mut self, flag: Flag) {
228        self.flags.push(flag);
229    }
230    
231    pub fn is_empty(&self) -> bool {
232        self.flags.is_empty()
233    }
234
235    pub fn iter<'a>(&'a self) -> impl Iterator<Item = Flag> + 'a {
236        self.flags.iter().copied()
237    }
238
239    pub fn from_u32(flags_n: u32) -> Self {
240        let mut flags = Flags::new();
241        for i in 0..=31 {
242            let flag_n = flags_n & (1 << i);
243            if flag_n != 0 {
244                let flag = Flag::from_u32(flag_n);
245                flags.push(flag);
246            }
247        }
248
249        flags
250    }
251
252    pub fn to_u32(&self) -> u32 {
253        let mut flag_n = 0u32;
254
255        for flag in self.flags.iter() {
256            flag_n |= flag.to_u32();
257        }
258
259        flag_n
260    }
261}
262
263impl fmt::Debug for Flags {
264    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
265        fmt.debug_set().entries(self.flags.iter()).finish()
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272
273    #[test]
274    fn write_and_read_header64() {
275        let header = Header64 {
276            magic: Magic::Magic64,
277            cpu_type: CpuType::X86_64(CpuSubTypeX86_64::All),
278            file_type: FileType::Object,
279            n_cmds: 2,
280            size_of_cmds: 42,
281            flags: Flags::new(),
282            reserved: 0,
283        };
284
285        let mut buf = Vec::new();
286
287        header.write_into(&mut buf);
288
289        assert_eq!(buf.len(), Header64::SIZE as usize);
290
291        let read = Header64::read_from(&mut buf.as_slice());
292        assert_eq!(read, header);
293    }
294}