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