Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/object-0.36.1/src/read/macho/file.rs
Line
Count
Source (jump to first uncovered line)
1
use alloc::vec::Vec;
2
use core::fmt::Debug;
3
use core::{mem, str};
4
5
use crate::endian::{self, BigEndian, Endian, Endianness};
6
use crate::macho;
7
use crate::pod::Pod;
8
use 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
14
use 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`].
24
pub 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`].
30
pub 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)]
37
pub struct MachOFile<'data, Mach, R = &'data [u8]>
38
where
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
51
impl<'data, Mach, R> MachOFile<'data, Mach, R>
52
where
53
    Mach: MachHeader,
54
    R: ReadRef<'data>,
55
{
56
    /// Parse the raw Mach-O file data.
57
0
    pub fn parse(data: R) -> Result<Self> {
58
0
        let header = Mach::parse(data, 0)?;
59
0
        let endian = header.endian()?;
60
61
        // Build a list of segments and sections to make some operations more efficient.
62
0
        let mut segments = Vec::new();
63
0
        let mut sections = Vec::new();
64
0
        let mut symbols = SymbolTable::default();
65
0
        if let Ok(mut commands) = header.load_commands(endian, data, 0) {
66
0
            while let Ok(Some(command)) = commands.next() {
67
0
                if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
68
0
                    segments.push(MachOSegmentInternal { segment, data });
69
0
                    for section in segment.sections(endian, section_data)? {
70
0
                        let index = SectionIndex(sections.len() + 1);
71
0
                        sections.push(MachOSectionInternal::parse(index, section, data));
72
0
                    }
73
0
                } else if let Some(symtab) = command.symtab()? {
74
0
                    symbols = symtab.symbols(endian, data)?;
75
0
                }
76
            }
77
0
        }
78
79
0
        Ok(MachOFile {
80
0
            endian,
81
0
            data,
82
0
            header_offset: 0,
83
0
            header,
84
0
            segments,
85
0
            sections,
86
0
            symbols,
87
0
        })
88
0
    }
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
0
    pub fn parse_dyld_cache_image<'cache, E: Endian>(
93
0
        image: &DyldCacheImage<'data, 'cache, E, R>,
94
0
    ) -> Result<Self> {
95
0
        let (data, header_offset) = image.image_data_and_offset()?;
96
0
        let header = Mach::parse(data, header_offset)?;
97
0
        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
0
        let mut segments = Vec::new();
104
0
        let mut sections = Vec::new();
105
0
        let mut linkedit_data: Option<R> = None;
106
0
        let mut symtab = None;
107
0
        if let Ok(mut commands) = header.load_commands(endian, data, header_offset) {
108
0
            while let Ok(Some(command)) = commands.next() {
109
0
                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
0
                    let addr = segment.vmaddr(endian).into();
114
0
                    let (data, _offset) = image
115
0
                        .cache
116
0
                        .data_and_offset_for_address(addr)
117
0
                        .read_error("Could not find segment data in dyld shared cache")?;
118
0
                    if segment.name() == macho::SEG_LINKEDIT.as_bytes() {
119
0
                        linkedit_data = Some(data);
120
0
                    }
121
0
                    segments.push(MachOSegmentInternal { segment, data });
122
123
0
                    for section in segment.sections(endian, section_data)? {
124
0
                        let index = SectionIndex(sections.len() + 1);
125
0
                        sections.push(MachOSectionInternal::parse(index, section, data));
126
0
                    }
127
0
                } else if let Some(st) = command.symtab()? {
128
0
                    symtab = Some(st);
129
0
                }
130
            }
131
0
        }
132
133
        // The symbols are found in the __LINKEDIT segment, so make sure to read them from the
134
        // correct subcache.
135
0
        let symbols = match (symtab, linkedit_data) {
136
0
            (Some(symtab), Some(linkedit_data)) => symtab.symbols(endian, linkedit_data)?,
137
0
            _ => SymbolTable::default(),
138
        };
139
140
0
        Ok(MachOFile {
141
0
            endian,
142
0
            data,
143
0
            header_offset,
144
0
            header,
145
0
            segments,
146
0
            sections,
147
0
            symbols,
148
0
        })
