object/read/macho/
fat.rs

1use crate::endian::BigEndian;
2use crate::macho;
3use crate::pod::Pod;
4use crate::read::{Architecture, Error, ReadError, ReadRef, Result};
5
6pub use macho::{FatArch32, FatArch64, FatHeader};
7
8/// A 32-bit Mach-O universal binary.
9///
10/// This is a file that starts with [`macho::FatHeader`], and corresponds
11/// to [`crate::FileKind::MachOFat32`].
12pub type MachOFatFile32<'data> = MachOFatFile<'data, macho::FatArch32>;
13
14/// A 64-bit Mach-O universal binary.
15///
16/// This is a file that starts with [`macho::FatHeader`], and corresponds
17/// to [`crate::FileKind::MachOFat64`].
18pub type MachOFatFile64<'data> = MachOFatFile<'data, macho::FatArch64>;
19
20/// A Mach-O universal binary.
21///
22/// This is a file that starts with [`macho::FatHeader`], and corresponds
23/// to [`crate::FileKind::MachOFat32`] or [`crate::FileKind::MachOFat64`].
24#[derive(Debug, Clone)]
25pub struct MachOFatFile<'data, Fat: FatArch> {
26    header: &'data macho::FatHeader,
27    arches: &'data [Fat],
28}
29
30impl<'data, Fat: FatArch> MachOFatFile<'data, Fat> {
31    /// Attempt to parse the fat header and fat arches.
32    pub fn parse<R: ReadRef<'data>>(data: R) -> Result<Self> {
33        let mut offset = 0;
34        let header = data
35            .read::<FatHeader>(&mut offset)
36            .read_error("Invalid fat header size or alignment")?;
37        if header.magic.get(BigEndian) != Fat::MAGIC {
38            return Err(Error("Invalid fat magic"));
39        }
40        let arches = data
41            .read_slice::<Fat>(&mut offset, header.nfat_arch.get(BigEndian) as usize)
42            .read_error("Invalid nfat_arch")?;
43        Ok(MachOFatFile { header, arches })
44    }
45
46    /// Return the fat header
47    pub fn header(&self) -> &'data macho::FatHeader {
48        self.header
49    }
50
51    /// Return the array of fat arches.
52    pub fn arches(&self) -> &'data [Fat] {
53        self.arches
54    }
55}
56
57/// A trait for generic access to [`macho::FatArch32`] and [`macho::FatArch64`].
58#[allow(missing_docs)]
59pub trait FatArch: Pod {
60    type Word: Into<u64>;
61    const MAGIC: u32;
62
63    fn cputype(&self) -> u32;
64    fn cpusubtype(&self) -> u32;
65    fn offset(&self) -> Self::Word;
66    fn size(&self) -> Self::Word;
67    fn align(&self) -> u32;
68
69    fn architecture(&self) -> Architecture {
70        match self.cputype() {
71            macho::CPU_TYPE_ARM => Architecture::Arm,
72            macho::CPU_TYPE_ARM64 => Architecture::Aarch64,
73            macho::CPU_TYPE_X86 => Architecture::I386,
74            macho::CPU_TYPE_X86_64 => Architecture::X86_64,
75            macho::CPU_TYPE_MIPS => Architecture::Mips,
76            macho::CPU_TYPE_POWERPC => Architecture::PowerPc,
77            macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64,
78            _ => Architecture::Unknown,
79        }
80    }
81
82    fn file_range(&self) -> (u64, u64) {
83        (self.offset().into(), self.size().into())
84    }
85
86    fn data<'data, R: ReadRef<'data>>(&self, file: R) -> Result<&'data [u8]> {
87        file.read_bytes_at(self.offset().into(), self.size().into())
88            .read_error("Invalid fat arch offset or size")
89    }
90}
91
92impl FatArch for FatArch32 {
93    type Word = u32;
94    const MAGIC: u32 = macho::FAT_MAGIC;
95
96    fn cputype(&self) -> u32 {
97        self.cputype.get(BigEndian)
98    }
99
100    fn cpusubtype(&self) -> u32 {
101        self.cpusubtype.get(BigEndian)
102    }
103
104    fn offset(&self) -> Self::Word {
105        self.offset.get(BigEndian)
106    }
107
108    fn size(&self) -> Self::Word {
109        self.size.get(BigEndian)
110    }
111
112    fn align(&self) -> u32 {
113        self.align.get(BigEndian)
114    }
115}
116
117impl FatArch for FatArch64 {
118    type Word = u64;
119    const MAGIC: u32 = macho::FAT_MAGIC_64;
120
121    fn cputype(&self) -> u32 {
122        self.cputype.get(BigEndian)
123    }
124
125    fn cpusubtype(&self) -> u32 {
126        self.cpusubtype.get(BigEndian)
127    }
128
129    fn offset(&self) -> Self::Word {
130        self.offset.get(BigEndian)
131    }
132
133    fn size(&self) -> Self::Word {
134        self.size.get(BigEndian)
135    }
136
137    fn align(&self) -> u32 {
138        self.align.get(BigEndian)
139    }
140}