1 use goblin::mach::{cputype::get_arch_from_flag, Mach, MultiArch};
2 
3 use crate::error::Error;
4 
5 /// Mach-O fat binary reader
6 #[derive(Debug)]
7 pub struct FatReader<'a> {
8     buffer: &'a [u8],
9     fat: MultiArch<'a>,
10 }
11 
12 impl<'a> FatReader<'a> {
13     /// Parse a Mach-O FAT binary from a buffer
new(buffer: &'a [u8]) -> Result<Self, Error>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
extract(&self, arch_name: &str) -> Option<&'a [u8]>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 
34 impl<'a> std::ops::Deref for FatReader<'a> {
35     type Target = MultiArch<'a>;
36 
deref(&self) -> &Self::Target37     fn deref(&self) -> &Self::Target {
38         &self.fat
39     }
40 }
41 
42 impl<'a> std::ops::DerefMut for FatReader<'a> {
deref_mut(&mut self) -> &mut Self::Target43     fn deref_mut(&mut self) -> &mut Self::Target {
44         &mut self.fat
45     }
46 }
47 
48 #[cfg(test)]
49 mod test {
50     use std::fs;
51 
52     use goblin::Object;
53 
54     use super::FatReader;
55     use crate::error::Error;
56 
57     #[test]
test_fat_reader_dylib()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]
test_fat_reader_exe()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]
test_fat_reader_ar()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]
test_fat_reader_not_fat()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]
test_fat_reader_extract_dylib()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]
test_fat_reader_extract_exe()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]
test_fat_reader_extract_ar()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 }
131