149
0
    }
150
151
    /// Return the section at the given index.
152
    #[inline]
153
0
    pub(super) fn section_internal(
154
0
        &self,
155
0
        index: SectionIndex,
156
0
    ) -> Result<&MachOSectionInternal<'data, Mach, R>> {
157
0
        index
158
0
            .0
159
0
            .checked_sub(1)
160
0
            .and_then(|index| self.sections.get(index))
161
0
            .read_error("Invalid Mach-O section index")
162
0
    }
163
164
    /// Returns the endianness.
165
0
    pub fn endian(&self) -> Mach::Endian {
166
0
        self.endian
167
0
    }
168
169
    /// Returns the raw data.
170
0
    pub fn data(&self) -> R {
171
0
        self.data
172
0
    }
173
174
    /// Returns the raw Mach-O file header.
175
    #[deprecated(note = "Use `macho_header` instead")]
176
0
    pub fn raw_header(&self) -> &'data Mach {
177
0
        self.header
178
0
    }
179
180
    /// Get the raw Mach-O file header.
181
0
    pub fn macho_header(&self) -> &'data Mach {
182
0
        self.header
183
0
    }
184
185
    /// Get the Mach-O load commands.
186
0
    pub fn macho_load_commands(&self) -> Result<LoadCommandIterator<'data, Mach::Endian>> {
187
0
        self.header
188
0
            .load_commands(self.endian, self.data, self.header_offset)
189
0
    }
190
191
    /// Get the Mach-O symbol table.
192
    ///
193
    /// Returns an empty symbol table if the file has no symbol table.
194
0
    pub fn macho_symbol_table(&self) -> &SymbolTable<'data, Mach, R> {
195
0
        &self.symbols
196
0
    }
197
198
    /// Return the `LC_BUILD_VERSION` load command if present.
199
0
    pub fn build_version(&self) -> Result<Option<&'data macho::BuildVersionCommand<Mach::Endian>>> {
200
0
        let mut commands = self
201
0
            .header
202
0
            .load_commands(self.endian, self.data, self.header_offset)?;
203
0
        while let Some(command) = commands.next()? {
204
0
            if let Some(build_version) = command.build_version()? {
205
0
                return Ok(Some(build_version));
206
0
            }
207
        }
208
0
        Ok(None)
209
0
    }
210
}
211
212
impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R>
213
where
214
    Mach: MachHeader,
215
    R: ReadRef<'data>,
216
{
217
}
218
219
impl<'data, Mach, R> Object<'data> for MachOFile<'data, Mach, R>
220
where
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
0
    fn architecture(&self) -> Architecture {
236
0
        match self.header.cputype(self.endian) {
237
0
            macho::CPU_TYPE_ARM => Architecture::Arm,
238
0
            macho::CPU_TYPE_ARM64 => Architecture::Aarch64,
239
0
            macho::CPU_TYPE_ARM64_32 => Architecture::Aarch64_Ilp32,
240
0
            macho::CPU_TYPE_X86 => Architecture::I386,
241
0
            macho::CPU_TYPE_X86_64 => Architecture::X86_64,
242
0
            macho::CPU_TYPE_MIPS => Architecture::Mips,
243
0
            macho::CPU_TYPE_POWERPC => Architecture::PowerPc,
244
0
            macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64,
245
0
            _ => Architecture::Unknown,
246
        }
247
0
    }
248
249
0
    fn sub_architecture(&self) -> Option<SubArchitecture> {
250
0
        match (
251
0
            self.header.cputype(self.endian),
252
0
            self.header.cpusubtype(self.endian),
253
0
        ) {
254
0
            (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) => Some(SubArchitecture::Arm64E),
255
0
            _ => None,
256
        }
257
0
    }
258
259
    #[inline]
260
0
    fn is_little_endian(&self) -> bool {
261
0
        self.header.is_little_endian()
262
0
    }
263
264
    #[inline]
265
0
    fn is_64(&self) -> bool {
266
0
        self.header.is_type_64()
267
0
    }
