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#[derive(Debug, Default, Clone, Copy)]
26#[non_exhaustive] pub struct MachOBuildVersion {
28 pub platform: u32,
31 pub minos: u32,
34 pub sdk: u32,
37}
38
39impl MachOBuildVersion {
40 fn cmdsize(&self) -> u32 {
41 let sz = mem::size_of::<macho::BuildVersionCommand<Endianness>>();
43 debug_assert!(sz <= u32::MAX as usize);
44 sz as u32
45 }
46}
47
48impl<'a> Object<'a> {
50 #[inline]
54 pub fn set_macho_cpu_subtype(&mut self, cpu_subtype: u32) {
55 self.macho_cpu_subtype = Some(cpu_subtype);
56 }
57
58 #[inline]
62 pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) {
63 self.macho_build_version = Some(info);
64 }
65}
66
67impl<'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 (&[], &[], 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 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 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 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 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 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 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 (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 reloc.subtractor = Some(self.section_symbol(section));
346 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 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 _ => 0,
403 };
404 relocation.addend += pcrel_offset;
405 }
406 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 let mut offset = 0;
441
442 offset += macho.mach_header_size();
444
445 let mut ncmds = 0;
447 let command_offset = offset;
448
449 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 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 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 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 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 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 match symbol.kind {
514 SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {}
515 SymbolKind::Section => {
516 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 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 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 offset = align(offset, pointer_align);
580 let symtab_offset = offset;
581 let symtab_len = nsyms * macho.nlist_size();
582 offset += symtab_len;
583
584 let strtab_offset = offset;
586 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 buffer
594 .reserve(offset)
595 .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
596
597 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 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 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(§ion.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(§ion.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 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 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 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 for (index, section) in self.sections.iter().enumerate() {
765 if !section.is_bss() {
766 buffer.resize(section_offsets[index].offset);
767 buffer.write_bytes(§ion.data);
768 }
769 }
770 debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len());
771
772 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 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 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(§ion.relocations) {
869 for reloc in section.relocations.iter().rev() {
870 write_reloc(reloc)?;
871 }
872 } else {
873 for reloc in §ion.relocations {
874 write_reloc(reloc)?;
875 }
876 }
877 }
878 }
879
880 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 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 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(§ion);
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(§ion);
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}