1 use core::slice; 2 3 use crate::read::{Error, File, ReadError, ReadRef, Result}; 4 use crate::{macho, Architecture, Endian, Endianness}; 5 6 /// A parsed representation of the dyld shared cache. 7 #[derive(Debug)] 8 pub struct DyldCache<'data, E = Endianness, R = &'data [u8]> 9 where 10 E: Endian, 11 R: ReadRef<'data>, 12 { 13 endian: E, 14 data: R, 15 header: &'data macho::DyldCacheHeader<E>, 16 mappings: &'data [macho::DyldCacheMappingInfo<E>], 17 images: &'data [macho::DyldCacheImageInfo<E>], 18 arch: Architecture, 19 } 20 21 impl<'data, E, R> DyldCache<'data, E, R> 22 where 23 E: Endian, 24 R: ReadRef<'data>, 25 { 26 /// Parse the raw dyld shared cache data. parse(data: R) -> Result<Self>27 pub fn parse(data: R) -> Result<Self> { 28 let header = macho::DyldCacheHeader::parse(data)?; 29 let (arch, endian) = header.parse_magic()?; 30 let mappings = header.mappings(endian, data)?; 31 let images = header.images(endian, data)?; 32 Ok(DyldCache { 33 endian, 34 data, 35 header, 36 mappings, 37 images, 38 arch, 39 }) 40 } 41 42 /// Get the architecture type of the file. architecture(&self) -> Architecture43 pub fn architecture(&self) -> Architecture { 44 self.arch 45 } 46 47 /// Get the endianness of the file. 48 #[inline] endianness(&self) -> Endianness49 pub fn endianness(&self) -> Endianness { 50 if self.is_little_endian() { 51 Endianness::Little 52 } else { 53 Endianness::Big 54 } 55 } 56 57 /// Return true if the file is little endian, false if it is big endian. is_little_endian(&self) -> bool58 pub fn is_little_endian(&self) -> bool { 59 self.endian.is_little_endian() 60 } 61 62 /// Iterate over the images in this cache. images<'cache>(&'cache self) -> DyldCacheImageIterator<'data, 'cache, E, R>63 pub fn images<'cache>(&'cache self) -> DyldCacheImageIterator<'data, 'cache, E, R> { 64 DyldCacheImageIterator { 65 cache: self, 66 iter: self.images.iter(), 67 } 68 } 69 } 70 71 /// An iterator over all the images (dylibs) in the dyld shared cache. 72 #[derive(Debug)] 73 pub struct DyldCacheImageIterator<'data, 'cache, E = Endianness, R = &'data [u8]> 74 where 75 E: Endian, 76 R: ReadRef<'data>, 77 { 78 cache: &'cache DyldCache<'data, E, R>, 79 iter: slice::Iter<'data, macho::DyldCacheImageInfo<E>>, 80 } 81 82 impl<'data, 'cache, E, R> Iterator for DyldCacheImageIterator<'data, 'cache, E, R> 83 where 84 E: Endian, 85 R: ReadRef<'data>, 86 { 87 type Item = DyldCacheImage<'data, E, R>; 88 next(&mut self) -> Option<DyldCacheImage<'data, E, R>>89 fn next(&mut self) -> Option<DyldCacheImage<'data, E, R>> { 90 let image_info = self.iter.next()?; 91 Some(DyldCacheImage { 92 endian: self.cache.endian, 93 data: self.cache.data, 94 mappings: self.cache.mappings, 95 image_info, 96 }) 97 } 98 } 99 100 /// One image (dylib) from inside the dyld shared cache. 101 #[derive(Debug)] 102 pub struct DyldCacheImage<'data, E = Endianness, R = &'data [u8]> 103 where 104 E: Endian, 105 R: ReadRef<'data>, 106 { 107 endian: E, 108 data: R, 109 mappings: &'data [macho::DyldCacheMappingInfo<E>], 110 image_info: &'data macho::DyldCacheImageInfo<E>, 111 } 112 113 impl<'data, E, R> DyldCacheImage<'data, E, R> 114 where 115 E: Endian, 116 R: ReadRef<'data>, 117 { 118 /// The file system path of this image. path(&self) -> Result<&'data str>119 pub fn path(&self) -> Result<&'data str> { 120 let path = self.image_info.path(self.endian, self.data)?; 121 // The path should always be ascii, so from_utf8 should alway succeed. 122 let path = core::str::from_utf8(path).map_err(|_| Error("Path string not valid utf-8"))?; 123 Ok(path) 124 } 125 126 /// The offset in the dyld cache file where this image starts. file_offset(&self) -> Result<u64>127 pub fn file_offset(&self) -> Result<u64> { 128 self.image_info.file_offset(self.endian, self.mappings) 129 } 130 131 /// Parse this image into an Object. parse_object(&self) -> Result<File<'data, R>>132 pub fn parse_object(&self) -> Result<File<'data, R>> { 133 File::parse_at(self.data, self.file_offset()?) 134 } 135 } 136 137 impl<E: Endian> macho::DyldCacheHeader<E> { 138 /// Read the dyld cache header. parse<'data, R: ReadRef<'data>>(data: R) -> Result<&'data Self>139 pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<&'data Self> { 140 data.read_at::<macho::DyldCacheHeader<E>>(0) 141 .read_error("Invalid dyld cache header size or alignment") 142 } 143 144 /// Returns (arch, endian) based on the magic string. parse_magic(&self) -> Result<(Architecture, E)>145 pub fn parse_magic(&self) -> Result<(Architecture, E)> { 146 let (arch, is_big_endian) = match &self.magic { 147 b"dyld_v1 i386\0" => (Architecture::I386, false), 148 b"dyld_v1 x86_64\0" => (Architecture::X86_64, false), 149 b"dyld_v1 x86_64h\0" => (Architecture::X86_64, false), 150 b"dyld_v1 ppc\0" => (Architecture::PowerPc, true), 151 b"dyld_v1 armv6\0" => (Architecture::Arm, false), 152 b"dyld_v1 armv7\0" => (Architecture::Arm, false), 153 b"dyld_v1 armv7f\0" => (Architecture::Arm, false), 154 b"dyld_v1 armv7s\0" => (Architecture::Arm, false), 155 b"dyld_v1 armv7k\0" => (Architecture::Arm, false), 156 b"dyld_v1 arm64\0" => (Architecture::Aarch64, false), 157 b"dyld_v1 arm64e\0" => (Architecture::Aarch64, false), 158 _ => return Err(Error("Unrecognized dyld cache magic")), 159 }; 160 let endian = 161 E::from_big_endian(is_big_endian).read_error("Unsupported dyld cache endian")?; 162 Ok((arch, endian)) 163 } 164 165 /// Return the mapping information table. mappings<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result<&'data [macho::DyldCacheMappingInfo<E>]>166 pub fn mappings<'data, R: ReadRef<'data>>( 167 &self, 168 endian: E, 169 data: R, 170 ) -> Result<&'data [macho::DyldCacheMappingInfo<E>]> { 171 data.read_slice_at::<macho::DyldCacheMappingInfo<E>>( 172 self.mapping_offset.get(endian).into(), 173 self.mapping_count.get(endian) as usize, 174 ) 175 .read_error("Invalid dyld cache mapping size or alignment") 176 } 177 178 /// Return the image information table. images<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result<&'data [macho::DyldCacheImageInfo<E>]>179 pub fn images<'data, R: ReadRef<'data>>( 180 &self, 181 endian: E, 182 data: R, 183 ) -> Result<&'data [macho::DyldCacheImageInfo<E>]> { 184 data.read_slice_at::<macho::DyldCacheImageInfo<E>>( 185 self.images_offset.get(endian).into(), 186 self.images_count.get(endian) as usize, 187 ) 188 .read_error("Invalid dyld cache image size or alignment") 189 } 190 } 191 192 impl<E: Endian> macho::DyldCacheImageInfo<E> { 193 /// The file system path of this image. path<'data, R: ReadRef<'data>>(&self, endian: E, data: R) -> Result<&'data [u8]>194 pub fn path<'data, R: ReadRef<'data>>(&self, endian: E, data: R) -> Result<&'data [u8]> { 195 let r_start = self.path_file_offset.get(endian).into(); 196 let r_end = data.len().read_error("Couldn't get data len()")?; 197 data.read_bytes_at_until(r_start..r_end, 0) 198 .read_error("Couldn't read dyld cache image path") 199 } 200 201 /// Find the file offset of the image by looking up its address in the mappings. file_offset( &self, endian: E, mappings: &[macho::DyldCacheMappingInfo<E>], ) -> Result<u64>202 pub fn file_offset( 203 &self, 204 endian: E, 205 mappings: &[macho::DyldCacheMappingInfo<E>], 206 ) -> Result<u64> { 207 let address = self.address.get(endian); 208 for mapping in mappings { 209 let mapping_address = mapping.address.get(endian); 210 if address >= mapping_address 211 && address < mapping_address.wrapping_add(mapping.size.get(endian)) 212 { 213 return Ok(address - mapping_address + mapping.file_offset.get(endian)); 214 } 215 } 216 Err(Error("Invalid dyld cache image address")) 217 } 218 } 219