268
269
0
    fn kind(&self) -> ObjectKind {
270
0
        match self.header.filetype(self.endian) {
271
0
            macho::MH_OBJECT => ObjectKind::Relocatable,
272
0
            macho::MH_EXECUTE => ObjectKind::Executable,
273
0
            macho::MH_CORE => ObjectKind::Core,
274
0
            macho::MH_DYLIB => ObjectKind::Dynamic,
275
0
            _ => ObjectKind::Unknown,
276
        }
277
0
    }
278
279
0
    fn segments(&self) -> MachOSegmentIterator<'data, '_, Mach, R> {
280
0
        MachOSegmentIterator {
281
0
            file: self,
282
0
            iter: self.segments.iter(),
283
0
        }
284
0
    }
285
286
0
    fn section_by_name_bytes<'file>(
287
0
        &'file self,
288
0
        section_name: &[u8],
289
0
    ) -> Option<MachOSection<'data, 'file, Mach, R>> {
290
0
        // Translate the section_name by stripping the query_prefix to construct
291
0
        // a function that matches names starting with name_prefix, taking into
292
0
        // consideration the maximum section name length.
293
0
        let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| {
294
0
            const MAX_SECTION_NAME_LEN: usize = 16;
295
0
            let suffix = section_name.strip_prefix(query_prefix).map(|suffix| {
296
0
                let max_len = MAX_SECTION_NAME_LEN - name_prefix.len();
297
0
                &suffix[..suffix.len().min(max_len)]
298
0
            });
299
0
            move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix
300
0
        };
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
0
        let matches_underscores_prefix = make_prefix_matcher(b".", b"__");
305
0
        // Matches "__zdebug_info" when searching for ".debug_info" and
306
0
        // "__zdebug_str_off" when searching for ".debug_str_offsets", as is
307
0
        // used by Go when using GNU-style compression.
308
0
        let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_");
309
0
        self.sections().find(|section| {
310
0
            section.name_bytes().map_or(false, |name| {
311
0
                name == section_name
312
0
                    || matches_underscores_prefix(name)
313
0
                    || matches_zdebug_prefix(name)
314
0
            })
315
0
        })
316
0
    }
317
318
0
    fn section_by_index(&self, index: SectionIndex) -> Result<MachOSection<'data, '_, Mach, R>> {
319
0
        let internal = *self.section_internal(index)?;
320
0
        Ok(MachOSection {
321
0
            file: self,
322
0
            internal,
323
0
        })
324
0
    }
325
326
0
    fn sections(&self) -> MachOSectionIterator<'data, '_, Mach, R> {
327
0
        MachOSectionIterator {
328
0
            file: self,
329
0
            iter: self.sections.iter(),
330
0
        }
331
0
    }
332
333
0
    fn comdats(&self) -> MachOComdatIterator<'data, '_, Mach, R> {
334
0
        MachOComdatIterator { file: self }
335
0
    }
336
337
0
    fn symbol_by_index(&self, index: SymbolIndex) -> Result<MachOSymbol<'data, '_, Mach, R>> {
338
0
        let nlist = self.symbols.symbol(index)?;
339
0
        MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index")
340
0
    }
341
342
0
    fn symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
343
0
        MachOSymbolIterator::new(self)
344
0
    }
345
346
    #[inline]
347
0
    fn symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
348
0
        Some(MachOSymbolTable { file: self })
349
0
    }
350
351
0
    fn dynamic_symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> {
352
0
        MachOSymbolIterator::empty(self)
353
0
    }
354
355
    #[inline]
356
0
    fn dynamic_symbol_table(&self) -> Option<MachOSymbolTable<'data, '_, Mach, R>> {
357
0
        None
358
0
    }
359
360
0
    fn object_map(&self) -> ObjectMap<'data> {
361
0
        self.symbols.object_map(self.endian)
362
0
    }
363
364
0
    fn imports(&self) -> Result<Vec<Import<'data>>> {
365
0
        let mut dysymtab = None;
366
0
        let mut libraries = Vec::new();
