Coverage Report

Created: 2026-02-14 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/object-0.38.1/src/write/macho.rs
Line
Count
Source
1
use core::mem;
2
3
use crate::endian::*;
4
use crate::macho;
5
use crate::write::string::*;
6
use crate::write::util::*;
7
use crate::write::*;
8
9
#[derive(Default, Clone, Copy)]
10
struct SectionOffsets {
11
    index: usize,
12
    offset: usize,
13
    address: u64,
14
    reloc_offset: usize,
15
    reloc_count: usize,
16
}
17
18
#[derive(Default, Clone, Copy)]
19
struct SymbolOffsets {
20
    index: usize,
21
    str_id: Option<StringId>,
22
}
23
24
/// The customizable portion of a [`macho::BuildVersionCommand`].
25
#[derive(Debug, Default, Clone, Copy)]
26
#[non_exhaustive] // May want to add the tool list?
27
pub struct MachOBuildVersion {
28
    /// One of the `PLATFORM_` constants (for example,
29
    /// [`object::macho::PLATFORM_MACOS`](macho::PLATFORM_MACOS)).
30
    pub platform: u32,
31
    /// The minimum OS version, where `X.Y.Z` is encoded in nibbles as
32
    /// `xxxx.yy.zz`.
33
    pub minos: u32,
34
    /// The SDK version as `X.Y.Z`, where `X.Y.Z` is encoded in nibbles as
35
    /// `xxxx.yy.zz`.
36
    pub sdk: u32,
37
}
38
39
impl MachOBuildVersion {
40
0
    fn cmdsize(&self) -> u32 {
41
        // Same size for both endianness, and we don't have `ntools`.
42
0
        let sz = mem::size_of::<macho::BuildVersionCommand<Endianness>>();
43
0
        debug_assert!(sz <= u32::MAX as usize);
44
0
        sz as u32
45
0
    }
46
}
47
48
// Public methods.
49
impl<'a> Object<'a> {
50
    /// Specify the Mach-O CPU subtype.
51
    ///
52
    /// Requires `feature = "macho"`.
53
    #[inline]
54
0
    pub fn set_macho_cpu_subtype(&mut self, cpu_subtype: u32) {
55
0
        self.macho_cpu_subtype = Some(cpu_subtype);
56
0
    }
57
58
    /// Specify information for a Mach-O `LC_BUILD_VERSION` command.
59
    ///
60
    /// Requires `feature = "macho"`.
61
    #[inline]
62
0
    pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) {
63
0
        self.macho_build_version = Some(info);
64
0
    }
65
}
66
67
// Private methods.
68
impl<'a> Object<'a> {
69
0
    pub(crate) fn macho_segment_name(&self, segment: StandardSegment) -> &'static [u8] {
70
0
        match segment {
71
0
            StandardSegment::Text => &b"__TEXT"[..],
72
0
            StandardSegment::Data => &b"__DATA"[..],
73
0
            StandardSegment::Debug => &b"__DWARF"[..],
74
        }
75
0
    }
76
77
0
    pub(crate) fn macho_section_info(
78
0
        &self,
79
0
        section: StandardSection,
80
0
    ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
81
0
        match section {
82
0
            StandardSection::Text => (
83
0
                &b"__TEXT"[..],
84
0
                &b"__text"[..],
85
0
                SectionKind::Text,
86
0
                SectionFlags::None,
87
0
            ),
88
0
            StandardSection::Data => (
89
0
                &b"__DATA"[..],
90
0
                &b"__data"[..],
91
0
                SectionKind::Data,
92
0
                SectionFlags::None,
93
0
            ),
94
0
            StandardSection::ReadOnlyData => (
95
0
                &b"__TEXT"[..],
96
0
                &b"__const"[..],
97
0
                SectionKind::ReadOnlyData,
98
0
                SectionFlags::None,
99
0
            ),
100
0
            StandardSection::ReadOnlyDataWithRel => (
101
0
                &b"__DATA"[..],
102
0
                &b"__const"[..],
103
0
                SectionKind::ReadOnlyDataWithRel,
104
0
                SectionFlags::None,
105
0
            ),
106
0
            StandardSection::ReadOnlyString => (
107
0
                &b"__TEXT"[..],
108
0
                &b"__cstring"[..],
109
0
                SectionKind::ReadOnlyString,
110
0
                SectionFlags::None,
111
0
            ),
112
0
            StandardSection::UninitializedData => (
113
0
                &b"__DATA"[..],
114
0
                &b"__bss"[..],
115
0
                SectionKind::UninitializedData,
116
0
                SectionFlags::None,
117
0
            ),
118
0
            StandardSection::Tls => (
119
0
                &b"__DATA"[..],
120
0
                &b"__thread_data"[..],
121
0
                SectionKind::Tls,
122
0
                SectionFlags::None,
123
0
            ),
124
0
            StandardSection::UninitializedTls => (
125
0
                &b"__DATA"[..],
126
0
                &b"__thread_bss"[..],
127
0
                SectionKind::UninitializedTls,
128
0
                SectionFlags::None,
129
0
            ),
130
0
            StandardSection::TlsVariables => (
131
0
                &b"__DATA"[..],
132
0
                &b"__thread_vars"[..],
133
0
                SectionKind::TlsVariables,
134
0
                SectionFlags::None,
135
0
            ),
136
0
            StandardSection::Common => (
137
0
                &b"__DATA"[..],
138
0
                &b"__common"[..],
139
0
                SectionKind::Common,
140
0
                SectionFlags::None,
141
0
            ),
142
            StandardSection::GnuProperty => {
143
                // Unsupported section.
144
0
                (&[], &[], SectionKind::Note, SectionFlags::None)
145
            }
146
        }
147
0
    }
