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