367
0
        let twolevel = self.header.flags(self.endian) & macho::MH_TWOLEVEL != 0;
368
0
        if twolevel {
369
0
            libraries.push(&[][..]);
370
0
        }
371
0
        let mut commands = self
372
0
            .header
373
0
            .load_commands(self.endian, self.data, self.header_offset)?;
374
0
        while let Some(command) = commands.next()? {
375
0
            if let Some(command) = command.dysymtab()? {
376
0
                dysymtab = Some(command);
377
0
            }
378
0
            if twolevel {
379
0
                if let Some(dylib) = command.dylib()? {
380
0
                    libraries.push(command.string(self.endian, dylib.dylib.name)?);
381
0
                }
382
0
            }
383
        }
384
385
0
        let mut imports = Vec::new();
386
0
        if let Some(dysymtab) = dysymtab {
387
0
            let index = dysymtab.iundefsym.get(self.endian) as usize;
388
0
            let number = dysymtab.nundefsym.get(self.endian) as usize;
389
0
            for i in index..(index.wrapping_add(number)) {
390
0
                let symbol = self.symbols.symbol(SymbolIndex(i))?;
391
0
                let name = symbol.name(self.endian, self.symbols.strings())?;
392
0
                let library = if twolevel {
393
0
                    libraries
394
0
                        .get(symbol.library_ordinal(self.endian) as usize)
395
0
                        .copied()
396
0
                        .read_error("Invalid Mach-O symbol library ordinal")?
397
                } else {
398
0
                    &[]
399
                };
400
0
                imports.push(Import {
401
0
                    name: ByteString(name),
402
0
                    library: ByteString(library),
403
0
                });
404
            }
405
0
        }
406
0
        Ok(imports)
407
0
    }
408
409
0
    fn exports(&self) -> Result<Vec<Export<'data>>> {
410
0
        let mut dysymtab = None;
411
0
        let mut commands = self
412
0
            .header
413
0
            .load_commands(self.endian, self.data, self.header_offset)?;
414
0
        while let Some(command) = commands.next()? {
415
0
            if let Some(command) = command.dysymtab()? {
416
0
                dysymtab = Some(command);
417
0
                break;
418
0
            }
419
        }
420
421
0
        let mut exports = Vec::new();
422
0
        if let Some(dysymtab) = dysymtab {
423
0
            let index = dysymtab.iextdefsym.get(self.endian) as usize;
424
0
            let number = dysymtab.nextdefsym.get(self.endian) as usize;
425
0
            for i in index..(index.wrapping_add(number)) {
426
0
                let symbol = self.symbols.symbol(SymbolIndex(i))?;
427
0
                let name = symbol.name(self.endian, self.symbols.strings())?;
428
0
                let address = symbol.n_value(self.endian).into();
429
0
                exports.push(Export {
430
0
                    name: ByteString(name),
431
0
                    address,
432
0
                });
433
            }
434
0
        }
435
0
        Ok(exports)
436
0
    }
437
438
    #[inline]
439
0
    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
440
0
        None
441
0
    }
442
443
0
    fn has_debug_symbols(&self) -> bool {
444
0
        self.section_by_name(".debug_info").is_some()
445
0
    }
446
447
0
    fn mach_uuid(&self) -> Result<Option<[u8; 16]>> {
448
0
        self.header.uuid(self.endian, self.data, self.header_offset)
449
0
    }
450
451
0
    fn relative_address_base(&self) -> u64 {
452
0
        0
453
0
    }
454
455
0
    fn entry(&self) -> u64 {
456
0
        if let Ok(mut commands) =
457
0
            self.header
458
0
                .load_commands(self.endian, self.data, self.header_offset)
459
        {
460
0
            while let Ok(Some(command)) = commands.next() {
461
0
                if let Ok(Some(command)) = command.entry_point() {
462
0
                    return command.entryoff.get(self.endian);
463
0
                }
464
            }
465
0
        }
466
0
        0
467
0
    }
468
469
0
    fn flags(&self) -> FileFlags {
470
0
        FileFlags::MachO {
471
0
            flags: self.header.flags(self.endian),
472
0
        }
473
0
    }