148
149
0
    pub(crate) fn macho_section_flags(&self, section: &Section<'_>) -> SectionFlags {
150
0
        let flags = match section.kind {
151
0
            SectionKind::Text => macho::S_ATTR_PURE_INSTRUCTIONS | macho::S_ATTR_SOME_INSTRUCTIONS,
152
0
            SectionKind::Data => 0,
153
0
            SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel => 0,
154
0
            SectionKind::ReadOnlyString => macho::S_CSTRING_LITERALS,
155
0
            SectionKind::UninitializedData | SectionKind::Common => macho::S_ZEROFILL,
156
0
            SectionKind::Tls => macho::S_THREAD_LOCAL_REGULAR,
157
0
            SectionKind::UninitializedTls => macho::S_THREAD_LOCAL_ZEROFILL,
158
0
            SectionKind::TlsVariables => macho::S_THREAD_LOCAL_VARIABLES,
159
0
            SectionKind::Debug | SectionKind::DebugString => macho::S_ATTR_DEBUG,
160
0
            SectionKind::OtherString => macho::S_CSTRING_LITERALS,
161
0
            SectionKind::Other | SectionKind::Linker | SectionKind::Metadata => 0,
162
            SectionKind::Note | SectionKind::Unknown | SectionKind::Elf(_) => {
163
0
                return SectionFlags::None;
164
            }
165
        };
166
0
        SectionFlags::MachO { flags }
167
0
    }
168
169
0
    pub(crate) fn macho_symbol_flags(&self, symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> {
170
0
        let mut n_desc = 0;
171
0
        if symbol.weak {
172
0
            if symbol.is_undefined() {
173
0
                n_desc |= macho::N_WEAK_REF;
174
0
            } else {
175
0
                n_desc |= macho::N_WEAK_DEF;
176
0
            }
177
0
        }
178
        // TODO: include n_type
179
0
        SymbolFlags::MachO { n_desc }
180
0
    }
181
182
0
    fn macho_tlv_bootstrap(&mut self) -> SymbolId {
183
0
        match self.tlv_bootstrap {
184
0
            Some(id) => id,
185
            None => {
186
0
                let id = self.add_symbol(Symbol {
187
0
                    name: b"_tlv_bootstrap".to_vec(),
188
0
                    value: 0,
189
0
                    size: 0,
190
0
                    kind: SymbolKind::Text,
191
0
                    scope: SymbolScope::Dynamic,
192
0
                    weak: false,
193
0
                    section: SymbolSection::Undefined,
194
0
                    flags: SymbolFlags::None,
195
0
                });
196
0
                self.tlv_bootstrap = Some(id);
197
0
                id
198
            }
199
        }
200
0
    }
201
202
    /// Create the `__thread_vars` entry for a TLS variable.
203
    ///
204
    /// The symbol given by `symbol_id` will be updated to point to this entry.
205
    ///
206
    /// A new `SymbolId` will be returned. The caller must update this symbol
207
    /// to point to the initializer.
208
    ///
209
    /// If `symbol_id` is not for a TLS variable, then it is returned unchanged.
210
0
    pub(crate) fn macho_add_thread_var(&mut self, symbol_id: SymbolId) -> SymbolId {
211
0
        let symbol = self.symbol_mut(symbol_id);
212
0
        if symbol.kind != SymbolKind::Tls {
213
0
            return symbol_id;
214
0
        }
215
216
        // Create the initializer symbol.
217
0
        let mut name = symbol.name.clone();
218
0
        name.extend_from_slice(b"$tlv$init");
219
0
        let init_symbol_id = self.add_raw_symbol(Symbol {
220
0
            name,
221
0
            value: 0,
222
0
            size: 0,
223
0
            kind: SymbolKind::Tls,
224
0
            scope: SymbolScope::Compilation,
225
0
            weak: false,
226
0
            section: SymbolSection::Undefined,
227
0
            flags: SymbolFlags::None,
228
0
        });
229
230
        // Add the tlv entry.
231
        // Three pointers in size:
232
        //   - __tlv_bootstrap - used to make sure support exists
233
        //   - spare pointer - used when mapped by the runtime
234
        //   - pointer to symbol initializer
235
0
        let section = self.section_id(StandardSection::TlsVariables);
236
0
        let address_size = self.architecture.address_size().unwrap().bytes();
237
0
        let size = u64::from(address_size) * 3;
238
0
        let data = vec![0; size as usize];
239
0
        let offset = self.append_section_data(section, &data, u64::from(address_size));
240
241
0
        let tlv_bootstrap = self.macho_tlv_bootstrap();
242
0
        self.add_relocation(
243
0
            section,
244
0
            Relocation {
245
0
                offset,
246
0
                symbol: tlv_bootstrap,
247
0
                addend: 0,
248
0
                flags: RelocationFlags::Generic {
249
0
                    kind: RelocationKind::Absolute,
250
0
                    encoding: RelocationEncoding::Generic,
251
0
                    size: address_size * 8,
252
0
                },
253
0
            },
254
        )
255
0
        .unwrap();
256
0
        self.add_relocation(
257
0
            section,
258
0
            Relocation {
259
0
                offset: offset + u64::from(address_size) * 2,
260
0
                symbol: init_symbol_id,
261
0
                addend: 0,
262
0
                flags: RelocationFlags::Generic {
263
0
                    kind: RelocationKind::Absolute,
264
0
                    encoding: RelocationEncoding::Generic,
265
0
                    size: address_size * 8,
266
0
                },
267
0
            },
268
        )
269
0
        .unwrap();
270
271
        // Update the symbol to point to the tlv.
272
0
        let symbol = self.symbol_mut(symbol_id);
273
0
        symbol.value = offset;
274
0
        symbol.size = size;
275
0
        symbol.section = SymbolSection::Section(section);
276
277
0
        init_symbol_id
278
0
    }
279
280
0
    pub(crate) fn macho_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> {
281
        use RelocationEncoding as E;
282
        use RelocationKind as K;
283
284
0
        let (kind, encoding, mut size) = if let RelocationFlags::Generic {
285
0
            kind,
286
0
            encoding,
287
0
            size,
288
0
        } = reloc.flags
289
        {
290
0
            (kind, encoding, size)
291
        } else {
292
0
            return Ok(());
293
        };
294
        // Aarch64 relocs of these sizes act as if they are double-word length
295
0
        if self.architecture == Architecture::Aarch64 && matches!(size, 12 | 21 | 26) {
296
0
            size = 32;
297
0
        }
298
0
        let r_length = match size {
299
0
            8 => 0,
300
0
            16 => 1,
301
0
            32 => 2,
302
0
            64 => 3,
303
0
            _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))),
