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