Skip to main content

object/write/
macho.rs

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