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