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