474
}
475
476
/// An iterator for the COMDAT section groups in a [`MachOFile64`].
477
pub 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`].
480
pub 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)]
487
pub struct MachOComdatIterator<'data, 'file, Mach, R = &'data [u8]>
488
where
489
    Mach: MachHeader,
490
    R: ReadRef<'data>,
491
{
492
    #[allow(unused)]
493
    file: &'file MachOFile<'data, Mach, R>,
494
}
495
496
impl<'data, 'file, Mach, R> Iterator for MachOComdatIterator<'data, 'file, Mach, R>
497
where
498
    Mach: MachHeader,
499
    R: ReadRef<'data>,
500
{
501
    type Item = MachOComdat<'data, 'file, Mach, R>;
502
503
    #[inline]
504
0
    fn next(&mut self) -> Option<Self::Item> {
505
0
        None
506
0
    }
507
}
508
509
/// A COMDAT section group in a [`MachOFile32`].
510
pub 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`].
514
pub 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)]
521
pub struct MachOComdat<'data, 'file, Mach, R = &'data [u8]>
522
where
523
    Mach: MachHeader,
524
    R: ReadRef<'data>,
525
{
526
    #[allow(unused)]
527
    file: &'file MachOFile<'data, Mach, R>,
528
}
529
530
impl<'data, 'file, Mach, R> read::private::Sealed for MachOComdat<'data, 'file, Mach, R>
531
where
532
    Mach: MachHeader,
533
    R: ReadRef<'data>,
534
{
535
}
536
537
impl<'data, 'file, Mach, R> ObjectComdat<'data> for MachOComdat<'data, 'file, Mach, R>
538
where
539
    Mach: MachHeader,
540
    R: ReadRef<'data>,
541
{
542
    type SectionIterator = MachOComdatSectionIterator<'data, 'file, Mach, R>;
543
544
    #[inline]
545
0
    fn kind(&self) -> ComdatKind {
546
0
        unreachable!();
547
    }
548
549
    #[inline]
550
0
    fn symbol(&self) -> SymbolIndex {
551
0
        unreachable!();
552
    }
553
554
    #[inline]
555
0
    fn name_bytes(&self) -> Result<&'data [u8]> {
556
0
        unreachable!();
557
    }
558
559
    #[inline]
560
0
    fn name(&self) -> Result<&'data str> {
561
0
        unreachable!();
562
    }
563
564
    #[inline]
565
0
    fn sections(&self) -> Self::SectionIterator {
566
0
        unreachable!();
567
    }