304
        };
305
0
        let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc)));
306
0
        let (r_pcrel, r_type) = match self.architecture {
307
0
            Architecture::I386 => match kind {
308
0
                K::Absolute => (false, macho::GENERIC_RELOC_VANILLA),
309
0
                _ => return unsupported_reloc(),
310
            },
311
0
            Architecture::Arm => match kind {
312
0
                K::Absolute => (false, macho::ARM_RELOC_VANILLA),
313
0
                _ => return unsupported_reloc(),
314
            },
315
0
            Architecture::X86_64 => match (kind, encoding) {
316
0
                (K::Absolute, E::Generic) => (false, macho::X86_64_RELOC_UNSIGNED),
317
0
                (K::Relative, E::Generic) => (true, macho::X86_64_RELOC_SIGNED),
318
0
                (K::Relative, E::X86RipRelative) => (true, macho::X86_64_RELOC_SIGNED),
319
0
                (K::Relative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH),
320
0
                (K::PltRelative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH),
321
0
                (K::GotRelative, E::Generic) => (true, macho::X86_64_RELOC_GOT),
322
0
                (K::GotRelative, E::X86RipRelativeMovq) => (true, macho::X86_64_RELOC_GOT_LOAD),
323
0
                _ => return unsupported_reloc(),
324
            },
325
0
            Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => match (kind, encoding) {
326
0
                (K::Absolute, E::Generic) => (false, macho::ARM64_RELOC_UNSIGNED),
327
0
                (K::Relative, E::AArch64Call) => (true, macho::ARM64_RELOC_BRANCH26),
328
0
                _ => return unsupported_reloc(),
329
            },
330
0
            Architecture::PowerPc | Architecture::PowerPc64 => match kind {
331
0
                K::Absolute => (false, macho::PPC_RELOC_VANILLA),
332
0
                _ => return unsupported_reloc(),
333
            },
334
            _ => {
335
0
                return Err(Error(format!(
336
0
                    "unimplemented architecture {:?}",
337
0
                    self.architecture
338
0
                )));
339
            }
340
        };
341
0
        reloc.flags = RelocationFlags::MachO {
342
0
            r_type,
343
0
            r_pcrel,
344
0
            r_length,
345
0
        };
346
0
        Ok(())
347
0
    }
348
349
0
    pub(crate) fn macho_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> {
350
0
        let (r_type, r_pcrel) = if let RelocationFlags::MachO {
351
0
            r_type, r_pcrel, ..
352
0
        } = relocation.flags
353
        {
354
0
            (r_type, r_pcrel)
355
        } else {
356
0
            return Err(Error(format!("invalid relocation flags {:?}", relocation)));
357
        };
358
0
        if r_pcrel {
359
            // For PC relative relocations on some architectures, the
360
            // addend does not include the offset required due to the
361
            // PC being different from the place of the relocation.
362
            // This differs from other file formats, so adjust the
363
            // addend here to account for this.
364
0
            let pcrel_offset = match self.architecture {
365
0
                Architecture::I386 => 4,
366
0
                Architecture::X86_64 => match r_type {
367
0
                    macho::X86_64_RELOC_SIGNED_1 => 5,
368
0
                    macho::X86_64_RELOC_SIGNED_2 => 6,
369
0
                    macho::X86_64_RELOC_SIGNED_4 => 8,
370
0
                    _ => 4,
371
                },
372
                // TODO: maybe missing support for some architectures and relocations
373
0
                _ => 0,
374
            };
375
0
            relocation.addend += pcrel_offset;
376
0
        }
377
        // Determine if addend is implicit.
378
0
        let implicit = if self.architecture == Architecture::Aarch64 {
379
0
            match r_type {
380
                macho::ARM64_RELOC_BRANCH26
381
                | macho::ARM64_RELOC_PAGE21
382
0
                | macho::ARM64_RELOC_PAGEOFF12 => false,
383
0
                _ => true,
384
            }
385
        } else {
386
0
            true
387
        };
388
0
        Ok(implicit)
389
0
    }
390
391
0
    pub(crate) fn macho_relocation_size(&self, reloc: &Relocation) -> Result<u8> {
392
0
        if let RelocationFlags::MachO { r_length, .. } = reloc.flags {
393
0
            Ok(8 << r_length)
394
        } else {
395
0
            Err(Error("invalid relocation flags".into()))
396
        }
397
0
    }
398
399
0
    pub(crate) fn macho_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
400
0
        let address_size = self.architecture.address_size().unwrap();
401
0
        let endian = self.endian;
402
0
        let macho32 = MachO32 { endian };
403
0
        let macho64 = MachO64 { endian };
