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 }
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 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 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 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 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 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 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 _ => 0,
333 };
334 relocation.addend += pcrel_offset;
335 }
336 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 let mut offset = 0;
371
372 offset += macho.mach_header_size();
374
375 let mut ncmds = 0;
377 let command_offset = offset;
378
379 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 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 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 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 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 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 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 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 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 offset = align(offset, pointer_align);
499 let symtab_offset = offset;
500 let symtab_len = nsyms * macho.nlist_size();
501 offset += symtab_len;
502
503 let strtab_offset = offset;
505 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 buffer
513 .reserve(offset)
514 .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
515
516 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 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 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(§ion.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(§ion.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 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 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 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 for (index, section) in self.sections.iter().enumerate() {
703 if !section.is_bss() {
704 buffer.resize(section_offsets[index].offset);
705 buffer.write_bytes(§ion.data);
706 }
707 }
708 debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len());
709
710 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 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 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(§ion.relocations) {
786 for reloc in section.relocations.iter().rev() {
787 write_reloc(reloc)?;
788 }
789 } else {
790 for reloc in §ion.relocations {
791 write_reloc(reloc)?;
792 }
793 }
794 }
795 }
796
797 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 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 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(§ion);
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(§ion);
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}