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 | use crate::AddressSize; |
9 | |
10 | #[derive (Default, Clone, Copy)] |
11 | struct 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)] |
20 | struct 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? |
28 | pub 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 | |
40 | impl 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. |
50 | impl<'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. |
69 | impl<'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(§ion.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(§ion.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(§ion.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 §ion.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 | |
864 | struct MachHeader { |
865 | cputype: u32, |
866 | cpusubtype: u32, |
867 | filetype: u32, |
868 | ncmds: u32, |
869 | sizeofcmds: u32, |
870 | flags: u32, |
871 | } |
872 | |
873 | struct 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 | |
886 | pub 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 | |
898 | struct Nlist { |
899 | n_strx: u32, |
900 | n_type: u8, |
901 | n_sect: u8, |
902 | n_desc: u16, |
903 | n_value: u64, |
904 | } |
905 | |
906 | trait 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 | |
917 | struct MachO32<E> { |
918 | endian: E, |
919 | } |
920 | |
921 | impl<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(§ion); |
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 | |
1006 | struct MachO64<E> { |
1007 | endian: E, |
1008 | } |
1009 | |
1010 | impl<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(§ion); |
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 | |