404
0
        let macho: &dyn MachO = match address_size {
405
0
            AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => &macho32,
406
0
            AddressSize::U64 => &macho64,
407
        };
408
0
        let pointer_align = address_size.bytes() as usize;
409
410
        // Calculate offsets of everything, and build strtab.
411
0
        let mut offset = 0;
412
413
        // Calculate size of Mach-O header.
414
0
        offset += macho.mach_header_size();
415
416
        // Calculate size of commands.
417
0
        let mut ncmds = 0;
418
0
        let command_offset = offset;
419
420
        // Calculate size of segment command and section headers.
421
0
        let segment_command_offset = offset;
422
0
        let segment_command_len =
423
0
            macho.segment_command_size() + self.sections.len() * macho.section_header_size();
424
0
        offset += segment_command_len;
425
0
        ncmds += 1;
426
427
        // Calculate size of build version.
428
0
        let build_version_offset = offset;
429
0
        if let Some(version) = &self.macho_build_version {
430
0
            offset += version.cmdsize() as usize;
431
0
            ncmds += 1;
432
0
        }
433
434
        // Calculate size of symtab command.
435
0
        let symtab_command_offset = offset;
436
0
        let symtab_command_len = mem::size_of::<macho::SymtabCommand<Endianness>>();
437
0
        offset += symtab_command_len;
438
0
        ncmds += 1;
439
440
        // Calculate size of dysymtab command.
441
0
        let dysymtab_command_offset = offset;
442
0
        let dysymtab_command_len = mem::size_of::<macho::DysymtabCommand<Endianness>>();
443
0
        offset += dysymtab_command_len;
444
0
        ncmds += 1;
445
446
0
        let sizeofcmds = offset - command_offset;
447
448
        // Calculate size of section data.
449
        // Section data can immediately follow the load commands without any alignment padding.
450
0
        let segment_file_offset = offset;
451
0
        let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
452
0
        let mut address = 0;
453
0
        for (index, section) in self.sections.iter().enumerate() {
454
0
            section_offsets[index].index = 1 + index;
455
0
            if !section.is_bss() {
456
0
                address = align_u64(address, section.align);
457
0
                section_offsets[index].address = address;
458
0
                section_offsets[index].offset = segment_file_offset + address as usize;
459
0
                address += section.size;
460
0
            }
461
        }
462
0
        let segment_file_size = address as usize;
463
0
        offset += address as usize;
464
0
        for (index, section) in self.sections.iter().enumerate() {
465
0
            if section.is_bss() {
466
0
                debug_assert!(section.data.is_empty());
467
0
                address = align_u64(address, section.align);
468
0
                section_offsets[index].address = address;
469
0
                address += section.size;
470
0
            }
471
        }
472
473
        // Partition symbols and add symbol strings to strtab.
474
0
        let mut strtab = StringTable::default();
475
0
        let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
476
0
        let mut local_symbols = vec![];
477
0
        let mut external_symbols = vec![];
478
0
        let mut undefined_symbols = vec![];
479
0
        for (index, symbol) in self.symbols.iter().enumerate() {
480
            // The unified API allows creating symbols that we don't emit, so filter
481
            // them out here.
482
            //
483
            // Since we don't actually emit the symbol kind, we validate it here too.
484
0
            match symbol.kind {
485
0
                SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {}
486
0
                SymbolKind::File | SymbolKind::Section => continue,
487
                SymbolKind::Label => {
488
0
                    return Err(Error(format!(
489
0
                        "unimplemented symbol `{}` kind {:?}",
490
0
                        symbol.name().unwrap_or(""),
491
0
                        symbol.kind
492
0
                    )));
493
                }
494
            }
495
0
            if !symbol.name.is_empty() {
496
0
                symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
497
0
            }
498
0
            if symbol.is_undefined() {
499
0
                undefined_symbols.push(index);
500
0
            } else if symbol.is_local() {
501
0
                local_symbols.push(index);
502
0
            } else {
503
0
                external_symbols.push(index);
504
0
            }
505
        }
506
507
0
        external_symbols.sort_by_key(|index| &*self.symbols[*index].name);
508
0
        undefined_symbols.sort_by_key(|index| &*self.symbols[*index].name);
509
510
        // Count symbols.
511
0
        let mut nsyms = 0;
512
0
        for index in local_symbols
513
0
            .iter()
514
0
            .copied()
515
0
            .chain(external_symbols.iter().copied())
516
0
            .chain(undefined_symbols.iter().copied())
517
0
        {
518
0
            symbol_offsets[index].index = nsyms;
519
0
            nsyms += 1;
520
0
        }
521
522
        // Calculate size of relocations.
523
0
        for (index, section) in self.sections.iter().enumerate() {
524
0
            let count: usize = section
525
0
                .relocations
526
0
                .iter()
527
0
                .map(|reloc| 1 + usize::from(reloc.addend != 0))
528
0
                .sum();
529
0
            if count != 0 {
530
0
                offset = align(offset, pointer_align);
531
0
                section_offsets[index].reloc_offset = offset;
532
0
                section_offsets[index].reloc_count = count;
533
0
                let len = count * mem::size_of::<macho::Relocation<Endianness>>();
534
0
                offset += len;
535
0
            }
536
        }
537
538
        // Calculate size of symtab.
539
0
        offset = align(offset, pointer_align);
540
0
        let symtab_offset = offset;
541
0
        let symtab_len = nsyms * macho.nlist_size();
542
0
        offset += symtab_len;
543
544
        // Calculate size of strtab.
545
0
        let strtab_offset = offset;
546
        // Start with null name.
547
0
        let mut strtab_data = vec![0];
548
0
        strtab.write(1, &mut strtab_data);
549
0
        write_align(&mut strtab_data, pointer_align);
