| 1 | use core::marker::PhantomData; | 
| 2 | use core::mem; | 
| 3 |  | 
| 4 | use crate::endian::Endian; | 
| 5 | use crate::macho; | 
| 6 | use crate::pod::Pod; | 
| 7 | use crate::read::macho::{MachHeader, SymbolTable}; | 
| 8 | use crate::read::{Bytes, Error, ReadError, ReadRef, Result, StringTable}; | 
| 9 |  | 
| 10 | /// An iterator for the load commands from a [`MachHeader`]. | 
| 11 | #[derive (Debug, Default, Clone, Copy)] | 
| 12 | pub struct LoadCommandIterator<'data, E: Endian> { | 
| 13 |     endian: E, | 
| 14 |     data: Bytes<'data>, | 
| 15 |     ncmds: u32, | 
| 16 | } | 
| 17 |  | 
| 18 | impl<'data, E: Endian> LoadCommandIterator<'data, E> { | 
| 19 |     pub(super) fn new(endian: E, data: &'data [u8], ncmds: u32) -> Self { | 
| 20 |         LoadCommandIterator { | 
| 21 |             endian, | 
| 22 |             data: Bytes(data), | 
| 23 |             ncmds, | 
| 24 |         } | 
| 25 |     } | 
| 26 |  | 
| 27 |     /// Return the next load command. | 
| 28 |     pub fn next(&mut self) -> Result<Option<LoadCommandData<'data, E>>> { | 
| 29 |         if self.ncmds == 0 { | 
| 30 |             return Ok(None); | 
| 31 |         } | 
| 32 |         let header = self | 
| 33 |             .data | 
| 34 |             .read_at::<macho::LoadCommand<E>>(0) | 
| 35 |             .read_error("Invalid Mach-O load command header" )?; | 
| 36 |         let cmd = header.cmd.get(self.endian); | 
| 37 |         let cmdsize = header.cmdsize.get(self.endian) as usize; | 
| 38 |         if cmdsize < mem::size_of::<macho::LoadCommand<E>>() { | 
| 39 |             return Err(Error("Invalid Mach-O load command size" )); | 
| 40 |         } | 
| 41 |         let data = self | 
| 42 |             .data | 
| 43 |             .read_bytes(cmdsize) | 
| 44 |             .read_error("Invalid Mach-O load command size" )?; | 
| 45 |         self.ncmds -= 1; | 
| 46 |         Ok(Some(LoadCommandData { | 
| 47 |             cmd, | 
| 48 |             data, | 
| 49 |             marker: Default::default(), | 
| 50 |         })) | 
| 51 |     } | 
| 52 | } | 
| 53 |  | 
| 54 | /// The data for a [`macho::LoadCommand`]. | 
| 55 | #[derive (Debug, Clone, Copy)] | 
| 56 | pub struct LoadCommandData<'data, E: Endian> { | 
| 57 |     cmd: u32, | 
| 58 |     // Includes the header. | 
| 59 |     data: Bytes<'data>, | 
| 60 |     marker: PhantomData<E>, | 
| 61 | } | 
| 62 |  | 
| 63 | impl<'data, E: Endian> LoadCommandData<'data, E> { | 
| 64 |     /// Return the `cmd` field of the [`macho::LoadCommand`]. | 
| 65 |     /// | 
| 66 |     /// This is one of the `LC_` constants. | 
| 67 |     pub fn cmd(&self) -> u32 { | 
| 68 |         self.cmd | 
| 69 |     } | 
| 70 |  | 
| 71 |     /// Return the `cmdsize` field of the [`macho::LoadCommand`]. | 
| 72 |     pub fn cmdsize(&self) -> u32 { | 
| 73 |         self.data.len() as u32 | 
| 74 |     } | 
| 75 |  | 
| 76 |     /// Parse the data as the given type. | 
| 77 |     #[inline ] | 
| 78 |     pub fn data<T: Pod>(&self) -> Result<&'data T> { | 
| 79 |         self.data | 
| 80 |             .read_at(0) | 
| 81 |             .read_error("Invalid Mach-O command size" ) | 
| 82 |     } | 
| 83 |  | 
| 84 |     /// Raw bytes of this [`macho::LoadCommand`] structure. | 
| 85 |     pub fn raw_data(&self) -> &'data [u8] { | 
| 86 |         self.data.0 | 
| 87 |     } | 
| 88 |  | 
| 89 |     /// Parse a load command string value. | 
| 90 |     /// | 
| 91 |     /// Strings used by load commands are specified by offsets that are | 
| 92 |     /// relative to the load command header. | 
| 93 |     pub fn string(&self, endian: E, s: macho::LcStr<E>) -> Result<&'data [u8]> { | 
| 94 |         self.data | 
| 95 |             .read_string_at(s.offset.get(endian) as usize) | 
| 96 |             .read_error("Invalid load command string offset" ) | 
| 97 |     } | 
| 98 |  | 
| 99 |     /// Parse the command data according to the `cmd` field. | 
| 100 |     pub fn variant(&self) -> Result<LoadCommandVariant<'data, E>> { | 
| 101 |         Ok(match self.cmd { | 
| 102 |             macho::LC_SEGMENT => { | 
| 103 |                 let mut data = self.data; | 
| 104 |                 let segment = data.read().read_error("Invalid Mach-O command size" )?; | 
| 105 |                 LoadCommandVariant::Segment32(segment, data.0) | 
| 106 |             } | 
| 107 |             macho::LC_SYMTAB => LoadCommandVariant::Symtab(self.data()?), | 
| 108 |             macho::LC_THREAD | macho::LC_UNIXTHREAD => { | 
| 109 |                 let mut data = self.data; | 
| 110 |                 let thread = data.read().read_error("Invalid Mach-O command size" )?; | 
| 111 |                 LoadCommandVariant::Thread(thread, data.0) | 
| 112 |             } | 
| 113 |             macho::LC_DYSYMTAB => LoadCommandVariant::Dysymtab(self.data()?), | 
| 114 |             macho::LC_LOAD_DYLIB | 
| 115 |             | macho::LC_LOAD_WEAK_DYLIB | 
| 116 |             | macho::LC_REEXPORT_DYLIB | 
| 117 |             | macho::LC_LAZY_LOAD_DYLIB | 
| 118 |             | macho::LC_LOAD_UPWARD_DYLIB => LoadCommandVariant::Dylib(self.data()?), | 
| 119 |             macho::LC_ID_DYLIB => LoadCommandVariant::IdDylib(self.data()?), | 
| 120 |             macho::LC_LOAD_DYLINKER => LoadCommandVariant::LoadDylinker(self.data()?), | 
| 121 |             macho::LC_ID_DYLINKER => LoadCommandVariant::IdDylinker(self.data()?), | 
| 122 |             macho::LC_PREBOUND_DYLIB => LoadCommandVariant::PreboundDylib(self.data()?), | 
| 123 |             macho::LC_ROUTINES => LoadCommandVariant::Routines32(self.data()?), | 
| 124 |             macho::LC_SUB_FRAMEWORK => LoadCommandVariant::SubFramework(self.data()?), | 
| 125 |             macho::LC_SUB_UMBRELLA => LoadCommandVariant::SubUmbrella(self.data()?), | 
| 126 |             macho::LC_SUB_CLIENT => LoadCommandVariant::SubClient(self.data()?), | 
| 127 |             macho::LC_SUB_LIBRARY => LoadCommandVariant::SubLibrary(self.data()?), | 
| 128 |             macho::LC_TWOLEVEL_HINTS => LoadCommandVariant::TwolevelHints(self.data()?), | 
| 129 |             macho::LC_PREBIND_CKSUM => LoadCommandVariant::PrebindCksum(self.data()?), | 
| 130 |             macho::LC_SEGMENT_64 => { | 
| 131 |                 let mut data = self.data; | 
| 132 |                 let segment = data.read().read_error("Invalid Mach-O command size" )?; | 
| 133 |                 LoadCommandVariant::Segment64(segment, data.0) | 
| 134 |             } | 
| 135 |             macho::LC_ROUTINES_64 => LoadCommandVariant::Routines64(self.data()?), | 
| 136 |             macho::LC_UUID => LoadCommandVariant::Uuid(self.data()?), | 
| 137 |             macho::LC_RPATH => LoadCommandVariant::Rpath(self.data()?), | 
| 138 |             macho::LC_CODE_SIGNATURE | 
| 139 |             | macho::LC_SEGMENT_SPLIT_INFO | 
| 140 |             | macho::LC_FUNCTION_STARTS | 
| 141 |             | macho::LC_DATA_IN_CODE | 
| 142 |             | macho::LC_DYLIB_CODE_SIGN_DRS | 
| 143 |             | macho::LC_LINKER_OPTIMIZATION_HINT | 
| 144 |             | macho::LC_DYLD_EXPORTS_TRIE | 
| 145 |             | macho::LC_DYLD_CHAINED_FIXUPS => LoadCommandVariant::LinkeditData(self.data()?), | 
| 146 |             macho::LC_ENCRYPTION_INFO => LoadCommandVariant::EncryptionInfo32(self.data()?), | 
| 147 |             macho::LC_DYLD_INFO | macho::LC_DYLD_INFO_ONLY => { | 
| 148 |                 LoadCommandVariant::DyldInfo(self.data()?) | 
| 149 |             } | 
| 150 |             macho::LC_VERSION_MIN_MACOSX | 
| 151 |             | macho::LC_VERSION_MIN_IPHONEOS | 
| 152 |             | macho::LC_VERSION_MIN_TVOS | 
| 153 |             | macho::LC_VERSION_MIN_WATCHOS => LoadCommandVariant::VersionMin(self.data()?), | 
| 154 |             macho::LC_DYLD_ENVIRONMENT => LoadCommandVariant::DyldEnvironment(self.data()?), | 
| 155 |             macho::LC_MAIN => LoadCommandVariant::EntryPoint(self.data()?), | 
| 156 |             macho::LC_SOURCE_VERSION => LoadCommandVariant::SourceVersion(self.data()?), | 
| 157 |             macho::LC_ENCRYPTION_INFO_64 => LoadCommandVariant::EncryptionInfo64(self.data()?), | 
| 158 |             macho::LC_LINKER_OPTION => LoadCommandVariant::LinkerOption(self.data()?), | 
| 159 |             macho::LC_NOTE => LoadCommandVariant::Note(self.data()?), | 
| 160 |             macho::LC_BUILD_VERSION => LoadCommandVariant::BuildVersion(self.data()?), | 
| 161 |             macho::LC_FILESET_ENTRY => LoadCommandVariant::FilesetEntry(self.data()?), | 
| 162 |             _ => LoadCommandVariant::Other, | 
| 163 |         }) | 
| 164 |     } | 
| 165 |  | 
| 166 |     /// Try to parse this command as a [`macho::SegmentCommand32`]. | 
| 167 |     /// | 
| 168 |     /// Returns the segment command and the data containing the sections. | 
| 169 |     pub fn segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, &'data [u8])>> { | 
| 170 |         if self.cmd == macho::LC_SEGMENT { | 
| 171 |             let mut data = self.data; | 
| 172 |             let segment = data.read().read_error("Invalid Mach-O command size" )?; | 
| 173 |             Ok(Some((segment, data.0))) | 
| 174 |         } else { | 
| 175 |             Ok(None) | 
| 176 |         } | 
| 177 |     } | 
| 178 |  | 
| 179 |     /// Try to parse this command as a [`macho::SymtabCommand`]. | 
| 180 |     /// | 
| 181 |     /// Returns the segment command and the data containing the sections. | 
| 182 |     pub fn symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>> { | 
| 183 |         if self.cmd == macho::LC_SYMTAB { | 
| 184 |             Some(self.data()).transpose() | 
| 185 |         } else { | 
| 186 |             Ok(None) | 
| 187 |         } | 
| 188 |     } | 
| 189 |  | 
| 190 |     /// Try to parse this command as a [`macho::DysymtabCommand`]. | 
| 191 |     pub fn dysymtab(self) -> Result<Option<&'data macho::DysymtabCommand<E>>> { | 
| 192 |         if self.cmd == macho::LC_DYSYMTAB { | 
| 193 |             Some(self.data()).transpose() | 
| 194 |         } else { | 
| 195 |             Ok(None) | 
| 196 |         } | 
| 197 |     } | 
| 198 |  | 
| 199 |     /// Try to parse this command as a [`macho::DylibCommand`]. | 
| 200 |     pub fn dylib(self) -> Result<Option<&'data macho::DylibCommand<E>>> { | 
| 201 |         if self.cmd == macho::LC_LOAD_DYLIB | 
| 202 |             || self.cmd == macho::LC_LOAD_WEAK_DYLIB | 
| 203 |             || self.cmd == macho::LC_REEXPORT_DYLIB | 
| 204 |             || self.cmd == macho::LC_LAZY_LOAD_DYLIB | 
| 205 |             || self.cmd == macho::LC_LOAD_UPWARD_DYLIB | 
| 206 |         { | 
| 207 |             Some(self.data()).transpose() | 
| 208 |         } else { | 
| 209 |             Ok(None) | 
| 210 |         } | 
| 211 |     } | 
| 212 |  | 
| 213 |     /// Try to parse this command as a [`macho::UuidCommand`]. | 
| 214 |     pub fn uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>> { | 
| 215 |         if self.cmd == macho::LC_UUID { | 
| 216 |             Some(self.data()).transpose() | 
| 217 |         } else { | 
| 218 |             Ok(None) | 
| 219 |         } | 
| 220 |     } | 
| 221 |  | 
| 222 |     /// Try to parse this command as a [`macho::SegmentCommand64`]. | 
| 223 |     pub fn segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, &'data [u8])>> { | 
| 224 |         if self.cmd == macho::LC_SEGMENT_64 { | 
| 225 |             let mut data = self.data; | 
| 226 |             let command = data.read().read_error("Invalid Mach-O command size" )?; | 
| 227 |             Ok(Some((command, data.0))) | 
| 228 |         } else { | 
| 229 |             Ok(None) | 
| 230 |         } | 
| 231 |     } | 
| 232 |  | 
| 233 |     /// Try to parse this command as a [`macho::DyldInfoCommand`]. | 
| 234 |     pub fn dyld_info(self) -> Result<Option<&'data macho::DyldInfoCommand<E>>> { | 
| 235 |         if self.cmd == macho::LC_DYLD_INFO || self.cmd == macho::LC_DYLD_INFO_ONLY { | 
| 236 |             Some(self.data()).transpose() | 
| 237 |         } else { | 
| 238 |             Ok(None) | 
| 239 |         } | 
| 240 |     } | 
| 241 |  | 
| 242 |     /// Try to parse this command as an [`macho::EntryPointCommand`]. | 
| 243 |     pub fn entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>> { | 
| 244 |         if self.cmd == macho::LC_MAIN { | 
| 245 |             Some(self.data()).transpose() | 
| 246 |         } else { | 
| 247 |             Ok(None) | 
| 248 |         } | 
| 249 |     } | 
| 250 |  | 
| 251 |     /// Try to parse this command as a [`macho::BuildVersionCommand`]. | 
| 252 |     pub fn build_version(self) -> Result<Option<&'data macho::BuildVersionCommand<E>>> { | 
| 253 |         if self.cmd == macho::LC_BUILD_VERSION { | 
| 254 |             Some(self.data()).transpose() | 
| 255 |         } else { | 
| 256 |             Ok(None) | 
| 257 |         } | 
| 258 |     } | 
| 259 | } | 
| 260 |  | 
| 261 | /// A [`macho::LoadCommand`] that has been interpreted according to its `cmd` field. | 
| 262 | #[derive (Debug, Clone, Copy)] | 
| 263 | #[non_exhaustive ] | 
| 264 | pub enum LoadCommandVariant<'data, E: Endian> { | 
| 265 |     /// `LC_SEGMENT` | 
| 266 |     Segment32(&'data macho::SegmentCommand32<E>, &'data [u8]), | 
| 267 |     /// `LC_SYMTAB` | 
| 268 |     Symtab(&'data macho::SymtabCommand<E>), | 
| 269 |     // obsolete: `LC_SYMSEG` | 
| 270 |     //Symseg(&'data macho::SymsegCommand<E>), | 
| 271 |     /// `LC_THREAD` or `LC_UNIXTHREAD` | 
| 272 |     Thread(&'data macho::ThreadCommand<E>, &'data [u8]), | 
| 273 |     // obsolete: `LC_IDFVMLIB` or `LC_LOADFVMLIB` | 
| 274 |     //Fvmlib(&'data macho::FvmlibCommand<E>), | 
| 275 |     // obsolete: `LC_IDENT` | 
| 276 |     //Ident(&'data macho::IdentCommand<E>), | 
| 277 |     // internal: `LC_FVMFILE` | 
| 278 |     //Fvmfile(&'data macho::FvmfileCommand<E>), | 
| 279 |     // internal: `LC_PREPAGE` | 
| 280 |     /// `LC_DYSYMTAB` | 
| 281 |     Dysymtab(&'data macho::DysymtabCommand<E>), | 
| 282 |     /// `LC_LOAD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_REEXPORT_DYLIB`, | 
| 283 |     /// `LC_LAZY_LOAD_DYLIB`, or `LC_LOAD_UPWARD_DYLIB` | 
| 284 |     Dylib(&'data macho::DylibCommand<E>), | 
| 285 |     /// `LC_ID_DYLIB` | 
| 286 |     IdDylib(&'data macho::DylibCommand<E>), | 
| 287 |     /// `LC_LOAD_DYLINKER` | 
| 288 |     LoadDylinker(&'data macho::DylinkerCommand<E>), | 
| 289 |     /// `LC_ID_DYLINKER` | 
| 290 |     IdDylinker(&'data macho::DylinkerCommand<E>), | 
| 291 |     /// `LC_PREBOUND_DYLIB` | 
| 292 |     PreboundDylib(&'data macho::PreboundDylibCommand<E>), | 
| 293 |     /// `LC_ROUTINES` | 
| 294 |     Routines32(&'data macho::RoutinesCommand32<E>), | 
| 295 |     /// `LC_SUB_FRAMEWORK` | 
| 296 |     SubFramework(&'data macho::SubFrameworkCommand<E>), | 
| 297 |     /// `LC_SUB_UMBRELLA` | 
| 298 |     SubUmbrella(&'data macho::SubUmbrellaCommand<E>), | 
| 299 |     /// `LC_SUB_CLIENT` | 
| 300 |     SubClient(&'data macho::SubClientCommand<E>), | 
| 301 |     /// `LC_SUB_LIBRARY` | 
| 302 |     SubLibrary(&'data macho::SubLibraryCommand<E>), | 
| 303 |     /// `LC_TWOLEVEL_HINTS` | 
| 304 |     TwolevelHints(&'data macho::TwolevelHintsCommand<E>), | 
| 305 |     /// `LC_PREBIND_CKSUM` | 
| 306 |     PrebindCksum(&'data macho::PrebindCksumCommand<E>), | 
| 307 |     /// `LC_SEGMENT_64` | 
| 308 |     Segment64(&'data macho::SegmentCommand64<E>, &'data [u8]), | 
| 309 |     /// `LC_ROUTINES_64` | 
| 310 |     Routines64(&'data macho::RoutinesCommand64<E>), | 
| 311 |     /// `LC_UUID` | 
| 312 |     Uuid(&'data macho::UuidCommand<E>), | 
| 313 |     /// `LC_RPATH` | 
| 314 |     Rpath(&'data macho::RpathCommand<E>), | 
| 315 |     /// `LC_CODE_SIGNATURE`, `LC_SEGMENT_SPLIT_INFO`, `LC_FUNCTION_STARTS`, | 
| 316 |     /// `LC_DATA_IN_CODE`, `LC_DYLIB_CODE_SIGN_DRS`, `LC_LINKER_OPTIMIZATION_HINT`, | 
| 317 |     /// `LC_DYLD_EXPORTS_TRIE`, or `LC_DYLD_CHAINED_FIXUPS`. | 
| 318 |     LinkeditData(&'data macho::LinkeditDataCommand<E>), | 
| 319 |     /// `LC_ENCRYPTION_INFO` | 
| 320 |     EncryptionInfo32(&'data macho::EncryptionInfoCommand32<E>), | 
| 321 |     /// `LC_DYLD_INFO` or `LC_DYLD_INFO_ONLY` | 
| 322 |     DyldInfo(&'data macho::DyldInfoCommand<E>), | 
| 323 |     /// `LC_VERSION_MIN_MACOSX`, `LC_VERSION_MIN_IPHONEOS`, `LC_VERSION_MIN_WATCHOS`, | 
| 324 |     /// or `LC_VERSION_MIN_TVOS` | 
| 325 |     VersionMin(&'data macho::VersionMinCommand<E>), | 
| 326 |     /// `LC_DYLD_ENVIRONMENT` | 
| 327 |     DyldEnvironment(&'data macho::DylinkerCommand<E>), | 
| 328 |     /// `LC_MAIN` | 
| 329 |     EntryPoint(&'data macho::EntryPointCommand<E>), | 
| 330 |     /// `LC_SOURCE_VERSION` | 
| 331 |     SourceVersion(&'data macho::SourceVersionCommand<E>), | 
| 332 |     /// `LC_ENCRYPTION_INFO_64` | 
| 333 |     EncryptionInfo64(&'data macho::EncryptionInfoCommand64<E>), | 
| 334 |     /// `LC_LINKER_OPTION` | 
| 335 |     LinkerOption(&'data macho::LinkerOptionCommand<E>), | 
| 336 |     /// `LC_NOTE` | 
| 337 |     Note(&'data macho::NoteCommand<E>), | 
| 338 |     /// `LC_BUILD_VERSION` | 
| 339 |     BuildVersion(&'data macho::BuildVersionCommand<E>), | 
| 340 |     /// `LC_FILESET_ENTRY` | 
| 341 |     FilesetEntry(&'data macho::FilesetEntryCommand<E>), | 
| 342 |     /// An unrecognized or obsolete load command. | 
| 343 |     Other, | 
| 344 | } | 
| 345 |  | 
| 346 | impl<E: Endian> macho::SymtabCommand<E> { | 
| 347 |     /// Return the symbol table that this command references. | 
| 348 |     pub fn symbols<'data, Mach: MachHeader<Endian = E>, R: ReadRef<'data>>( | 
| 349 |         &self, | 
| 350 |         endian: E, | 
| 351 |         data: R, | 
| 352 |     ) -> Result<SymbolTable<'data, Mach, R>> { | 
| 353 |         let symbols: &'data [::Nlist] = dataResult<&[::Nlist], …> | 
| 354 |             .read_slice_at( | 
| 355 |                 self.symoff.get(endian).into(), | 
| 356 |                 self.nsyms.get(endian) as usize, | 
| 357 |             ) | 
| 358 |             .read_error("Invalid Mach-O symbol table offset or size" )?; | 
| 359 |         let str_start: u64 = self.stroff.get(endian).into(); | 
| 360 |         let str_end: u64 = str_startOption | 
| 361 |             .checked_add(self.strsize.get(endian).into()) | 
| 362 |             .read_error("Invalid Mach-O string table length" )?; | 
| 363 |         let strings: StringTable<'_, R> = StringTable::new(data, str_start, str_end); | 
| 364 |         Ok(SymbolTable::new(symbols, strings)) | 
| 365 |     } | 
| 366 | } | 
| 367 |  | 
| 368 | #[cfg (test)] | 
| 369 | mod tests { | 
| 370 |     use super::*; | 
| 371 |     use crate::LittleEndian; | 
| 372 |  | 
| 373 |     #[test ] | 
| 374 |     fn cmd_size_invalid() { | 
| 375 |         let mut commands = LoadCommandIterator::new(LittleEndian, &[0; 8], 10); | 
| 376 |         assert!(commands.next().is_err()); | 
| 377 |         let mut commands = LoadCommandIterator::new(LittleEndian, &[0, 0, 0, 0, 7, 0, 0, 0, 0], 10); | 
| 378 |         assert!(commands.next().is_err()); | 
| 379 |         let mut commands = LoadCommandIterator::new(LittleEndian, &[0, 0, 0, 0, 8, 0, 0, 0, 0], 10); | 
| 380 |         assert!(commands.next().is_ok()); | 
| 381 |     } | 
| 382 | } | 
| 383 |  |