diff --git a/crates/examples/src/bin/dyldcachedump.rs b/crates/examples/src/bin/dyldcachedump.rs index bb2df698..01974e35 100644 --- a/crates/examples/src/bin/dyldcachedump.rs +++ b/crates/examples/src/bin/dyldcachedump.rs @@ -1,4 +1,4 @@ -use object::read::macho::DyldCache; +use object::macho; use object::Endianness; use std::{env, fs, process}; @@ -29,8 +29,11 @@ fn main() { continue; } }; - let cache = match DyldCache::::parse(&*file) { - Ok(cache) => cache, + + // TODO: Convert this back to use DyldCache, and read subcaches first. + + let header = match macho::DyldCacheHeader::::parse(&*file) { + Ok(header) => header, Err(err) => { println!( "Failed to parse Dyld shared cache file '{}': {}", @@ -39,12 +42,28 @@ fn main() { continue; } }; + let (_arch, endian) = header.parse_magic().unwrap(); + let mappings = header.mappings(endian, &*file).unwrap(); + let images = header.images(endian, &*file).unwrap(); - // Print the list of image paths in this file. - for image in cache.images() { - if let Ok(path) = image.path() { - println!("{}", path); - } + println!("Mappings:"); + for mapping in mappings { + let start_address = mapping.address.get(endian); + let end_address = start_address.wrapping_add(mapping.size.get(endian)); + let file_offset = mapping.file_offset.get(endian); + println!( + "0x{:x}-0x{:x} at file offset 0x{:x}", + start_address, end_address, file_offset + ); + } + println!(); + println!("Images:"); + for image in images { + let address = image.address.get(endian); + let path = image.path(endian, &*file).unwrap(); + // The path should always be ascii, so from_utf8 should alway succeed. + let path = core::str::from_utf8(path).unwrap(); + println!("0x{:x} {}", address, path); } } } diff --git a/crates/examples/src/bin/objdump.rs b/crates/examples/src/bin/objdump.rs index c7f989b2..2c0a2836 100644 --- a/crates/examples/src/bin/objdump.rs +++ b/crates/examples/src/bin/objdump.rs @@ -18,6 +18,7 @@ fn main() { process::exit(1); } }; + let extra_files = open_subcaches_if_exist(&file_path); let file = match unsafe { memmap2::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { @@ -25,8 +26,47 @@ fn main() { process::exit(1); } }; + let extra_files: Vec<_> = extra_files + .into_iter() + .map( + |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } { + Ok(mmap) => mmap, + Err(err) => { + eprintln!("Failed to map file '{}': {}", file_path, err,); + process::exit(1); + } + }, + ) + .collect(); + let extra_file_data: Vec<&[u8]> = extra_files.iter().map(|f| &**f).collect(); let stdout = io::stdout(); let stderr = io::stderr(); - objdump::print(&mut stdout.lock(), &mut stderr.lock(), &*file, member_names).unwrap(); + objdump::print( + &mut stdout.lock(), + &mut stderr.lock(), + &*file, + &extra_file_data, + member_names, + ) + .unwrap(); +} + +// If the file is a dyld shared cache, and we're on macOS 12 or later, +// then there will be one or more "subcache" files next to this file, +// with the names filename.1, filename.2 etc. +// Read those files now, if they exist, even if we don't know that +// we're dealing with a dyld shared cache. By the time we know what +// we're dealing with, it's too late to read more files. +fn open_subcaches_if_exist(path: &str) -> Vec { + let mut files = Vec::new(); + for i in 1.. { + let subcache_path = format!("{}.{}", path, i); + match fs::File::open(&subcache_path) { + Ok(subcache_file) => files.push(subcache_file), + Err(_) => break, + }; + } + println!("have {} extra files", files.len()); + files } diff --git a/crates/examples/src/objdump.rs b/crates/examples/src/objdump.rs index e01b88eb..1dd672b6 100644 --- a/crates/examples/src/objdump.rs +++ b/crates/examples/src/objdump.rs @@ -7,6 +7,7 @@ pub fn print( w: &mut W, e: &mut E, file: &[u8], + extra_files: &[&[u8]], member_names: Vec, ) -> Result<()> { let mut member_names: Vec<_> = member_names.into_iter().map(|name| (name, false)).collect(); @@ -47,7 +48,7 @@ pub fn print( Err(err) => writeln!(e, "Failed to parse Fat 64 data: {}", err)?, } } - } else if let Ok(cache) = DyldCache::::parse(&*file) { + } else if let Ok(cache) = DyldCache::::parse(&*file, extra_files) { writeln!(w, "Format: dyld cache {:?}-endian", cache.endianness())?; writeln!(w, "Architecture: {:?}", cache.architecture())?; for image in cache.images() { diff --git a/crates/examples/tests/testfiles.rs b/crates/examples/tests/testfiles.rs index 6fe22ee0..8e854571 100644 --- a/crates/examples/tests/testfiles.rs +++ b/crates/examples/tests/testfiles.rs @@ -28,7 +28,7 @@ fn testfiles() { println!("File {}", path); let data = fs::read(&path).unwrap(); fail |= testfile(path, &data, "objdump", |mut out, mut err, data| { - objdump::print(&mut out, &mut err, data, vec![]).unwrap() + objdump::print(&mut out, &mut err, data, &[], vec![]).unwrap() }); fail |= testfile(path, &data, "readobj", readobj::print); println!(); diff --git a/src/macho.rs b/src/macho.rs index 9d81f7d6..6795826a 100644 --- a/src/macho.rs +++ b/src/macho.rs @@ -295,15 +295,33 @@ pub struct DyldCacheHeader { /// e.g. "dyld_v0 i386" pub magic: [u8; 16], /// file offset to first dyld_cache_mapping_info - pub mapping_offset: U32, + pub mapping_offset: U32, // offset: 0x10 /// number of dyld_cache_mapping_info entries - pub mapping_count: U32, + pub mapping_count: U32, // offset: 0x14 /// file offset to first dyld_cache_image_info - pub images_offset: U32, + pub images_offset: U32, // offset: 0x18 /// number of dyld_cache_image_info entries - pub images_count: U32, + pub images_count: U32, // offset: 0x1c /// base address of dyld when cache was built - pub dyld_base_address: U64, + pub dyld_base_address: U64, // offset: 0x20 + /// + reserved1: [u8; 48], // offset: 0x28 + /// unique value for each shared cache file + pub uuid: [u8; 16], // offset: 0x58 + /// + reserved2: [u8; 288], // offset: 0x68 + /// file offset to first dyld_subcache_info + pub subcaches_offset: U32, // offset: 0x188 + /// number of dyld_subcache_info entries + pub subcaches_count: U32, // offset: 0x18c + /// + reserved3: [u8; 48], // offset: 0x1a0 + /// file offset to first dyld_cache_image_info + /// Use this instead of images_offset if mapping_offset is at least 0x1c4. + pub images_across_all_subcaches_offset: U32, // offset: 0x1c0 + /// number of dyld_cache_image_info entries + /// Use this instead of images_count if mapping_offset is at least 0x1c4. + pub images_across_all_subcaches_count: U32, // offset: 0x1c4 } /// Corresponds to struct dyld_cache_mapping_info from dyld_cache_format.h. @@ -338,6 +356,17 @@ pub struct DyldCacheImageInfo { pub pad: U32, } +/// Corresponds to a struct whose source code has not been published as of Nov 2021. +/// Added in the dyld cache version which shipped with macOS 12 / iOS 15. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldSubCacheInfo { + /// The UUID of this subcache. + pub uuid: [u8; 16], + /// + pub unknown: U64, +} + // Definitions from "/usr/include/mach-o/loader.h". /* @@ -3199,6 +3228,7 @@ unsafe_impl_endian_pod!( DyldCacheHeader, DyldCacheMappingInfo, DyldCacheImageInfo, + DyldSubCacheInfo, MachHeader32, MachHeader64, LoadCommand, diff --git a/src/read/any.rs b/src/read/any.rs index 940cf278..e014500d 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -20,7 +20,7 @@ use crate::read::{ SymbolMapName, SymbolScope, SymbolSection, }; #[allow(unused_imports)] -use crate::Endianness; +use crate::{AddressSize, Endian, Endianness}; /// Evaluate an expression on the contents of a file format enum. /// @@ -220,23 +220,21 @@ impl<'data, R: ReadRef<'data>> File<'data, R> { Ok(File { inner }) } - /// Parse the raw file data at an arbitrary offset inside the input data. - /// - /// Currently, this is only supported for Mach-O images. - /// This can be used for parsing Mach-O images inside the dyld shared cache, - /// where multiple images, located at different offsets, share the same address - /// space. - pub fn parse_at(data: R, offset: u64) -> Result { - let _inner = match FileKind::parse_at(data, offset)? { - #[cfg(feature = "macho")] - FileKind::MachO32 => FileInternal::MachO32(macho::MachOFile32::parse_at(data, offset)?), - #[cfg(feature = "macho")] - FileKind::MachO64 => FileInternal::MachO64(macho::MachOFile64::parse_at(data, offset)?), - #[allow(unreachable_patterns)] + /// Parse a Mach-O image from the dyld shared cache. + #[cfg(feature = "macho")] + pub fn parse_dyld_cache_image<'cache, E: Endian>( + image: &macho::DyldCacheImage<'data, 'cache, E, R>, + ) -> Result { + let inner = match image.cache.architecture().address_size() { + Some(AddressSize::U64) => { + FileInternal::MachO64(macho::MachOFile64::parse_dyld_cache_image(image)?) + } + Some(AddressSize::U32) => { + FileInternal::MachO32(macho::MachOFile32::parse_dyld_cache_image(image)?) + } _ => return Err(Error("Unsupported file format")), }; - #[allow(unreachable_code)] - Ok(File { inner: _inner }) + Ok(File { inner }) } /// Return the file format. @@ -501,9 +499,9 @@ where #[cfg(feature = "elf")] Elf64(elf::ElfSegment64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] - MachO32(macho::MachOSegment32<'data, 'file, Endianness, R>), + MachO32(macho::MachOSegment32<'data, Endianness, R>), #[cfg(feature = "macho")] - MachO64(macho::MachOSegment64<'data, 'file, Endianness, R>), + MachO64(macho::MachOSegment64<'data, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeSegment32<'data, 'file, R>), #[cfg(feature = "pe")] diff --git a/src/read/macho/dyld_cache.rs b/src/read/macho/dyld_cache.rs index ee758ce0..6798e219 100644 --- a/src/read/macho/dyld_cache.rs +++ b/src/read/macho/dyld_cache.rs @@ -1,5 +1,7 @@ use core::slice; +use alloc::vec::Vec; + use crate::read::{Error, File, ReadError, ReadRef, Result}; use crate::{macho, Architecture, Endian, Endianness}; @@ -12,26 +14,58 @@ where { endian: E, data: R, + subcaches: Vec>, header: &'data macho::DyldCacheHeader, mappings: &'data [macho::DyldCacheMappingInfo], images: &'data [macho::DyldCacheImageInfo], arch: Architecture, } +/// Information about a subcache. +#[derive(Debug)] +pub struct DyldSubCache<'data, E = Endianness, R = &'data [u8]> +where + E: Endian, + R: ReadRef<'data>, +{ + data: R, + mappings: &'data [macho::DyldCacheMappingInfo], +} + +// This is the offset of the images_across_all_subcaches_count field. +const MIN_HEADER_SIZE_SUBCACHES: u32 = 0x1c4; + impl<'data, E, R> DyldCache<'data, E, R> where E: Endian, R: ReadRef<'data>, { /// Parse the raw dyld shared cache data. - pub fn parse(data: R) -> Result { + pub fn parse(data: R, subcache_data: &[R]) -> Result { let header = macho::DyldCacheHeader::parse(data)?; let (arch, endian) = header.parse_magic()?; let mappings = header.mappings(endian, data)?; + + let mut subcaches = Vec::new(); + if let Some(subcaches_info) = header.subcaches(endian, data)? { + if subcache_data.len() != subcaches_info.len() { + return Err(Error("Incorrect number of SubCaches")); + } + + for (&data, info) in subcache_data.iter().zip(subcaches_info.iter()) { + let sc_header = macho::DyldCacheHeader::::parse(data)?; + if sc_header.uuid != info.uuid { + return Err(Error("Unexpected SubCache UUID")); + } + let mappings = sc_header.mappings(endian, data)?; + subcaches.push(DyldSubCache { data, mappings }); + } + } let images = header.images(endian, data)?; Ok(DyldCache { endian, data, + subcaches, header, mappings, images, @@ -66,6 +100,22 @@ where iter: self.images.iter(), } } + + /// Find the address in a mapping and return the cache or subcache data it was found in, + /// together with the translated file offset. + pub fn data_and_offset_for_address(&self, address: u64) -> Option<(R, u64)> { + if let Some(file_offset) = address_to_file_offset(address, self.endian, self.mappings) { + return Some((self.data, file_offset)); + } + for subcache in &self.subcaches { + if let Some(file_offset) = + address_to_file_offset(address, self.endian, subcache.mappings) + { + return Some((subcache.data, file_offset)); + } + } + None + } } /// An iterator over all the images (dylibs) in the dyld shared cache. @@ -84,14 +134,12 @@ where E: Endian, R: ReadRef<'data>, { - type Item = DyldCacheImage<'data, E, R>; + type Item = DyldCacheImage<'data, 'cache, E, R>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { let image_info = self.iter.next()?; Some(DyldCacheImage { - endian: self.cache.endian, - data: self.cache.data, - mappings: self.cache.mappings, + cache: self.cache, image_info, }) } @@ -99,38 +147,39 @@ where /// One image (dylib) from inside the dyld shared cache. #[derive(Debug)] -pub struct DyldCacheImage<'data, E = Endianness, R = &'data [u8]> +pub struct DyldCacheImage<'data, 'cache, E = Endianness, R = &'data [u8]> where E: Endian, R: ReadRef<'data>, { - endian: E, - data: R, - mappings: &'data [macho::DyldCacheMappingInfo], + pub(crate) cache: &'cache DyldCache<'data, E, R>, image_info: &'data macho::DyldCacheImageInfo, } -impl<'data, E, R> DyldCacheImage<'data, E, R> +impl<'data, 'cache, E, R> DyldCacheImage<'data, 'cache, E, R> where E: Endian, R: ReadRef<'data>, { /// The file system path of this image. pub fn path(&self) -> Result<&'data str> { - let path = self.image_info.path(self.endian, self.data)?; + let path = self.image_info.path(self.cache.endian, self.cache.data)?; // The path should always be ascii, so from_utf8 should alway succeed. let path = core::str::from_utf8(path).map_err(|_| Error("Path string not valid utf-8"))?; Ok(path) } /// The offset in the dyld cache file where this image starts. - pub fn file_offset(&self) -> Result { - self.image_info.file_offset(self.endian, self.mappings) + pub fn image_data_and_offset(&self) -> Result<(R, u64)> { + let address = self.image_info.address.get(self.cache.endian); + self.cache + .data_and_offset_for_address(address) + .ok_or(Error("Address not found in any mapping")) } /// Parse this image into an Object. pub fn parse_object(&self) -> Result> { - File::parse_at(self.data, self.file_offset()?) + File::parse_dyld_cache_image(self) } } @@ -175,17 +224,44 @@ impl macho::DyldCacheHeader { .read_error("Invalid dyld cache mapping size or alignment") } + /// Return the information about subcaches, if present. + pub fn subcaches<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result]>> { + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES { + let subcaches = data + .read_slice_at::>( + self.subcaches_offset.get(endian).into(), + self.subcaches_count.get(endian) as usize, + ) + .read_error("Invalid dyld subcaches size or alignment")?; + Ok(Some(subcaches)) + } else { + Ok(None) + } + } + /// Return the image information table. pub fn images<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result<&'data [macho::DyldCacheImageInfo]> { - data.read_slice_at::>( - self.images_offset.get(endian).into(), - self.images_count.get(endian) as usize, - ) - .read_error("Invalid dyld cache image size or alignment") + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES { + data.read_slice_at::>( + self.images_across_all_subcaches_offset.get(endian).into(), + self.images_across_all_subcaches_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache image size or alignment") + } else { + data.read_slice_at::>( + self.images_offset.get(endian).into(), + self.images_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache image size or alignment") + } } } @@ -205,14 +281,26 @@ impl macho::DyldCacheImageInfo { mappings: &[macho::DyldCacheMappingInfo], ) -> Result { let address = self.address.get(endian); - for mapping in mappings { - let mapping_address = mapping.address.get(endian); - if address >= mapping_address - && address < mapping_address.wrapping_add(mapping.size.get(endian)) - { - return Ok(address - mapping_address + mapping.file_offset.get(endian)); - } + match address_to_file_offset(address, endian, mappings) { + Some(file_offset) => Ok(file_offset), + None => Err(Error("Invalid dyld cache image address")), + } + } +} + +/// Find the file offset of the image by looking up its address in the mappings. +pub fn address_to_file_offset( + address: u64, + endian: E, + mappings: &[macho::DyldCacheMappingInfo], +) -> Option { + for mapping in mappings { + let mapping_address = mapping.address.get(endian); + if address >= mapping_address + && address < mapping_address.wrapping_add(mapping.size.get(endian)) + { + return Some(address - mapping_address + mapping.file_offset.get(endian)); } - Err(Error("Invalid dyld cache image address")) } + None } diff --git a/src/read/macho/file.rs b/src/read/macho/file.rs index 0d4961b1..ff2410ca 100644 --- a/src/read/macho/file.rs +++ b/src/read/macho/file.rs @@ -10,9 +10,9 @@ use crate::read::{ use crate::{endian, macho, BigEndian, ByteString, Endian, Endianness, Pod}; use super::{ - LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator, MachOSegment, - MachOSegmentIterator, MachOSymbol, MachOSymbolIterator, MachOSymbolTable, Nlist, Section, - Segment, SymbolTable, + DyldCacheImage, LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator, + MachOSegment, MachOSegmentIterator, MachOSymbol, MachOSymbolIterator, MachOSymbolTable, Nlist, + Section, Segment, SymbolTable, }; /// A 32-bit Mach-O object file. @@ -35,6 +35,7 @@ where pub(super) data: R, pub(super) header_offset: u64, pub(super) header: &'data Mach, + pub(super) segments: Vec>, pub(super) sections: Vec>, pub(super) symbols: SymbolTable<'data, Mach, R>, } @@ -46,38 +47,98 @@ where { /// Parse the raw Mach-O file data. pub fn parse(data: R) -> Result { - Self::parse_at(data, 0) + let header = Mach::parse(data, 0)?; + let endian = header.endian()?; + + // Build a list of sections to make some operations more efficient. + let mut segments = Vec::new(); + let mut sections = Vec::new(); + let mut symtab = None; + if let Ok(mut commands) = header.load_commands(endian, data, 0) { + while let Ok(Some(command)) = commands.next() { + if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { + segments.push(MachOSegment { + segment, + data, + endian, + }); + for section in segment.sections(endian, section_data)? { + let index = SectionIndex(sections.len() + 1); + sections.push(MachOSectionInternal::parse(index, section)); + } + } else if let Some(st) = command.symtab()? { + symtab = Some(st); + } + } + } + + let symbols = match symtab { + Some(symtab) => symtab.symbols(endian, data)?, + None => SymbolTable::default(), + }; + + Ok(MachOFile { + endian, + data, + header_offset: 0, + header, + segments, + sections, + symbols, + }) } /// Parse the raw Mach-O file data at an arbitrary offset inside the input data. /// This can be used for parsing Mach-O images inside the dyld shared cache, /// where multiple images, located at different offsets, share the same address /// space. - pub fn parse_at(data: R, header_offset: u64) -> Result { + pub fn parse_dyld_cache_image<'cache, E: Endian>( + image: &DyldCacheImage<'data, 'cache, E, R>, + ) -> Result { + let (data, header_offset) = image.image_data_and_offset()?; let header = Mach::parse(data, header_offset)?; let endian = header.endian()?; - let mut symbols = SymbolTable::default(); // Build a list of sections to make some operations more efficient. + let mut segments = Vec::new(); let mut sections = Vec::new(); + let mut linkedit_data: Option = None; + let mut symtab = None; if let Ok(mut commands) = header.load_commands(endian, data, header_offset) { while let Ok(Some(command)) = commands.next() { if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { + let addr = segment.vmaddr(endian).into(); + if let Some((data, _offset)) = image.cache.data_and_offset_for_address(addr) { + if segment.name() == macho::SEG_LINKEDIT.as_bytes() { + linkedit_data = Some(data); + } + segments.push(MachOSegment { + segment, + data, + endian, + }); + } for section in segment.sections(endian, section_data)? { let index = SectionIndex(sections.len() + 1); sections.push(MachOSectionInternal::parse(index, section)); } - } else if let Some(symtab) = command.symtab()? { - symbols = symtab.symbols(endian, data)?; + } else if let Some(st) = command.symtab()? { + symtab = Some(st); } } } + let symbols = match (symtab, linkedit_data) { + (Some(symtab), Some(linkedit_data)) => symtab.symbols(endian, linkedit_data)?, + _ => SymbolTable::default(), + }; + Ok(MachOFile { endian, data, header_offset, header, + segments, sections, symbols, }) @@ -95,6 +156,12 @@ where .and_then(|index| self.sections.get(index)) .read_error("Invalid Mach-O section index") } + + pub(super) fn segment_by_name_bytes(&self, segment_name: &[u8]) -> Option<&MachOSegment<'data, Mach, R>> { + self.segments + .iter() + .find(|segment| segment.segment.segname() == segment_name) + } } impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R> @@ -110,7 +177,7 @@ where Mach: MachHeader, R: 'file + ReadRef<'data>, { - type Segment = MachOSegment<'data, 'file, Mach, R>; + type Segment = MachOSegment<'data, Mach, R>; type SegmentIterator = MachOSegmentIterator<'data, 'file, Mach, R>; type Section = MachOSection<'data, 'file, Mach, R>; type SectionIterator = MachOSectionIterator<'data, 'file, Mach, R>; @@ -154,12 +221,7 @@ where fn segments(&'file self) -> MachOSegmentIterator<'data, 'file, Mach, R> { MachOSegmentIterator { - file: self, - commands: self - .header - .load_commands(self.endian, self.data, self.header_offset) - .ok() - .unwrap_or_else(Default::default), + iter: self.segments.iter(), } } diff --git a/src/read/macho/section.rs b/src/read/macho/section.rs index 3a5a22eb..7b8f87b5 100644 --- a/src/read/macho/section.rs +++ b/src/read/macho/section.rs @@ -80,9 +80,14 @@ where R: ReadRef<'data>, { fn bytes(&self) -> Result<&'data [u8]> { + let segment_name = self.internal.section.segment_name(); + let segment = self + .file + .segment_by_name_bytes(segment_name) + .read_error("Invalid segment name on section")?; self.internal .section - .data(self.file.endian, self.file.data) + .data(self.file.endian, segment.data) .read_error("Invalid Mach-O section size or offset") } } diff --git a/src/read/macho/segment.rs b/src/read/macho/segment.rs index 3c2d9649..7a6a520d 100644 --- a/src/read/macho/segment.rs +++ b/src/read/macho/segment.rs @@ -1,12 +1,12 @@ use core::fmt::Debug; -use core::{result, str}; +use core::{result, slice, str}; use crate::endian::{self, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::{self, ObjectSegment, ReadError, ReadRef, Result}; -use super::{LoadCommandData, LoadCommandIterator, MachHeader, MachOFile, Section}; +use super::{LoadCommandData, MachHeader, Section}; /// An iterator over the segments of a `MachOFile32`. pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = @@ -23,8 +23,7 @@ where Mach: MachHeader, R: ReadRef<'data>, { - pub(super) file: &'file MachOFile<'data, Mach, R>, - pub(super) commands: LoadCommandIterator<'data, Mach::Endian>, + pub(super) iter: slice::Iter<'file, MachOSegment<'data, Mach, R>>, } impl<'data, 'file, Mach, R> Iterator for MachOSegmentIterator<'data, 'file, Mach, R> @@ -32,72 +31,64 @@ where Mach: MachHeader, R: ReadRef<'data>, { - type Item = MachOSegment<'data, 'file, Mach, R>; + type Item = MachOSegment<'data, Mach, R>; fn next(&mut self) -> Option { - loop { - let command = self.commands.next().ok()??; - if let Ok(Some((segment, _))) = Mach::Segment::from_command(command) { - return Some(MachOSegment { - file: self.file, - segment, - }); - } - } + self.iter.next().cloned() } } /// A segment of a `MachOFile32`. -pub type MachOSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = - MachOSegment<'data, 'file, macho::MachHeader32, R>; +pub type MachOSegment32<'data, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, macho::MachHeader32, R>; /// A segment of a `MachOFile64`. -pub type MachOSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = - MachOSegment<'data, 'file, macho::MachHeader64, R>; +pub type MachOSegment64<'data, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, macho::MachHeader64, R>; /// A segment of a `MachOFile`. -#[derive(Debug)] -pub struct MachOSegment<'data, 'file, Mach, R = &'data [u8]> +#[derive(Debug, Clone)] +pub struct MachOSegment<'data, Mach, R = &'data [u8]> where - 'data: 'file, Mach: MachHeader, R: ReadRef<'data>, { - file: &'file MachOFile<'data, Mach, R>, - segment: &'data Mach::Segment, + pub(super) segment: &'data Mach::Segment, + pub(super) data: R, + pub(super) endian: Mach::Endian, } -impl<'data, 'file, Mach, R> MachOSegment<'data, 'file, Mach, R> +impl<'data, Mach, R> MachOSegment<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { fn bytes(&self) -> Result<&'data [u8]> { self.segment - .data(self.file.endian, self.file.data) + .data(self.endian, self.data) .read_error("Invalid Mach-O segment size or offset") } } -impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R> +impl<'data, Mach, R> read::private::Sealed for MachOSegment<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } -impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R> +impl<'data, Mach, R> ObjectSegment<'data> for MachOSegment<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { #[inline] fn address(&self) -> u64 { - self.segment.vmaddr(self.file.endian).into() + self.segment.vmaddr(self.endian).into() } #[inline] fn size(&self) -> u64 { - self.segment.vmsize(self.file.endian).into() + self.segment.vmsize(self.endian).into() } #[inline] @@ -108,7 +99,7 @@ where #[inline] fn file_range(&self) -> (u64, u64) { - self.segment.file_range(self.file.endian) + self.segment.file_range(self.endian) } fn data(&self) -> Result<&'data [u8]> {