550
0
        offset += strtab_data.len();
551
552
        // Start writing.
553
0
        buffer
554
0
            .reserve(offset)
555
0
            .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
556
557
        // Write file header.
558
0
        let (cputype, mut cpusubtype) = match (self.architecture, self.sub_architecture) {
559
0
            (Architecture::Arm, None) => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL),
560
0
            (Architecture::Aarch64, None) => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL),
561
            (Architecture::Aarch64, Some(SubArchitecture::Arm64E)) => {
562
0
                (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E)
563
            }
564
            (Architecture::Aarch64_Ilp32, None) => {
565
0
                (macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8)
566
            }
567
0
            (Architecture::I386, None) => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL),
568
0
            (Architecture::X86_64, None) => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL),
569
            (Architecture::PowerPc, None) => {
570
0
                (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL)
571
            }
572
            (Architecture::PowerPc64, None) => {
573
0
                (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL)
574
            }
575
            _ => {
576
0
                return Err(Error(format!(
577
0
                    "unimplemented architecture {:?} with sub-architecture {:?}",
578
0
                    self.architecture, self.sub_architecture
579
0
                )));
580
            }
581
        };
582
583
0
        if let Some(cpu_subtype) = self.macho_cpu_subtype {
584
0
            cpusubtype = cpu_subtype;
585
0
        }
586
587
0
        let mut flags = match self.flags {
588
0
            FileFlags::MachO { flags } => flags,
589
0
            _ => 0,
590
        };
591
0
        if self.macho_subsections_via_symbols {
592
0
            flags |= macho::MH_SUBSECTIONS_VIA_SYMBOLS;
593
0
        }
594
0
        macho.write_mach_header(
595
0
            buffer,
596
0
            MachHeader {
597
0
                cputype,
598
0
                cpusubtype,
599
0
                filetype: macho::MH_OBJECT,
600
0
                ncmds,
601
0
                sizeofcmds: sizeofcmds as u32,
602
0
                flags,
603
0
            },
604
        );
605
606
        // Write segment command.
607
0
        debug_assert_eq!(segment_command_offset, buffer.len());
608
0
        macho.write_segment_command(
609
0
            buffer,
610
0
            SegmentCommand {
611
0
                cmdsize: segment_command_len as u32,
612
0
                segname: [0; 16],
613
0
                vmaddr: 0,
614
0
                vmsize: address,
615
0
                fileoff: segment_file_offset as u64,
616
0
                filesize: segment_file_size as u64,
617
0
                maxprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
618
0
                initprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
619
0
                nsects: self.sections.len() as u32,
620
0
                flags: 0,
621
0
            },
622
        );
623
624
        // Write section headers.
625
0
        for (index, section) in self.sections.iter().enumerate() {
626
0
            let mut sectname = [0; 16];
627
0
            sectname
628
0
                .get_mut(..section.name.len())
629
0
                .ok_or_else(|| {
630
0
                    Error(format!(
631
0
                        "section name `{}` is too long",
632
0
                        section.name().unwrap_or(""),
633
0
                    ))
634
0
                })?
635
0
                .copy_from_slice(&section.name);
636
0
            let mut segname = [0; 16];
637
0
            segname
638
0
                .get_mut(..section.segment.len())
639
0
                .ok_or_else(|| {
640
0
                    Error(format!(
641
0
                        "segment name `{}` is too long",
642
0
                        section.segment().unwrap_or(""),
643
0
                    ))
644
0
                })?
645
0
                .copy_from_slice(&section.segment);
646
0
            let SectionFlags::MachO { flags } = self.section_flags(section) else {
647
0
                return Err(Error(format!(
648
0
                    "unimplemented section `{}` kind {:?}",
649
0
                    section.name().unwrap_or(""),
650
0
                    section.kind
651
0
                )));
652
            };
653
0
            macho.write_section(
654
0
                buffer,
655
0
                SectionHeader {
656
0
                    sectname,
657
0
                    segname,
658
0
                    addr: section_offsets[index].address,
659
0
                    size: section.size,
660
0
                    offset: section_offsets[index].offset as u32,
661
0
                    align: section.align.trailing_zeros(),
662
0
                    reloff: section_offsets[index].reloc_offset as u32,
663
0
                    nreloc: section_offsets[index].reloc_count as u32,
664
0
                    flags,
665
0
                },
666
            );
667
        }
668
669
        // Write build version.
670
0
        if let Some(version) = &self.macho_build_version {
671
0
            debug_assert_eq!(build_version_offset, buffer.len());
672
0
            buffer.write(&macho::BuildVersionCommand {
673
0
                cmd: U32::new(endian, macho::LC_BUILD_VERSION),
674
0
                cmdsize: U32::new(endian, version.cmdsize()),
675
0
                platform: U32::new(endian, version.platform),
676
0
                minos: U32::new(endian, version.minos),
677
0
                sdk: U32::new(endian, version.sdk),
678
0
                ntools: U32::new(endian, 0),
679
0
            });
680
0
        }
681
682
        // Write symtab command.
683
0
        debug_assert_eq!(symtab_command_offset, buffer.len());
684
0
        let symtab_command = macho::SymtabCommand {
685
0
            cmd: U32::new(endian, macho::LC_SYMTAB),
686
0
            cmdsize: U32::new(endian, symtab_command_len as u32),
687
0
            symoff: U32::new(endian, symtab_offset as u32),
688
0
            nsyms: U32::new(endian, nsyms as u32),
689
0
            stroff: U32::new(endian, strtab_offset as u32),
690
0
            strsize: U32::new(endian, strtab_data.len() as u32),
691
0
        };
692
0
        buffer.write(&symtab_command);
