Skip to main content

object/read/macho/
file.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3use core::{mem, str};
4
5use crate::endian::{self, BigEndian, Endian, Endianness, NativeEndian};
6use crate::pod::Pod;
7use crate::read::{
8    self, Architecture, ByteString, ComdatKind, Error, Export, FileFlags, Import,
9    NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectMap, ObjectSection,
10    ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex,
11};
12use crate::{macho, SkipDebugList};
13
14use super::{
15    DyldCacheImage, LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator,
16    MachOSegment, MachOSegmentInternal, MachOSegmentIterator, MachOSymbol, MachOSymbolIterator,
17    MachOSymbolTable, Nlist, Section, Segment, SymbolTable,
18};
19
20/// A 32-bit Mach-O object file.
21///
22/// This is a file that starts with [`macho::MachHeader32`], and corresponds
23/// to [`crate::FileKind::MachO32`].
24pub type MachOFile32<'data, Endian = Endianness, R = &'data [u8]> =
25    MachOFile<'data, macho::MachHeader32<Endian>, R>;
26
27/// A 64-bit Mach-O object file.
28///
29/// This is a file that starts with [`macho::MachHeader64`], and corresponds
30/// to [`crate::FileKind::MachO64`].
31pub type MachOFile64<'data, Endian = Endianness, R = &'data [u8]> =
32    MachOFile<'data, macho::MachHeader64<Endian>, R>;
33
34/// The Mach-O file format that matches the pointer width and endianness of the target platform.
35#[cfg(target_pointer_width = "32")]
36pub type NativeMachOFile<'data, R = &'data [u8]> = MachOFile32<'data, NativeEndian, R>;
37
38/// The Mach-O file format that matches the pointer width and endianness of the target platform.
39#[cfg(target_pointer_width = "64")]
40pub type NativeMachOFile<'data, R = &'data [u8]> = MachOFile64<'data, NativeEndian, R>;
41
42/// A partially parsed Mach-O file.
43///
44/// Most of the functionality of this type is provided by the [`Object`] trait implementation.
45#[derive(Debug)]
46pub struct MachOFile<'data, Mach, R = &'data [u8]>
47where
48    Mach: MachHeader,
49    R: ReadRef<'data>,
50{
51    pub(super) endian: Mach::Endian,
52    pub(super) data: SkipDebugList<R>,
53    pub(super) header_offset: u64,
54    pub(super) header: &'data Mach,
55    pub(super) segments: Vec<MachOSegmentInternal<'data, Mach, R>>,
56    pub(super) sections: Vec<MachOSectionInternal<'data, Mach, R>>,
57    pub(super) symbols: SymbolTable<'data, Mach, R>,
58}
59
60impl<'data, Mach, R> MachOFile<'data, Mach, R>
61where
62    Mach: MachHeader,
63    R: ReadRef<'data>,
64{
65    /// Parse the raw Mach-O file data.
66    pub fn parse(data: R) -> Result<Self> {
67        let header = Mach::parse(data, 0)?;
68        let endian = header.endian()?;
69
70        // Build a list of segments and sections to make some operations more efficient.
71        let mut segments = Vec::new();
72        let mut sections = Vec::new();
73        let mut symbols = SymbolTable::default();
74        if let Ok(mut commands) = header.load_commands(endian, data, 0) {
75            while let Ok(Some(command)) = commands.next() {
76                if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
77                    segments.push(MachOSegmentInternal { segment, data });
78                    for section in segment.sections(endian, section_data)? {
79                        let index = SectionIndex(sections.len() + 1);
80                        sections.push(MachOSectionInternal::parse(index, section, data));
81                    }
82                } else if let Some(symtab) = command.symtab()? {
83                    symbols = symtab.symbols(endian, data)?;
84                }
85            }
86        }
87
88        Ok(MachOFile {
89            endian,
90            data: SkipDebugList(data),
91            header_offset: 0,
92            header,
93            segments,
94            sections,
95            symbols,
96        })
97    }
98
99    /// Parse the Mach-O file for the given image from the dyld shared cache.
100    /// This will read different sections from different subcaches, if necessary.
101    pub fn parse_dyld_cache_image<'cache, E: Endian>(
102        image: &DyldCacheImage<'data, 'cache, E, R>,
103    ) -> Result<Self> {
104        let (data, header_offset) = image.image_data_and_offset()?;
105        let header = Mach::parse(data, header_offset)?;
106        let endian = header.endian()?;
107
108        // Build a list of sections to make some operations more efficient.
109        // Also build a list of segments, because we need to remember which ReadRef
110        // to read each section's data from. Only the DyldCache knows this information,
111        // and we won't have access to it once we've exited this function.
112        let mut segments = Vec::new();
113        let mut sections = Vec::new();
114        let mut linkedit_data: Option<R> = None;
115        let mut symtab = None;
116        if let Ok(mut commands) = header.load_commands(endian, data, header_offset) {
117            while let Ok(Some(command)) = commands.next() {
118                if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
119                    // Each segment can be stored in a different subcache. Get the segment's
120                    // address and look it up in the cache mappings, to find the correct cache data.
121                    // This was observed for the arm64e __LINKEDIT segment in macOS 12.0.1.
122                    let addr = segment.vmaddr(endian).into();
123                    let (data, _offset) = image
124                        .cache
125                        .data_and_offset_for_address(addr)
126                        .read_error("Could not find segment data in dyld shared cache")?;
127                    if segment.name() == macho::SEG_LINKEDIT.as_bytes() {
128                        linkedit_data = Some(data);
129                    }
130                    segments.push(MachOSegmentInternal { segment, data });
131
132                    for section in segment.sections(endian, section_data)? {
133                        let index = SectionIndex(sections.len() + 1);
134                        sections.push(MachOSectionInternal::parse(index, section, data));
135                    }
136                } else if let Some(st) = command.symtab()? {
137                    symtab = Some(st);
138                }
139            }
140        }
141
142        // The symbols are found in the __LINKEDIT segment, so make sure to read them from the
143        // correct subcache.
144        let symbols = match (symtab, linkedit_data) {
145            (Some(symtab), Some(linkedit_data)) => symtab.symbols(endian, linkedit_data)?,
146            _ => SymbolTable::default(),
147        };
148
149        Ok(MachOFile {
150            endian,
151            data: SkipDebugList(data),
152            header_offset,
153            header,
154            segments,
155            sections,
156            symbols,
157        })
158    }
159
160    /// Return the section at the given index.
161    #[inline]
162    pub(super) fn section_internal(
163        &self,
164        index: SectionIndex,
165    ) -> Result<&MachOSectionInternal<'data, Mach, R>> {
166        index
167            .0
168            .checked_sub(1)
169            .and_then(|index| self.sections.get(index))
170            .read_error("Invalid Mach-O section index")
171    }
172
173    /// Returns the endianness.
174    pub fn endian(&self) -> Mach::Endian {
175        self.endian
176    }
177
178    /// Returns the raw data.
179    pub fn data(&self) -> R {
180        self.data.0
181    }
182
183    /// Returns the raw Mach-O file header.
184    #[deprecated(note = "Use `macho_header` instead")]
185    pub fn raw_header(&self) -> &'data Mach {
186        self.header
187    }
188
189    /// Get the raw Mach-O file header.
190    pub fn macho_header(&self) -> &'data Mach {
191        self.header
192    }
193
194    /// Get the Mach-O load commands.
195    pub fn macho_load_commands(&self) -> Result<LoadCommandIterator<'data, Mach::Endian>> {
196        self.header
197            .load_commands(self.endian, self.data.0, self.header_offset)
198    }
199
200    /// Get the Mach-O symbol table.
201    ///
202    /// Returns an empty symbol table if the file has no symbol table.
203    pub fn macho_symbol_table(&self) -> &SymbolTable<'data, Mach, R> {
204        &self.symbols
205    }
206
207    /// Return the `LC_BUILD_VERSION` load command if present.
208    pub fn build_version(&self) -> Result<Option<&'data macho::BuildVersionCommand<Mach::Endian>>> {
209        let mut commands =
210            self.header
211                .load_commands(self.endian, self.data.0, self.header_offset)?;
212        while let Some(command) = commands.next()? {
213            if let Some(build_version) = command.build_version()? {
214                return Ok(Some(build_version));
215            }
216        }
217        Ok(None)
218    }
219}
220
221impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R>
222where
223    Mach: MachHeader,
224    R: ReadRef<'data>,
225{
226}
227
228impl<'data, Mach, R> Object<'data> for MachOFile<'data, Mach, R>
229where
230    Mach: MachHeader,
231    R: ReadRef<'data>,
232{
233    type Segment<'file>
234        = MachOSegment<'data, 'file, Mach, R>
235    where
236        Self: 'file,
237        'data: 'file;
238    type SegmentIterator<'file>
239        = MachOSegmentIterator<'data, 'file, Mach, R>
240    where
241        Self: 'file,
242        'data: 'file;
243    type Section<'file>
244        = MachOSection<'data, 'file, Mach, R>
245    where
246        Self: 'file,
247        'data: 'file;
248    type SectionIterator<'file>
249        = MachOSectionIterator<'data, 'file, Mach, R>
250    where
251        Self: 'file,
252        'data: 'file;
253    type Comdat<'file>
254        = MachOComdat<'data, 'file, Mach, R>
255    where
256        Self: 'file,
257        'data: 'file;
258    type ComdatIterator<'file>
259        = MachOComdatIterator<'data, 'file, Mach, R>
260    where
261        Self: 'file,
262        'data: 'file;
263    type Symbol<'file>
264        = MachOSymbol<'data, 'file, Mach, R>
265    where
266        Self: 'file,
267        'data: 'file;
268    type SymbolIterator<'file>
269        = MachOSymbolIterator<'data, 'file, Mach, R>
270    where
271        Self: 'file,
272        'data: 'file;
273    type SymbolTable<'file>
274        = MachOSymbolTable<'data, 'file, Mach, R>
275    where
276        Self: 'file,
277        'data: 'file;
278    type DynamicRelocationIterator<'file>
279        = NoDynamicRelocationIterator
280    where
281        Self: 'file,
282        'data: 'file;
283
284    fn architecture(&self) -> Architecture {
285        match self.header.cputype(self.endian) {
286            macho::CPU_TYPE_ARM => Architecture::Arm,
287            macho::CPU_TYPE_ARM64 => Architecture::Aarch64,
288            macho::CPU_TYPE_ARM64_32 => Architecture::Aarch64_Ilp32,
289            macho::CPU_TYPE_X86 => Architecture::I386,
290            macho::CPU_TYPE_X86_64 => Architecture::X86_64,
291            macho::CPU_TYPE_MIPS => Architecture::Mips,
292            macho::CPU_TYPE_POWERPC => Architecture::PowerPc,
293            macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64,
294            _ => Architecture::Unknown,
295        }
296    }
297
298    fn sub_architecture(&self) -> Option<SubArchitecture> {
299        match (
300            self.header.cputype(self.endian),
301            self.header.cpusubtype(self.endian),
302        ) {
303            (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) => Some(SubArchitecture::Arm64E),
304            _ => None,
305        }
306    }
307
308    #[inline]
309    fn is_little_endian(&self) -> bool {
310        self.header.is_little_endian()
311    }
312
313    #[inline]
314    fn is_64(&self) -> bool {
315        self.header.is_type_64()
316    }
317
318    fn kind(&self) -> ObjectKind {
319        match self.header.filetype(self.endian) {
320            macho::MH_OBJECT => ObjectKind::Relocatable,
321            macho::MH_EXECUTE => ObjectKind::Executable,
322            macho::MH_CORE => ObjectKind::Core,
323            macho::MH_DYLIB => ObjectKind::Dynamic,
324            _ => ObjectKind::Unknown,
325        }
326    }
327
328    fn segments(&self) -> MachOSegmentIterator<'data, '_, Mach, R> {
329        MachOSegmentIterator {
330            file: self,
331            iter: self.segments.iter(),
332        }
333    }
334
335    fn section_by_name_bytes<'file>(
336        &'file self,
337        section_name: &[u8],
338    ) -> Option<MachOSection<'data, 'file, Mach, R>> {
339        // Translate the section_name by stripping the query_prefix to construct
340        // a function that matches names starting with name_prefix, taking into
341        // consideration the maximum section name length.
342        let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| {
343            const MAX_SECTION_NAME_LEN: usize = 16;
344            let suffix = section_name.strip_prefix(query_prefix).map(|suffix| {
345                let max_len = MAX_SECTION_NAME_LEN - name_prefix.len();
346                &suffix[..suffix.len().min(max_len)]
347            });
348            move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix
349        };
350        // Matches "__text" when searching for ".text" and "__debug_str_offs"
351        // when searching for ".debug_str_offsets", as is common in
352        // macOS/Mach-O.
353        let matches_underscores_prefix = make_prefix_matcher(b".", b"__");
354        // Matches "__zdebug_info" when searching for ".debug_info" and
355        // "__zdebug_str_off" when searching for ".debug_str_offsets", as is
356        // used by Go when using GNU-style compression.
357        let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_");
358        self.sections().find(|section| {
359            section.name_bytes().map_or(false, |name| {
360                name == section_name
361                    || matches_underscores_prefix(name)
362                    || matches_zdebug_prefix(name)
363            })
364        })
365    }
366
367    fn section_by_index(&self, index: SectionIndex) -> Result<MachOSection<'data, '_, Mach, R>> {
368        let internal = *self.section_internal(index)?;
369        Ok(MachOSection {
370            file: self,
371            internal,
372        })
373    }
374
375    fn sections(&self) -> MachOSectionIterator<'data, '_, Mach, R> {
376        MachOSectionIterator {
377            file: self,
378            iter: self.sections.iter(),
379        }
380    }
381
382    fn comdats(&self) -> MachOComdatIterator<'data, '_, Mach, R> {
383        MachOComdatIterator { file: self }
384    }
385
386    fn symbol_by_index(&self, index: SymbolIndex) -> Result<MachOSymbol<'data, '_, Mach, R>> {
387        let nlist = self.symbols.symbol(index)?;
388        MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index")
389    }
390
391    fn symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
392        MachOSymbolIterator::new(self)
393    }
394
395    #[inline]
396    fn symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
397        Some(MachOSymbolTable { file: self })
398    }
399
400    fn dynamic_symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
401        MachOSymbolIterator::empty(self)
402    }
403
404    #[inline]
405    fn dynamic_symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
406        None
407    }
408
409    fn object_map(&self) -> ObjectMap<'data> {
410        self.symbols.object_map(self.endian)
411    }
412
413    fn imports(&self) -> Result<Vec<Import<'data>>> {
414        let mut dysymtab = None;
415        let mut libraries = Vec::new();
416        let twolevel = self.header.flags(self.endian) & macho::MH_TWOLEVEL != 0;
417        if twolevel {
418            libraries.push(&[][..]);
419        }
420        let mut commands =
421            self.header
422                .load_commands(self.endian, self.data.0, self.header_offset)?;
423        while let Some(command) = commands.next()? {
424            if let Some(command) = command.dysymtab()? {
425                dysymtab = Some(command);
426            }
427            if twolevel {
428                if let Some(dylib) = command.dylib()? {
429                    libraries.push(command.string(self.endian, dylib.dylib.name)?);
430                }
431            }
432        }
433
434        let mut imports = Vec::new();
435        if let Some(dysymtab) = dysymtab {
436            let index = dysymtab.iundefsym.get(self.endian) as usize;
437            let number = dysymtab.nundefsym.get(self.endian) as usize;
438            for i in index..(index.wrapping_add(number)) {
439                let symbol = self.symbols.symbol(SymbolIndex(i))?;
440                let name = symbol.name(self.endian, self.symbols.strings())?;
441                let library = if twolevel {
442                    libraries
443                        .get(symbol.library_ordinal(self.endian) as usize)
444                        .copied()
445                        .read_error("Invalid Mach-O symbol library ordinal")?
446                } else {
447                    &[]
448                };
449                imports.push(Import {
450                    name: ByteString(name),
451                    library: ByteString(library),
452                });
453            }
454        }
455        Ok(imports)
456    }
457
458    fn exports(&self) -> Result<Vec<Export<'data>>> {
459        let mut dysymtab = None;
460        let mut commands =
461            self.header
462                .load_commands(self.endian, self.data.0, self.header_offset)?;
463        while let Some(command) = commands.next()? {
464            if let Some(command) = command.dysymtab()? {
465                dysymtab = Some(command);
466                break;
467            }
468        }
469
470        let mut exports = Vec::new();
471        if let Some(dysymtab) = dysymtab {
472            let index = dysymtab.iextdefsym.get(self.endian) as usize;
473            let number = dysymtab.nextdefsym.get(self.endian) as usize;
474            for i in index..(index.wrapping_add(number)) {
475                let symbol = self.symbols.symbol(SymbolIndex(i))?;
476                let name = symbol.name(self.endian, self.symbols.strings())?;
477                let address = symbol.n_value(self.endian).into();
478                exports.push(Export {
479                    name: ByteString(name),
480                    address,
481                });
482            }
483        }
484        Ok(exports)
485    }
486
487    #[inline]
488    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
489        None
490    }
491
492    fn has_debug_symbols(&self) -> bool {
493        self.section_by_name(".debug_info").is_some()
494    }
495
496    fn mach_uuid(&self) -> Result<Option<[u8; 16]>> {
497        self.header
498            .uuid(self.endian, self.data.0, self.header_offset)
499    }
500
501    fn relative_address_base(&self) -> u64 {
502        0
503    }
504
505    fn entry(&self) -> u64 {
506        if let Ok(mut commands) =
507            self.header
508                .load_commands(self.endian, self.data.0, self.header_offset)
509        {
510            while let Ok(Some(command)) = commands.next() {
511                if let Ok(Some(command)) = command.entry_point() {
512                    return command.entryoff.get(self.endian);
513                }
514                if let Ok(Some((_, thread_data))) = command.unix_thread() {
515                    // Extract entry point from LC_UNIXTHREAD based on CPU type.
516                    // Thread data layout: flavor (u32), count (u32), then register state.
517                    let cputype = self.header.cputype(self.endian);
518                    // Offset and size of the program counter in the thread state.
519                    // Offsets include 8 bytes for flavor and count fields.
520                    let (pc_offset, pc_size) = match cputype {
521                        // x86_thread_state64: 16 u64 regs (rax-r15), then rip.
522                        macho::CPU_TYPE_X86_64 => (8 + 16 * 8, 8),
523                        // arm_thread_state64: 29 u64 regs (x0-x28), fp, lr, sp, then pc.
524                        macho::CPU_TYPE_ARM64 => (8 + 32 * 8, 8),
525                        // x86_thread_state32: 10 u32 regs, then eip.
526                        macho::CPU_TYPE_X86 => (8 + 10 * 4, 4),
527                        // arm_thread_state32: 15 u32 regs (r0-r12, sp, lr), then pc.
528                        macho::CPU_TYPE_ARM => (8 + 15 * 4, 4),
529                        _ => (0, 0),
530                    };
531                    if pc_size == 8 {
532                        if let Some(pc_bytes) = thread_data.get(pc_offset..pc_offset + 8) {
533                            let mut bytes = [0u8; 8];
534                            bytes.copy_from_slice(pc_bytes);
535                            return self.endian.read_u64(bytes);
536                        }
537                    } else if pc_size == 4 {
538                        if let Some(pc_bytes) = thread_data.get(pc_offset..pc_offset + 4) {
539                            let mut bytes = [0u8; 4];
540                            bytes.copy_from_slice(pc_bytes);
541                            return u64::from(self.endian.read_u32(bytes));
542                        }
543                    }
544                }
545            }
546        }
547        0
548    }
549
550    fn flags(&self) -> FileFlags {
551        FileFlags::MachO {
552            flags: self.header.flags(self.endian),
553        }
554    }
555}
556
557/// An iterator for the COMDAT section groups in a [`MachOFile64`].
558pub type MachOComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
559    MachOComdatIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
560/// An iterator for the COMDAT section groups in a [`MachOFile64`].
561pub type MachOComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
562    MachOComdatIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
563
564/// An iterator for the COMDAT section groups in a [`MachOFile`].
565///
566/// This is a stub that doesn't implement any functionality.
567#[derive(Debug)]
568pub struct MachOComdatIterator<'data, 'file, Mach, R = &'data [u8]>
569where
570    Mach: MachHeader,
571    R: ReadRef<'data>,
572{
573    #[allow(unused)]
574    file: &'file MachOFile<'data, Mach, R>,
575}
576
577impl<'data, 'file, Mach, R> Iterator for MachOComdatIterator<'data, 'file, Mach, R>
578where
579    Mach: MachHeader,
580    R: ReadRef<'data>,
581{
582    type Item = MachOComdat<'data, 'file, Mach, R>;
583
584    #[inline]
585    fn next(&mut self) -> Option<Self::Item> {
586        None
587    }
588}
589
590/// A COMDAT section group in a [`MachOFile32`].
591pub type MachOComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
592    MachOComdat<'data, 'file, macho::MachHeader32<Endian>, R>;
593
594/// A COMDAT section group in a [`MachOFile64`].
595pub type MachOComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
596    MachOComdat<'data, 'file, macho::MachHeader64<Endian>, R>;
597
598/// A COMDAT section group in a [`MachOFile`].
599///
600/// This is a stub that doesn't implement any functionality.
601#[derive(Debug)]
602pub struct MachOComdat<'data, 'file, Mach, R = &'data [u8]>
603where
604    Mach: MachHeader,
605    R: ReadRef<'data>,
606{
607    #[allow(unused)]
608    file: &'file MachOFile<'data, Mach, R>,
609}
610
611impl<'data, 'file, Mach, R> read::private::Sealed for MachOComdat<'data, 'file, Mach, R>
612where
613    Mach: MachHeader,
614    R: ReadRef<'data>,
615{
616}
617
618impl<'data, 'file, Mach, R> ObjectComdat<'data> for MachOComdat<'data, 'file, Mach, R>
619where
620    Mach: MachHeader,
621    R: ReadRef<'data>,
622{
623    type SectionIterator = MachOComdatSectionIterator<'data, 'file, Mach, R>;
624
625    #[inline]
626    fn kind(&self) -> ComdatKind {
627        unreachable!();
628    }
629
630    #[inline]
631    fn symbol(&self) -> SymbolIndex {
632        unreachable!();
633    }
634
635    #[inline]
636    fn name_bytes(&self) -> Result<&'data [u8]> {
637        unreachable!();
638    }
639
640    #[inline]
641    fn name(&self) -> Result<&'data str> {
642        unreachable!();
643    }
644
645    #[inline]
646    fn sections(&self) -> Self::SectionIterator {
647        unreachable!();
648    }
649}
650
651/// An iterator for the sections in a COMDAT section group in a [`MachOFile32`].
652pub type MachOComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
653    MachOComdatSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
654/// An iterator for the sections in a COMDAT section group in a [`MachOFile64`].
655pub type MachOComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
656    MachOComdatSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
657
658/// An iterator for the sections in a COMDAT section group in a [`MachOFile`].
659///
660/// This is a stub that doesn't implement any functionality.
661#[derive(Debug)]
662pub struct MachOComdatSectionIterator<'data, 'file, Mach, R = &'data [u8]>
663where
664    Mach: MachHeader,
665    R: ReadRef<'data>,
666{
667    #[allow(unused)]
668    file: &'file MachOFile<'data, Mach, R>,
669}
670
671impl<'data, 'file, Mach, R> Iterator for MachOComdatSectionIterator<'data, 'file, Mach, R>
672where
673    Mach: MachHeader,
674    R: ReadRef<'data>,
675{
676    type Item = SectionIndex;
677
678    fn next(&mut self) -> Option<Self::Item> {
679        None
680    }
681}
682
683/// A trait for generic access to [`macho::MachHeader32`] and [`macho::MachHeader64`].
684#[allow(missing_docs)]
685pub trait MachHeader: Debug + Pod {
686    type Word: Into<u64>;
687    type Endian: endian::Endian;
688    type Segment: Segment<Endian = Self::Endian, Section = Self::Section>;
689    type Section: Section<Endian = Self::Endian>;
690    type Nlist: Nlist<Endian = Self::Endian>;
691
692    /// Return true if this type is a 64-bit header.
693    ///
694    /// This is a property of the type, not a value in the header data.
695    fn is_type_64(&self) -> bool;
696
697    /// Return true if the `magic` field signifies big-endian.
698    fn is_big_endian(&self) -> bool;
699
700    /// Return true if the `magic` field signifies little-endian.
701    fn is_little_endian(&self) -> bool;
702
703    fn magic(&self) -> u32;
704    fn cputype(&self, endian: Self::Endian) -> u32;
705    fn cpusubtype(&self, endian: Self::Endian) -> u32;
706    fn filetype(&self, endian: Self::Endian) -> u32;
707    fn ncmds(&self, endian: Self::Endian) -> u32;
708    fn sizeofcmds(&self, endian: Self::Endian) -> u32;
709    fn flags(&self, endian: Self::Endian) -> u32;
710
711    // Provided methods.
712
713    /// Read the file header.
714    ///
715    /// Also checks that the magic field in the file header is a supported format.
716    fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> {
717        let header = data
718            .read_at::<Self>(offset)
719            .read_error("Invalid Mach-O header size or alignment")?;
720        if !header.is_supported() {
721            return Err(Error("Unsupported Mach-O header"));
722        }
723        Ok(header)
724    }
725
726    fn is_supported(&self) -> bool {
727        self.is_little_endian() || self.is_big_endian()
728    }
729
730    fn endian(&self) -> Result<Self::Endian> {
731        Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported Mach-O endian")
732    }
733
734    fn load_commands<'data, R: ReadRef<'data>>(
735        &self,
736        endian: Self::Endian,
737        data: R,
738        header_offset: u64,
739    ) -> Result<LoadCommandIterator<'data, Self::Endian>> {
740        let data = data
741            .read_bytes_at(
742                header_offset + mem::size_of::<Self>() as u64,
743                self.sizeofcmds(endian).into(),
744            )
745            .read_error("Invalid Mach-O load command table size")?;
746        Ok(LoadCommandIterator::new(endian, data, self.ncmds(endian)))
747    }
748
749    /// Return the UUID from the `LC_UUID` load command, if one is present.
750    fn uuid<'data, R: ReadRef<'data>>(
751        &self,
752        endian: Self::Endian,
753        data: R,
754        header_offset: u64,
755    ) -> Result<Option<[u8; 16]>> {
756        let mut commands = self.load_commands(endian, data, header_offset)?;
757        while let Some(command) = commands.next()? {
758            if let Ok(Some(uuid)) = command.uuid() {
759                return Ok(Some(uuid.uuid));
760            }
761        }
762        Ok(None)
763    }
764}
765
766impl<Endian: endian::Endian> MachHeader for macho::MachHeader32<Endian> {
767    type Word = u32;
768    type Endian = Endian;
769    type Segment = macho::SegmentCommand32<Endian>;
770    type Section = macho::Section32<Endian>;
771    type Nlist = macho::Nlist32<Endian>;
772
773    fn is_type_64(&self) -> bool {
774        false
775    }
776
777    fn is_big_endian(&self) -> bool {
778        self.magic() == macho::MH_MAGIC
779    }
780
781    fn is_little_endian(&self) -> bool {
782        self.magic() == macho::MH_CIGAM
783    }
784
785    fn magic(&self) -> u32 {
786        self.magic.get(BigEndian)
787    }
788
789    fn cputype(&self, endian: Self::Endian) -> u32 {
790        self.cputype.get(endian)
791    }
792
793    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
794        self.cpusubtype.get(endian)
795    }
796
797    fn filetype(&self, endian: Self::Endian) -> u32 {
798        self.filetype.get(endian)
799    }
800
801    fn ncmds(&self, endian: Self::Endian) -> u32 {
802        self.ncmds.get(endian)
803    }
804
805    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
806        self.sizeofcmds.get(endian)
807    }
808
809    fn flags(&self, endian: Self::Endian) -> u32 {
810        self.flags.get(endian)
811    }
812}
813
814impl<Endian: endian::Endian> MachHeader for macho::MachHeader64<Endian> {
815    type Word = u64;
816    type Endian = Endian;
817    type Segment = macho::SegmentCommand64<Endian>;
818    type Section = macho::Section64<Endian>;
819    type Nlist = macho::Nlist64<Endian>;
820
821    fn is_type_64(&self) -> bool {
822        true
823    }
824
825    fn is_big_endian(&self) -> bool {
826        self.magic() == macho::MH_MAGIC_64
827    }
828
829    fn is_little_endian(&self) -> bool {
830        self.magic() == macho::MH_CIGAM_64
831    }
832
833    fn magic(&self) -> u32 {
834        self.magic.get(BigEndian)
835    }
836
837    fn cputype(&self, endian: Self::Endian) -> u32 {
838        self.cputype.get(endian)
839    }
840
841    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
842        self.cpusubtype.get(endian)
843    }
844
845    fn filetype(&self, endian: Self::Endian) -> u32 {
846        self.filetype.get(endian)
847    }
848
849    fn ncmds(&self, endian: Self::Endian) -> u32 {
850        self.ncmds.get(endian)
851    }
852
853    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
854        self.sizeofcmds.get(endian)
855    }
856
857    fn flags(&self, endian: Self::Endian) -> u32 {
858        self.flags.get(endian)
859    }
860}