fat_macho/
read.rs

1use goblin::mach::{cputype::get_arch_from_flag, Mach, MultiArch};
2
3use crate::error::Error;
4
5/// Mach-O fat binary reader
6#[derive(Debug)]
7pub struct FatReader<'a> {
8    buffer: &'a [u8],
9    fat: MultiArch<'a>,
10}
11
12impl<'a> FatReader<'a> {
13    /// Parse a Mach-O FAT binary from a buffer
14    pub fn new(buffer: &'a [u8]) -> Result<Self, Error> {
15        match Mach::parse(buffer)? {
16            Mach::Fat(fat) => Ok(Self { buffer, fat }),
17            Mach::Binary(_) => Err(Error::NotFatBinary),
18        }
19    }
20
21    /// Extract thin binary by arch name
22    pub fn extract(&self, arch_name: &str) -> Option<&'a [u8]> {
23        if let Some((cpu_type, _cpu_subtype)) = get_arch_from_flag(arch_name) {
24            return self
25                .fat
26                .find_cputype(cpu_type)
27                .unwrap_or_default()
28                .map(|arch| arch.slice(self.buffer));
29        }
30        None
31    }
32}
33
34impl<'a> std::ops::Deref for FatReader<'a> {
35    type Target = MultiArch<'a>;
36
37    fn deref(&self) -> &Self::Target {
38        &self.fat
39    }
40}
41
42impl<'a> std::ops::DerefMut for FatReader<'a> {
43    fn deref_mut(&mut self) -> &mut Self::Target {
44        &mut self.fat
45    }
46}
47
48#[cfg(test)]
49mod test {
50    use std::fs;
51
52    use goblin::Object;
53
54    use super::FatReader;
55    use crate::error::Error;
56
57    #[test]
58    fn test_fat_reader_dylib() {
59        let buf = fs::read("tests/fixtures/simplefat.dylib").unwrap();
60        let reader = FatReader::new(&buf);
61        assert!(reader.is_ok());
62    }
63
64    #[test]
65    fn test_fat_reader_exe() {
66        let buf = fs::read("tests/fixtures/simplefat").unwrap();
67        let reader = FatReader::new(&buf).unwrap();
68        assert_eq!(2, reader.narches);
69
70        let buf = fs::read("tests/fixtures/hellofat").unwrap();
71        let reader = FatReader::new(&buf).unwrap();
72        assert_eq!(3, reader.narches);
73    }
74
75    #[test]
76    fn test_fat_reader_ar() {
77        let buf = fs::read("tests/fixtures/simplefat.a").unwrap();
78        let reader = FatReader::new(&buf);
79        assert!(reader.is_ok());
80    }
81
82    #[test]
83    fn test_fat_reader_not_fat() {
84        let buf = fs::read("tests/fixtures/thin_x86_64").unwrap();
85        let reader = FatReader::new(&buf);
86        assert!(reader.is_err());
87        assert!(matches!(reader.unwrap_err(), Error::NotFatBinary));
88
89        let buf = fs::read("tests/fixtures/thin_arm64").unwrap();
90        let reader = FatReader::new(&buf);
91        assert!(reader.is_err());
92        assert!(matches!(reader.unwrap_err(), Error::NotFatBinary));
93    }
94
95    #[test]
96    fn test_fat_reader_extract_dylib() {
97        let buf = fs::read("tests/fixtures/simplefat.dylib").unwrap();
98        let reader = FatReader::new(&buf).unwrap();
99        let x86_64 = reader.extract("x86_64").unwrap();
100        let x86_64_obj = Object::parse(x86_64).unwrap();
101        assert!(matches!(x86_64_obj, Object::Mach(_)));
102        let arm64 = reader.extract("arm64").unwrap();
103        let arm64_obj = Object::parse(arm64).unwrap();
104        assert!(matches!(arm64_obj, Object::Mach(_)));
105    }
106
107    #[test]
108    fn test_fat_reader_extract_exe() {
109        let buf = fs::read("tests/fixtures/simplefat").unwrap();
110        let reader = FatReader::new(&buf).unwrap();
111        let x86_64 = reader.extract("x86_64").unwrap();
112        let x86_64_obj = Object::parse(x86_64).unwrap();
113        assert!(matches!(x86_64_obj, Object::Mach(_)));
114        let arm64 = reader.extract("arm64").unwrap();
115        let arm64_obj = Object::parse(arm64).unwrap();
116        assert!(matches!(arm64_obj, Object::Mach(_)));
117    }
118
119    #[test]
120    fn test_fat_reader_extract_ar() {
121        let buf = fs::read("tests/fixtures/simplefat.a").unwrap();
122        let reader = FatReader::new(&buf).unwrap();
123        let x86_64 = reader.extract("x86_64").unwrap();
124        let x86_64_obj = Object::parse(x86_64).unwrap();
125        assert!(matches!(x86_64_obj, Object::Archive(_)));
126        let arm64 = reader.extract("arm64").unwrap();
127        let arm64_obj = Object::parse(arm64).unwrap();
128        assert!(matches!(arm64_obj, Object::Archive(_)));
129    }
130}