693
694
        // Write dysymtab command.
695
0
        debug_assert_eq!(dysymtab_command_offset, buffer.len());
696
0
        let dysymtab_command = macho::DysymtabCommand {
697
0
            cmd: U32::new(endian, macho::LC_DYSYMTAB),
698
0
            cmdsize: U32::new(endian, dysymtab_command_len as u32),
699
0
            ilocalsym: U32::new(endian, 0),
700
0
            nlocalsym: U32::new(endian, local_symbols.len() as u32),
701
0
            iextdefsym: U32::new(endian, local_symbols.len() as u32),
702
0
            nextdefsym: U32::new(endian, external_symbols.len() as u32),
703
0
            iundefsym: U32::new(
704
0
                endian,
705
0
                local_symbols.len() as u32 + external_symbols.len() as u32,
706
0
            ),
707
0
            nundefsym: U32::new(endian, undefined_symbols.len() as u32),
708
0
            tocoff: U32::default(),
709
0
            ntoc: U32::default(),
710
0
            modtaboff: U32::default(),
711
0
            nmodtab: U32::default(),
712
0
            extrefsymoff: U32::default(),
713
0
            nextrefsyms: U32::default(),
714
0
            indirectsymoff: U32::default(),
715
0
            nindirectsyms: U32::default(),
716
0
            extreloff: U32::default(),
717
0
            nextrel: U32::default(),
718
0
            locreloff: U32::default(),
719
0
            nlocrel: U32::default(),
720
0
        };
721
0
        buffer.write(&dysymtab_command);
722
723
        // Write section data.
724
0
        for (index, section) in self.sections.iter().enumerate() {
725
0
            if !section.is_bss() {
726
0
                buffer.resize(section_offsets[index].offset);
727
0
                buffer.write_bytes(&section.data);
728
0
            }
729
        }
730
0
        debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len());
731
732
        // Write relocations.
733
0
        for (index, section) in self.sections.iter().enumerate() {
734
0
            if !section.relocations.is_empty() {
735
0
                write_align(buffer, pointer_align);
736
0
                debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
737
738
0
                let mut write_reloc = |reloc: &Relocation| {
739
0
                    let (r_type, r_pcrel, r_length) = if let RelocationFlags::MachO {
740
0
                        r_type,
741
0
                        r_pcrel,
742
0
                        r_length,
743
0
                    } = reloc.flags
744
                    {
745
0
                        (r_type, r_pcrel, r_length)
746
                    } else {
747
0
                        return Err(Error("invalid relocation flags".into()));
748
                    };
749
750
                    // Write explicit addend.
751
0
                    if reloc.addend != 0 {
752
0
                        let r_type = match self.architecture {
753
                            Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => {
754
0
                                macho::ARM64_RELOC_ADDEND
755
                            }
756
                            _ => {
757
0
                                return Err(Error(format!("unimplemented relocation {:?}", reloc)))
758
                            }
759
                        };
760
761
0
                        let reloc_info = macho::RelocationInfo {
762
0
                            r_address: reloc.offset as u32,
763
0
                            r_symbolnum: reloc.addend as u32,
764
0
                            r_pcrel: false,
765
0
                            r_length,
766
0
                            r_extern: false,
767
0
                            r_type,
768
0
                        };
769
0
                        buffer.write(&reloc_info.relocation(endian));
770
0
                    }
771
772
                    let r_extern;
773
                    let r_symbolnum;
774
0
                    let symbol = &self.symbols[reloc.symbol.0];
775
0
                    if symbol.kind == SymbolKind::Section {
776
0
                        r_symbolnum = section_offsets[symbol.section.id().unwrap().0].index as u32;
777
0
                        r_extern = false;
778
0
                    } else {
779
0
                        r_symbolnum = symbol_offsets[reloc.symbol.0].index as u32;
780
0
                        r_extern = true;
781
0
                    }
782
783
0
                    let reloc_info = macho::RelocationInfo {
784
0
                        r_address: reloc.offset as u32,
785
0
                        r_symbolnum,
786
0
                        r_pcrel,
787
0
                        r_length,
788
0
                        r_extern,
789
0
                        r_type,
790
0
                    };
791
0
                    buffer.write(&reloc_info.relocation(endian));
792
0
                    Ok(())
793
0
                };
794
795
                // Relocations are emitted in descending order as otherwise Apple's
796
                // new linker crashes. This matches LLVM's behavior too:
797
                // https://github.com/llvm/llvm-project/blob/e9b8cd0c8/llvm/lib/MC/MachObjectWriter.cpp#L1001-L1002
798
0
                let need_reverse = |relocs: &[Relocation]| {
799
0
                    let Some(first) = relocs.first() else {
800
0
                        return false;
801
                    };
802
0
                    let Some(last) = relocs.last() else {
803
0
                        return false;
804
                    };
805
0
                    first.offset < last.offset
806
0
                };
807
0
                if need_reverse(&section.relocations) {
808
0
                    for reloc in section.relocations.iter().rev() {
809
0
                        write_reloc(reloc)?;
810
                    }
811
                } else {
812
0
                    for reloc in &section.relocations {
813
0
                        write_reloc(reloc)?;
814
                    }
815
                }
816
0
            }
817
        }
818
819
        // Write symtab.
820
0
        write_align(buffer, pointer_align);
821
0
        debug_assert_eq!(symtab_offset, buffer.len());
822
0
        for index in local_symbols
823
0
            .iter()
824
0
            .copied()
825
0
            .chain(external_symbols.iter().copied())
826
0
            .chain(undefined_symbols.iter().copied())
