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> = MachOSegment<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
225    type SegmentIterator<'file> = MachOSegmentIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
226    type Section<'file> = MachOSection<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
227    type SectionIterator<'file> = MachOSectionIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
228    type Comdat<'file> = MachOComdat<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
229    type ComdatIterator<'file> = MachOComdatIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
230    type Symbol<'file> = MachOSymbol<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
231    type SymbolIterator<'file> = MachOSymbolIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
232    type SymbolTable<'file> = MachOSymbolTable<'data, 'file, Mach, R> where Self: 'file, 'data: 'file;
233    type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file;
234
235    fn architecture(&self) -> Architecture {
236        match self.header.cputype(self.endian) {
237            macho::CPU_TYPE_ARM => Architecture::Arm,
238            macho::CPU_TYPE_ARM64 => Architecture::Aarch64,
239            macho::CPU_TYPE_ARM64_32 => Architecture::Aarch64_Ilp32,
240            macho::CPU_TYPE_X86 => Architecture::I386,
241            macho::CPU_TYPE_X86_64 => Architecture::X86_64,
242            macho::CPU_TYPE_MIPS => Architecture::Mips,
243            macho::CPU_TYPE_POWERPC => Architecture::PowerPc,
244            macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64,
245            _ => Architecture::Unknown,
246        }
247    }
248
249    fn sub_architecture(&self) -> Option<SubArchitecture> {
250        match (
251            self.header.cputype(self.endian),
252            self.header.cpusubtype(self.endian),
253        ) {
254            (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) => Some(SubArchitecture::Arm64E),
255            _ => None,
256        }
257    }
258
259    #[inline]
260    fn is_little_endian(&self) -> bool {
261        self.header.is_little_endian()
262    }
263
264    #[inline]
265    fn is_64(&self) -> bool {
266        self.header.is_type_64()
267    }
268
269    fn kind(&self) -> ObjectKind {
270        match self.header.filetype(self.endian) {
271            macho::MH_OBJECT => ObjectKind::Relocatable,
272            macho::MH_EXECUTE => ObjectKind::Executable,
273            macho::MH_CORE => ObjectKind::Core,
274            macho::MH_DYLIB => ObjectKind::Dynamic,
275            _ => ObjectKind::Unknown,
276        }
277    }
278
279    fn segments(&self) -> MachOSegmentIterator<'data, '_, Mach, R> {
280        MachOSegmentIterator {
281            file: self,
282            iter: self.segments.iter(),
283        }
284    }
285
286    fn section_by_name_bytes<'file>(
287        &'file self,
288        section_name: &[u8],
289    ) -> Option<MachOSection<'data, 'file, Mach, R>> {
290        // Translate the section_name by stripping the query_prefix to construct
291        // a function that matches names starting with name_prefix, taking into
292        // consideration the maximum section name length.
293        let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| {
294            const MAX_SECTION_NAME_LEN: usize = 16;
295            let suffix = section_name.strip_prefix(query_prefix).map(|suffix| {
296                let max_len = MAX_SECTION_NAME_LEN - name_prefix.len();
297                &suffix[..suffix.len().min(max_len)]
298            });
299            move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix
300        };
301        // Matches "__text" when searching for ".text" and "__debug_str_offs"
302        // when searching for ".debug_str_offsets", as is common in
303        // macOS/Mach-O.
304        let matches_underscores_prefix = make_prefix_matcher(b".", b"__");
305        // Matches "__zdebug_info" when searching for ".debug_info" and
306        // "__zdebug_str_off" when searching for ".debug_str_offsets", as is
307        // used by Go when using GNU-style compression.
308        let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_");
309        self.sections().find(|section| {
310            section.name_bytes().map_or(false, |name| {
311                name == section_name
312                    || matches_underscores_prefix(name)
313                    || matches_zdebug_prefix(name)
314            })
315        })
316    }
317
318    fn section_by_index(&self, index: SectionIndex) -> Result<MachOSection<'data, '_, Mach, R>> {
319        let internal = *self.section_internal(index)?;
320        Ok(MachOSection {
321            file: self,
322            internal,
323        })
324    }
325
326    fn sections(&self) -> MachOSectionIterator<'data, '_, Mach, R> {
327        MachOSectionIterator {
328            file: self,
329            iter: self.sections.iter(),
330        }
331    }
332
333    fn comdats(&self) -> MachOComdatIterator<'data, '_, Mach, R> {
334        MachOComdatIterator { file: self }
335    }
336
337    fn symbol_by_index(&self, index: SymbolIndex) -> Result<MachOSymbol<'data, '_, Mach, R>> {
338        let nlist = self.symbols.symbol(index)?;
339        MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index")
340    }
341
342    fn symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
343        MachOSymbolIterator::new(self)
344    }
345
346    #[inline]
347    fn symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
348        Some(MachOSymbolTable { file: self })
349    }
350
351    fn dynamic_symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
352        MachOSymbolIterator::empty(self)
353    }
354
355    #[inline]
356    fn dynamic_symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
357        None
358    }
359
360    fn object_map(&self) -> ObjectMap<'data> {
361        self.symbols.object_map(self.endian)
362    }
363
364    fn imports(&self) -> Result<Vec<Import<'data>>> {
365        let mut dysymtab = None;
366        let mut libraries = Vec::new();
367        let twolevel = self.header.flags(self.endian) & macho::MH_TWOLEVEL != 0;
368        if twolevel {
369            libraries.push(&[][..]);
370        }
371        let mut commands = self
372            .header
373            .load_commands(self.endian, self.data, self.header_offset)?;
374        while let Some(command) = commands.next()? {
375            if let Some(command) = command.dysymtab()? {
376                dysymtab = Some(command);
377            }
378            if twolevel {
379                if let Some(dylib) = command.dylib()? {
380                    libraries.push(command.string(self.endian, dylib.dylib.name)?);
381                }
382            }
383        }
384
385        let mut imports = Vec::new();
386        if let Some(dysymtab) = dysymtab {
387            let index = dysymtab.iundefsym.get(self.endian) as usize;
388            let number = dysymtab.nundefsym.get(self.endian) as usize;
389            for i in index..(index.wrapping_add(number)) {
390                let symbol = self.symbols.symbol(SymbolIndex(i))?;
391                let name = symbol.name(self.endian, self.symbols.strings())?;
392                let library = if twolevel {
393                    libraries
394                        .get(symbol.library_ordinal(self.endian) as usize)
395                        .copied()
396                        .read_error("Invalid Mach-O symbol library ordinal")?
397                } else {
398                    &[]
399                };
400                imports.push(Import {
401                    name: ByteString(name),
402                    library: ByteString(library),
403                });
404            }
405        }
406        Ok(imports)
407    }
408
409    fn exports(&self) -> Result<Vec<Export<'data>>> {
410        let mut dysymtab = None;
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                break;
418            }
419        }
420
421        let mut exports = Vec::new();
422        if let Some(dysymtab) = dysymtab {
423            let index = dysymtab.iextdefsym.get(self.endian) as usize;
424            let number = dysymtab.nextdefsym.get(self.endian) as usize;
425            for i in index..(index.wrapping_add(number)) {
426                let symbol = self.symbols.symbol(SymbolIndex(i))?;
427                let name = symbol.name(self.endian, self.symbols.strings())?;
428                let address = symbol.n_value(self.endian).into();
429                exports.push(Export {
430                    name: ByteString(name),
431                    address,
432                });
433            }
434        }
435        Ok(exports)
436    }
437
438    #[inline]
439    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
440        None
441    }
442
443    fn has_debug_symbols(&self) -> bool {
444        self.section_by_name(".debug_info").is_some()
445    }
446
447    fn mach_uuid(&self) -> Result<Option<[u8; 16]>> {
448        self.header.uuid(self.endian, self.data, self.header_offset)
449    }
450
451    fn relative_address_base(&self) -> u64 {
452        0
453    }
454
455    fn entry(&self) -> u64 {
456        if let Ok(mut commands) =
457            self.header
458                .load_commands(self.endian, self.data, self.header_offset)
459        {
460            while let Ok(Some(command)) = commands.next() {
461                if let Ok(Some(command)) = command.entry_point() {
462                    return command.entryoff.get(self.endian);
463                }
464            }
465        }
466        0
467    }
468
469    fn flags(&self) -> FileFlags {
470        FileFlags::MachO {
471            flags: self.header.flags(self.endian),
472        }
473    }
474}
475
476/// An iterator for the COMDAT section groups in a [`MachOFile64`].
477pub type MachOComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
478    MachOComdatIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
479/// An iterator for the COMDAT section groups in a [`MachOFile64`].
480pub type MachOComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
481    MachOComdatIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
482
483/// An iterator for the COMDAT section groups in a [`MachOFile`].
484///
485/// This is a stub that doesn't implement any functionality.
486#[derive(Debug)]
487pub struct MachOComdatIterator<'data, 'file, Mach, R = &'data [u8]>
488where
489    Mach: MachHeader,
490    R: ReadRef<'data>,
491{
492    #[allow(unused)]
493    file: &'file MachOFile<'data, Mach, R>,
494}
495
496impl<'data, 'file, Mach, R> Iterator for MachOComdatIterator<'data, 'file, Mach, R>
497where
498    Mach: MachHeader,
499    R: ReadRef<'data>,
500{
501    type Item = MachOComdat<'data, 'file, Mach, R>;
502
503    #[inline]
504    fn next(&mut self) -> Option<Self::Item> {
505        None
506    }
507}
508
509/// A COMDAT section group in a [`MachOFile32`].
510pub type MachOComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
511    MachOComdat<'data, 'file, macho::MachHeader32<Endian>, R>;
512
513/// A COMDAT section group in a [`MachOFile64`].
514pub type MachOComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
515    MachOComdat<'data, 'file, macho::MachHeader64<Endian>, R>;
516
517/// A COMDAT section group in a [`MachOFile`].
518///
519/// This is a stub that doesn't implement any functionality.
520#[derive(Debug)]
521pub struct MachOComdat<'data, 'file, Mach, R = &'data [u8]>
522where
523    Mach: MachHeader,
524    R: ReadRef<'data>,
525{
526    #[allow(unused)]
527    file: &'file MachOFile<'data, Mach, R>,
528}
529
530impl<'data, 'file, Mach, R> read::private::Sealed for MachOComdat<'data, 'file, Mach, R>
531where
532    Mach: MachHeader,
533    R: ReadRef<'data>,
534{
535}
536
537impl<'data, 'file, Mach, R> ObjectComdat<'data> for MachOComdat<'data, 'file, Mach, R>
538where
539    Mach: MachHeader,
540    R: ReadRef<'data>,
541{
542    type SectionIterator = MachOComdatSectionIterator<'data, 'file, Mach, R>;
543
544    #[inline]
545    fn kind(&self) -> ComdatKind {
546        unreachable!();
547    }
548
549    #[inline]
550    fn symbol(&self) -> SymbolIndex {
551        unreachable!();
552    }
553
554    #[inline]
555    fn name_bytes(&self) -> Result<&'data [u8]> {
556        unreachable!();
557    }
558
559    #[inline]
560    fn name(&self) -> Result<&'data str> {
561        unreachable!();
562    }
563
564    #[inline]
565    fn sections(&self) -> Self::SectionIterator {
566        unreachable!();
567    }
568}
569
570/// An iterator for the sections in a COMDAT section group in a [`MachOFile32`].
571pub type MachOComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
572    MachOComdatSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
573/// An iterator for the sections in a COMDAT section group in a [`MachOFile64`].
574pub type MachOComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
575    MachOComdatSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
576
577/// An iterator for the sections in a COMDAT section group in a [`MachOFile`].
578///
579/// This is a stub that doesn't implement any functionality.
580#[derive(Debug)]
581pub struct MachOComdatSectionIterator<'data, 'file, Mach, R = &'data [u8]>
582where
583    Mach: MachHeader,
584    R: ReadRef<'data>,
585{
586    #[allow(unused)]
587    file: &'file MachOFile<'data, Mach, R>,
588}
589
590impl<'data, 'file, Mach, R> Iterator for MachOComdatSectionIterator<'data, 'file, Mach, R>
591where
592    Mach: MachHeader,
593    R: ReadRef<'data>,
594{
595    type Item = SectionIndex;
596
597    fn next(&mut self) -> Option<Self::Item> {
598        None
599    }
600}
601
602/// A trait for generic access to [`macho::MachHeader32`] and [`macho::MachHeader64`].
603#[allow(missing_docs)]
604pub trait MachHeader: Debug + Pod {
605    type Word: Into<u64>;
606    type Endian: endian::Endian;
607    type Segment: Segment<Endian = Self::Endian, Section = Self::Section>;
608    type Section: Section<Endian = Self::Endian>;
609    type Nlist: Nlist<Endian = Self::Endian>;
610
611    /// Return true if this type is a 64-bit header.
612    ///
613    /// This is a property of the type, not a value in the header data.
614    fn is_type_64(&self) -> bool;
615
616    /// Return true if the `magic` field signifies big-endian.
617    fn is_big_endian(&self) -> bool;
618
619    /// Return true if the `magic` field signifies little-endian.
620    fn is_little_endian(&self) -> bool;
621
622    fn magic(&self) -> u32;
623    fn cputype(&self, endian: Self::Endian) -> u32;
624    fn cpusubtype(&self, endian: Self::Endian) -> u32;
625    fn filetype(&self, endian: Self::Endian) -> u32;
626    fn ncmds(&self, endian: Self::Endian) -> u32;
627    fn sizeofcmds(&self, endian: Self::Endian) -> u32;
628    fn flags(&self, endian: Self::Endian) -> u32;
629
630    // Provided methods.
631
632    /// Read the file header.
633    ///
634    /// Also checks that the magic field in the file header is a supported format.
635    fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> {
636        let header = data
637            .read_at::<Self>(offset)
638            .read_error("Invalid Mach-O header size or alignment")?;
639        if !header.is_supported() {
640            return Err(Error("Unsupported Mach-O header"));
641        }
642        Ok(header)
643    }
644
645    fn is_supported(&self) -> bool {
646        self.is_little_endian() || self.is_big_endian()
647    }
648
649    fn endian(&self) -> Result<Self::Endian> {
650        Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported Mach-O endian")
651    }
652
653    fn load_commands<'data, R: ReadRef<'data>>(
654        &self,
655        endian: Self::Endian,
656        data: R,
657        header_offset: u64,
658    ) -> Result<LoadCommandIterator<'data, Self::Endian>> {
659        let data = data
660            .read_bytes_at(
661                header_offset + mem::size_of::<Self>() as u64,
662                self.sizeofcmds(endian).into(),
663            )
664            .read_error("Invalid Mach-O load command table size")?;
665        Ok(LoadCommandIterator::new(endian, data, self.ncmds(endian)))
666    }
667
668    /// Return the UUID from the `LC_UUID` load command, if one is present.
669    fn uuid<'data, R: ReadRef<'data>>(
670        &self,
671        endian: Self::Endian,
672        data: R,
673        header_offset: u64,
674    ) -> Result<Option<[u8; 16]>> {
675        let mut commands = self.load_commands(endian, data, header_offset)?;
676        while let Some(command) = commands.next()? {
677            if let Ok(Some(uuid)) = command.uuid() {
678                return Ok(Some(uuid.uuid));
679            }
680        }
681        Ok(None)
682    }
683}
684
685impl<Endian: endian::Endian> MachHeader for macho::MachHeader32<Endian> {
686    type Word = u32;
687    type Endian = Endian;
688    type Segment = macho::SegmentCommand32<Endian>;
689    type Section = macho::Section32<Endian>;
690    type Nlist = macho::Nlist32<Endian>;
691
692    fn is_type_64(&self) -> bool {
693        false
694    }
695
696    fn is_big_endian(&self) -> bool {
697        self.magic() == macho::MH_MAGIC
698    }
699
700    fn is_little_endian(&self) -> bool {
701        self.magic() == macho::MH_CIGAM
702    }
703
704    fn magic(&self) -> u32 {
705        self.magic.get(BigEndian)
706    }
707
708    fn cputype(&self, endian: Self::Endian) -> u32 {
709        self.cputype.get(endian)
710    }
711
712    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
713        self.cpusubtype.get(endian)
714    }
715
716    fn filetype(&self, endian: Self::Endian) -> u32 {
717        self.filetype.get(endian)
718    }
719
720    fn ncmds(&self, endian: Self::Endian) -> u32 {
721        self.ncmds.get(endian)
722    }
723
724    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
725        self.sizeofcmds.get(endian)
726    }
727
728    fn flags(&self, endian: Self::Endian) -> u32 {
729        self.flags.get(endian)
730    }
731}
732
733impl<Endian: endian::Endian> MachHeader for macho::MachHeader64<Endian> {
734    type Word = u64;
735    type Endian = Endian;
736    type Segment = macho::SegmentCommand64<Endian>;
737    type Section = macho::Section64<Endian>;
738    type Nlist = macho::Nlist64<Endian>;
739
740    fn is_type_64(&self) -> bool {
741        true
742    }
743
744    fn is_big_endian(&self) -> bool {
745        self.magic() == macho::MH_MAGIC_64
746    }
747
748    fn is_little_endian(&self) -> bool {
749        self.magic() == macho::MH_CIGAM_64
750    }
751
752    fn magic(&self) -> u32 {
753        self.magic.get(BigEndian)
754    }
755
756    fn cputype(&self, endian: Self::Endian) -> u32 {
757        self.cputype.get(endian)
758    }
759
760    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
761        self.cpusubtype.get(endian)
762    }
763
764    fn filetype(&self, endian: Self::Endian) -> u32 {
765        self.filetype.get(endian)
766    }
767
768    fn ncmds(&self, endian: Self::Endian) -> u32 {
769        self.ncmds.get(endian)
770    }
771
772    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
773        self.sizeofcmds.get(endian)
774    }
775
776    fn flags(&self, endian: Self::Endian) -> u32 {
777        self.flags.get(endian)
778    }
779}