568
}
569
570
/// An iterator for the sections in a COMDAT section group in a [`MachOFile32`].
571
pub 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`].
574
pub 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)]
581
pub struct MachOComdatSectionIterator<'data, 'file, Mach, R = &'data [u8]>
582
where
583
    Mach: MachHeader,
584
    R: ReadRef<'data>,
585
{
586
    #[allow(unused)]
587
    file: &'file MachOFile<'data, Mach, R>,
588
}
589
590
impl<'data, 'file, Mach, R> Iterator for MachOComdatSectionIterator<'data, 'file, Mach, R>
591
where
592
    Mach: MachHeader,
593
    R: ReadRef<'data>,
594
{
595
    type Item = SectionIndex;
596
597
0
    fn next(&mut self) -> Option<Self::Item> {
598
0
        None
599
0
    }
600
}
601
602
/// A trait for generic access to [`macho::MachHeader32`] and [`macho::MachHeader64`].
603
#[allow(missing_docs)]
604
pub 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
0
    fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> {
636
0
        let header = data
637
0
            .read_at::<Self>(offset)
638
0
            .read_error("Invalid Mach-O header size or alignment")?;
639
0
        if !header.is_supported() {
640
0
            return Err(Error("Unsupported Mach-O header"));
641
0
        }
642
0
        Ok(header)
643
0
    }
644
645
0
    fn is_supported(&self) -> bool {
646
0
        self.is_little_endian() || self.is_big_endian()
647
0
    }
648
649
0
    fn endian(&self) -> Result<Self::Endian> {
650
0
        Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported Mach-O endian")
651
0
    }
652
653
0
    fn load_commands<'data, R: ReadRef<'data>>(
654
0
        &self,
655
0
        endian: Self::Endian,
656
0
        data: R,
657
0
        header_offset: u64,
658
0
    ) -> Result<LoadCommandIterator<'data, Self::Endian>> {
659
0
        let data = data
660
0
            .read_bytes_at(
661
0
                header_offset + mem::size_of::<Self>() as u64,
662
0
                self.sizeofcmds(endian).into(),
663
0
            )
664
0
            .read_error("Invalid Mach-O load command table size")?;
665
0
        Ok(LoadCommandIterator::new(endian, data, self.ncmds(endian)))
666
0
    }
667
668
    /// Return the UUID from the `LC_UUID` load command, if one is present.
669
0
    fn uuid<'data, R: ReadRef<'data>>(
670
0
        &self,
671
0
        endian: Self::Endian,
672
0
        data: R,
673
0
        header_offset: u64,
674
0
    ) -> Result<Option<[u8; 16]>> {
675
0
        let mut commands = self.load_commands(endian, data, header_offset)?;
676
0
        while let Some(command) = commands.next()? {
677
0
            if let Ok(Some(uuid)) = command.uuid() {
678
0
                return Ok(Some(uuid.uuid));
679
0
            }
680
        }
681
0
        Ok(None)
682
0
    }
683
}
684
685
impl<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
0
    fn is_type_64(&self) -> bool {
693
0
        false
694
0
    }
695
696
0
    fn is_big_endian(&self) -> bool {
697
0
        self.magic() == macho::MH_MAGIC
698
0
    }
699
700
0
    fn is_little_endian(&self) -> bool {
701
0
        self.magic() == macho::MH_CIGAM
702
0
    }
703
704
0
    fn magic(&self) -> u32 {
705
0
        self.magic.get(BigEndian)
706
0
    }
707
708
0
    fn cputype(&self, endian: Self::Endian) -> u32 {
709
0
        self.cputype.get(endian)
710
0
    }
711
712
0
    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
713
0
        self.cpusubtype.get(endian)
714
0
    }
715
716
0
    fn filetype(&self, endian: Self::Endian) -> u32 {
717
0
        self.filetype.get(endian)
718
0
    }
719
720
0
    fn ncmds(&self, endian: Self::Endian) -> u32 {
721
0
        self.ncmds.get(endian)
722
0
    }
723
724
0
    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
725
0
        self.sizeofcmds.get(endian)
726
0
    }
727
728
0
    fn flags(&self, endian: Self::Endian) -> u32 {
729
0
        self.flags.get(endian)
730
0
    }
731
}
732
733
impl<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
0
    fn is_type_64(&self) -> bool {
741
0
        true
742
0
    }
743
744
0
    fn is_big_endian(&self) -> bool {
745
0
        self.magic() == macho::MH_MAGIC_64
746
0
    }
747
748
0
    fn is_little_endian(&self) -> bool {
749
0
        self.magic() == macho::MH_CIGAM_64
750
0
    }
751
752
0
    fn magic(&self) -> u32 {
753
0
        self.magic.get(BigEndian)
754
0
    }
755
756
0
    fn cputype(&self, endian: Self::Endian) -> u32 {
757
0
        self.cputype.get(endian)
758
0
    }
759
760
0
    fn cpusubtype(&self, endian: Self::Endian) -> u32 {
761
0
        self.cpusubtype.get(endian)
762
0
    }
763
764
0
    fn filetype(&self, endian: Self::Endian) -> u32 {
765
0
        self.filetype.get(endian)
766
0
    }
767
768
0
    fn ncmds(&self, endian: Self::Endian) -> u32 {
769
0
        self.ncmds.get(endian)
770
0
    }
771
772
0
    fn sizeofcmds(&self, endian: Self::Endian) -> u32 {
773
0
        self.sizeofcmds.get(endian)
774
0
    }
775
776
0
    fn flags(&self, endian: Self::Endian) -> u32 {
777
0
        self.flags.get(endian)
778
0
    }
779
}