827
        {
828
0
            let symbol = &self.symbols[index];
829
            // TODO: N_STAB
830
0
            let (mut n_type, n_sect) = match symbol.section {
831
0
                SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0),
832
0
                SymbolSection::Absolute => (macho::N_ABS, 0),
833
0
                SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1),
834
                SymbolSection::None | SymbolSection::Common => {
835
0
                    return Err(Error(format!(
836
0
                        "unimplemented symbol `{}` section {:?}",
837
0
                        symbol.name().unwrap_or(""),
838
0
                        symbol.section
839
0
                    )));
840
                }
841
            };
842
0
            match symbol.scope {
843
0
                SymbolScope::Unknown | SymbolScope::Compilation => {}
844
0
                SymbolScope::Linkage => {
845
0
                    n_type |= macho::N_EXT | macho::N_PEXT;
846
0
                }
847
0
                SymbolScope::Dynamic => {
848
0
                    n_type |= macho::N_EXT;
849
0
                }
850
            }
851
852
0
            let SymbolFlags::MachO { n_desc } = self.symbol_flags(symbol) else {
853
0
                return Err(Error(format!(
854
0
                    "unimplemented symbol `{}` kind {:?}",
855
0
                    symbol.name().unwrap_or(""),
856
0
                    symbol.kind
857
0
                )));
858
            };
859
860
0
            let n_value = match symbol.section.id() {
861
0
                Some(section) => section_offsets[section.0].address + symbol.value,
862
0
                None => symbol.value,
863
            };
864
865
0
            let n_strx = symbol_offsets[index]
866
0
                .str_id
867
0
                .map(|id| strtab.get_offset(id))
868
0
                .unwrap_or(0);
869
870
0
            macho.write_nlist(
871
0
                buffer,
872
0
                Nlist {
873
0
                    n_strx: n_strx as u32,
874
0
                    n_type,
875
0
                    n_sect: n_sect as u8,
876
0
                    n_desc,
877
0
                    n_value,
878
0
                },
879
            );
880
        }
881
882
        // Write strtab.
883
0
        debug_assert_eq!(strtab_offset, buffer.len());
884
0
        buffer.write_bytes(&strtab_data);
885
886
0
        debug_assert_eq!(offset, buffer.len());
887
888
0
        Ok(())
889
0
    }
890
}
891
892
struct MachHeader {
893
    cputype: u32,
894
    cpusubtype: u32,
895
    filetype: u32,
896
    ncmds: u32,
897
    sizeofcmds: u32,
898
    flags: u32,
899
}
900
901
struct SegmentCommand {
902
    cmdsize: u32,
903
    segname: [u8; 16],
904
    vmaddr: u64,
905
    vmsize: u64,
906
    fileoff: u64,
907
    filesize: u64,
908
    maxprot: u32,
909
    initprot: u32,
910
    nsects: u32,
911
    flags: u32,
912
}
913
914
pub struct SectionHeader {
915
    sectname: [u8; 16],
916
    segname: [u8; 16],
917
    addr: u64,
918
    size: u64,
919
    offset: u32,
920
    align: u32,
921
    reloff: u32,
922
    nreloc: u32,
923
    flags: u32,
924
}
925
926
struct Nlist {
927
    n_strx: u32,
928
    n_type: u8,
929
    n_sect: u8,
930
    n_desc: u16,
931
    n_value: u64,
932
}
933
934
trait MachO {
935
    fn mach_header_size(&self) -> usize;
936
    fn segment_command_size(&self) -> usize;
937
    fn section_header_size(&self) -> usize;
938
    fn nlist_size(&self) -> usize;
939
    fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, section: MachHeader);
940
    fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand);
941
    fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader);
942
    fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist);
943
}
944
945
struct MachO32<E> {
946
    endian: E,
947
}
948
949
impl<E: Endian> MachO for MachO32<E> {
950
0
    fn mach_header_size(&self) -> usize {
951
0
        mem::size_of::<macho::MachHeader32<E>>()
952
0
    }
953
954
0
    fn segment_command_size(&self) -> usize {
955
0
        mem::size_of::<macho::SegmentCommand32<E>>()
956
0
    }
957
958
0
    fn section_header_size(&self) -> usize {
959
0
        mem::size_of::<macho::Section32<E>>()
960
0
    }
961
962
0
    fn nlist_size(&self) -> usize {
963
0
        mem::size_of::<macho::Nlist32<E>>()
964
0
    }
965
966
0
    fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
967
0
        let endian = self.endian;
968
0
        let magic = if endian.is_big_endian() {
969
0
            macho::MH_MAGIC
970
        } else {
971
0
            macho::MH_CIGAM
972
        };
973
0
        let header = macho::MachHeader32 {
974
0
            magic: U32::new(BigEndian, magic),
975
0
            cputype: U32::new(endian, header.cputype),
976
0
            cpusubtype: U32::new(endian, header.cpusubtype),
977
0
            filetype: U32::new(endian, header.filetype),
978
0
            ncmds: U32::new(endian, header.ncmds),
979
0
            sizeofcmds: U32::new(endian, header.sizeofcmds),
980
0
            flags: U32::new(endian, header.flags),
981
0
        };
982
0
        buffer.write(&header);
983
0
    }
984
985
0
    fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
986
0
        let endian = self.endian;
987
0
        let segment = macho::SegmentCommand32 {
988
0
            cmd: U32::new(endian, macho::LC_SEGMENT),
989
0
            cmdsize: U32::new(endian, segment.cmdsize),
990
0
            segname: segment.segname,
991
0
            vmaddr: U32::new(endian, segment.vmaddr as u32),
992
0
            vmsize: U32::new(endian, segment.vmsize as u32),
993
0
            fileoff: U32::new(endian, segment.fileoff as u32),
994
0
            filesize: U32::new(endian, segment.filesize as u32),
995
0
            maxprot: U32::new(endian, segment.maxprot),
996
0
            initprot: U32::new(endian, segment.initprot),
997
0
            nsects: U32::new(endian, segment.nsects),
998
0
            flags: U32::new(endian, segment.flags),
999
0
        };
