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