1use goblin::mach::{cputype::get_arch_from_flag, Mach, MultiArch};
2
3use crate::error::Error;
4
5#[derive(Debug)]
7pub struct FatReader<'a> {
8 buffer: &'a [u8],
9 fat: MultiArch<'a>,
10}
11
12impl<'a> FatReader<'a> {
13 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 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}