1000
0
        buffer.write(&segment);
1001
0
    }
1002
1003
0
    fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
1004
0
        let endian = self.endian;
1005
0
        let section = macho::Section32 {
1006
0
            sectname: section.sectname,
1007
0
            segname: section.segname,
1008
0
            addr: U32::new(endian, section.addr as u32),
1009
0
            size: U32::new(endian, section.size as u32),
1010
0
            offset: U32::new(endian, section.offset),
1011
0
            align: U32::new(endian, section.align),
1012
0
            reloff: U32::new(endian, section.reloff),
1013
0
            nreloc: U32::new(endian, section.nreloc),
1014
0
            flags: U32::new(endian, section.flags),
1015
0
            reserved1: U32::default(),
1016
0
            reserved2: U32::default(),
1017
0
        };
1018
0
        buffer.write(&section);
1019
0
    }
1020
1021
0
    fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
1022
0
        let endian = self.endian;
1023
0
        let nlist = macho::Nlist32 {
1024
0
            n_strx: U32::new(endian, nlist.n_strx),
1025
0
            n_type: nlist.n_type,
1026
0
            n_sect: nlist.n_sect,
1027
0
            n_desc: U16::new(endian, nlist.n_desc),
1028
0
            n_value: U32::new(endian, nlist.n_value as u32),
1029
0
        };
1030
0
        buffer.write(&nlist);
1031
0
    }
1032
}
1033
1034
struct MachO64<E> {
1035
    endian: E,
1036
}
1037
1038
impl<E: Endian> MachO for MachO64<E> {
1039
0
    fn mach_header_size(&self) -> usize {
1040
0
        mem::size_of::<macho::MachHeader64<E>>()
1041
0
    }
1042
1043
0
    fn segment_command_size(&self) -> usize {
1044
0
        mem::size_of::<macho::SegmentCommand64<E>>()
1045
0
    }
1046
1047
0
    fn section_header_size(&self) -> usize {
1048
0
        mem::size_of::<macho::Section64<E>>()
1049
0
    }
1050
1051
0
    fn nlist_size(&self) -> usize {
1052
0
        mem::size_of::<macho::Nlist64<E>>()
1053
0
    }
1054
1055
0
    fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
1056
0
        let endian = self.endian;
1057
0
        let magic = if endian.is_big_endian() {
1058
0
            macho::MH_MAGIC_64
1059
        } else {
1060
0
            macho::MH_CIGAM_64
1061
        };
1062
0
        let header = macho::MachHeader64 {
1063
0
            magic: U32::new(BigEndian, magic),
1064
0
            cputype: U32::new(endian, header.cputype),
1065
0
            cpusubtype: U32::new(endian, header.cpusubtype),
1066
0
            filetype: U32::new(endian, header.filetype),
1067
0
            ncmds: U32::new(endian, header.ncmds),
1068
0
            sizeofcmds: U32::new(endian, header.sizeofcmds),
1069
0
            flags: U32::new(endian, header.flags),
1070
0
            reserved: U32::default(),
1071
0
        };
1072
0
        buffer.write(&header);
1073
0
    }
1074
1075
0
    fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
1076
0
        let endian = self.endian;
1077
0
        let segment = macho::SegmentCommand64 {
1078
0
            cmd: U32::new(endian, macho::LC_SEGMENT_64),
1079
0
            cmdsize: U32::new(endian, segment.cmdsize),
1080
0
            segname: segment.segname,
1081
0
            vmaddr: U64::new(endian, segment.vmaddr),
1082
0
            vmsize: U64::new(endian, segment.vmsize),
1083
0
            fileoff: U64::new(endian, segment.fileoff),
1084
0
            filesize: U64::new(endian, segment.filesize),
1085
0
            maxprot: U32::new(endian, segment.maxprot),
1086
0
            initprot: U32::new(endian, segment.initprot),
1087
0
            nsects: U32::new(endian, segment.nsects),
1088
0
            flags: U32::new(endian, segment.flags),
1089
0
        };
1090
0
        buffer.write(&segment);
1091
0
    }
1092
1093
0
    fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
1094
0
        let endian = self.endian;
1095
0
        let section = macho::Section64 {
1096
0
            sectname: section.sectname,
1097
0
            segname: section.segname,
1098
0
            addr: U64::new(endian, section.addr),
1099
0
            size: U64::new(endian, section.size),
1100
0
            offset: U32::new(endian, section.offset),
1101
0
            align: U32::new(endian, section.align),
1102
0
            reloff: U32::new(endian, section.reloff),
1103
0
            nreloc: U32::new(endian, section.nreloc),
1104
0
            flags: U32::new(endian, section.flags),
1105
0
            reserved1: U32::default(),
1106
0
            reserved2: U32::default(),
1107
0
            reserved3: U32::default(),
1108
0
        };
1109
0
        buffer.write(&section);
1110
0
    }
1111
1112
0
    fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
1113
0
        let endian = self.endian;
1114
0
        let nlist = macho::Nlist64 {
1115
0
            n_strx: U32::new(endian, nlist.n_strx),
1116
0
            n_type: nlist.n_type,
1117
0
            n_sect: nlist.n_sect,
1118
0
            n_desc: U16::new(endian, nlist.n_desc),
1119
0
            n_value: U64Bytes::new(endian, nlist.n_value),
1120
0
        };
1121
0
        buffer.write(&nlist);
1122
0
    }
1123
}