summaryrefslogtreecommitdiff
path: root/vendor/object/src/write/macho.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/object/src/write/macho.rs')
-rw-r--r--vendor/object/src/write/macho.rs1095
1 files changed, 1095 insertions, 0 deletions
diff --git a/vendor/object/src/write/macho.rs b/vendor/object/src/write/macho.rs
new file mode 100644
index 0000000..1c61523
--- /dev/null
+++ b/vendor/object/src/write/macho.rs
@@ -0,0 +1,1095 @@
+use core::mem;
+
+use crate::endian::*;
+use crate::macho;
+use crate::write::string::*;
+use crate::write::util::*;
+use crate::write::*;
+use crate::AddressSize;
+
+#[derive(Default, Clone, Copy)]
+struct SectionOffsets {
+ index: usize,
+ offset: usize,
+ address: u64,
+ reloc_offset: usize,
+ reloc_count: usize,
+}
+
+#[derive(Default, Clone, Copy)]
+struct SymbolOffsets {
+ index: usize,
+ str_id: Option<StringId>,
+}
+
+/// The customizable portion of a [`macho::BuildVersionCommand`].
+#[derive(Debug, Default, Clone, Copy)]
+#[non_exhaustive] // May want to add the tool list?
+pub struct MachOBuildVersion {
+ /// One of the `PLATFORM_` constants (for example,
+ /// [`object::macho::PLATFORM_MACOS`](macho::PLATFORM_MACOS)).
+ pub platform: u32,
+ /// The minimum OS version, where `X.Y.Z` is encoded in nibbles as
+ /// `xxxx.yy.zz`.
+ pub minos: u32,
+ /// The SDK version as `X.Y.Z`, where `X.Y.Z` is encoded in nibbles as
+ /// `xxxx.yy.zz`.
+ pub sdk: u32,
+}
+
+impl MachOBuildVersion {
+ fn cmdsize(&self) -> u32 {
+ // Same size for both endianness, and we don't have `ntools`.
+ let sz = mem::size_of::<macho::BuildVersionCommand<Endianness>>();
+ debug_assert!(sz <= u32::MAX as usize);
+ sz as u32
+ }
+}
+
+// Public methods.
+impl<'a> Object<'a> {
+ /// Specify the Mach-O CPU subtype.
+ ///
+ /// Requires `feature = "macho"`.
+ #[inline]
+ pub fn set_macho_cpu_subtype(&mut self, cpu_subtype: u32) {
+ self.macho_cpu_subtype = Some(cpu_subtype);
+ }
+
+ /// Specify information for a Mach-O `LC_BUILD_VERSION` command.
+ ///
+ /// Requires `feature = "macho"`.
+ #[inline]
+ pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) {
+ self.macho_build_version = Some(info);
+ }
+}
+
+// Private methods.
+impl<'a> Object<'a> {
+ pub(crate) fn macho_set_subsections_via_symbols(&mut self) {
+ let flags = match self.flags {
+ FileFlags::MachO { flags } => flags,
+ _ => 0,
+ };
+ self.flags = FileFlags::MachO {
+ flags: flags | macho::MH_SUBSECTIONS_VIA_SYMBOLS,
+ };
+ }
+
+ pub(crate) fn macho_segment_name(&self, segment: StandardSegment) -> &'static [u8] {
+ match segment {
+ StandardSegment::Text => &b"__TEXT"[..],
+ StandardSegment::Data => &b"__DATA"[..],
+ StandardSegment::Debug => &b"__DWARF"[..],
+ }
+ }
+
+ pub(crate) fn macho_section_info(
+ &self,
+ section: StandardSection,
+ ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
+ match section {
+ StandardSection::Text => (
+ &b"__TEXT"[..],
+ &b"__text"[..],
+ SectionKind::Text,
+ SectionFlags::None,
+ ),
+ StandardSection::Data => (
+ &b"__DATA"[..],
+ &b"__data"[..],
+ SectionKind::Data,
+ SectionFlags::None,
+ ),
+ StandardSection::ReadOnlyData => (
+ &b"__TEXT"[..],
+ &b"__const"[..],
+ SectionKind::ReadOnlyData,
+ SectionFlags::None,
+ ),
+ StandardSection::ReadOnlyDataWithRel => (
+ &b"__DATA"[..],
+ &b"__const"[..],
+ SectionKind::ReadOnlyDataWithRel,
+ SectionFlags::None,
+ ),
+ StandardSection::ReadOnlyString => (
+ &b"__TEXT"[..],
+ &b"__cstring"[..],
+ SectionKind::ReadOnlyString,
+ SectionFlags::None,
+ ),
+ StandardSection::UninitializedData => (
+ &b"__DATA"[..],
+ &b"__bss"[..],
+ SectionKind::UninitializedData,
+ SectionFlags::None,
+ ),
+ StandardSection::Tls => (
+ &b"__DATA"[..],
+ &b"__thread_data"[..],
+ SectionKind::Tls,
+ SectionFlags::None,
+ ),
+ StandardSection::UninitializedTls => (
+ &b"__DATA"[..],
+ &b"__thread_bss"[..],
+ SectionKind::UninitializedTls,
+ SectionFlags::None,
+ ),
+ StandardSection::TlsVariables => (
+ &b"__DATA"[..],
+ &b"__thread_vars"[..],
+ SectionKind::TlsVariables,
+ SectionFlags::None,
+ ),
+ StandardSection::Common => (
+ &b"__DATA"[..],
+ &b"__common"[..],
+ SectionKind::Common,
+ SectionFlags::None,
+ ),
+ StandardSection::GnuProperty => {
+ // Unsupported section.
+ (&[], &[], SectionKind::Note, SectionFlags::None)
+ }
+ }
+ }
+
+ fn macho_tlv_bootstrap(&mut self) -> SymbolId {
+ match self.tlv_bootstrap {
+ Some(id) => id,
+ None => {
+ let id = self.add_symbol(Symbol {
+ name: b"_tlv_bootstrap".to_vec(),
+ value: 0,
+ size: 0,
+ kind: SymbolKind::Text,
+ scope: SymbolScope::Dynamic,
+ weak: false,
+ section: SymbolSection::Undefined,
+ flags: SymbolFlags::None,
+ });
+ self.tlv_bootstrap = Some(id);
+ id
+ }
+ }
+ }
+
+ /// Create the `__thread_vars` entry for a TLS variable.
+ ///
+ /// The symbol given by `symbol_id` will be updated to point to this entry.
+ ///
+ /// A new `SymbolId` will be returned. The caller must update this symbol
+ /// to point to the initializer.
+ ///
+ /// If `symbol_id` is not for a TLS variable, then it is returned unchanged.
+ pub(crate) fn macho_add_thread_var(&mut self, symbol_id: SymbolId) -> SymbolId {
+ let symbol = self.symbol_mut(symbol_id);
+ if symbol.kind != SymbolKind::Tls {
+ return symbol_id;
+ }
+
+ // Create the initializer symbol.
+ let mut name = symbol.name.clone();
+ name.extend_from_slice(b"$tlv$init");
+ let init_symbol_id = self.add_raw_symbol(Symbol {
+ name,
+ value: 0,
+ size: 0,
+ kind: SymbolKind::Tls,
+ scope: SymbolScope::Compilation,
+ weak: false,
+ section: SymbolSection::Undefined,
+ flags: SymbolFlags::None,
+ });
+
+ // Add the tlv entry.
+ // Three pointers in size:
+ // - __tlv_bootstrap - used to make sure support exists
+ // - spare pointer - used when mapped by the runtime
+ // - pointer to symbol initializer
+ let section = self.section_id(StandardSection::TlsVariables);
+ let address_size = self.architecture.address_size().unwrap().bytes();
+ let size = u64::from(address_size) * 3;
+ let data = vec![0; size as usize];
+ let offset = self.append_section_data(section, &data, u64::from(address_size));
+
+ let tlv_bootstrap = self.macho_tlv_bootstrap();
+ self.add_relocation(
+ section,
+ Relocation {
+ offset,
+ size: address_size * 8,
+ kind: RelocationKind::Absolute,
+ encoding: RelocationEncoding::Generic,
+ symbol: tlv_bootstrap,
+ addend: 0,
+ },
+ )
+ .unwrap();
+ self.add_relocation(
+ section,
+ Relocation {
+ offset: offset + u64::from(address_size) * 2,
+ size: address_size * 8,
+ kind: RelocationKind::Absolute,
+ encoding: RelocationEncoding::Generic,
+ symbol: init_symbol_id,
+ addend: 0,
+ },
+ )
+ .unwrap();
+
+ // Update the symbol to point to the tlv.
+ let symbol = self.symbol_mut(symbol_id);
+ symbol.value = offset;
+ symbol.size = size;
+ symbol.section = SymbolSection::Section(section);
+
+ init_symbol_id
+ }
+
+ pub(crate) fn macho_fixup_relocation(&mut self, relocation: &mut Relocation) -> i64 {
+ let relative = match relocation.kind {
+ RelocationKind::Relative
+ | RelocationKind::GotRelative
+ | RelocationKind::PltRelative
+ | RelocationKind::MachO { relative: true, .. } => true,
+ _ => false,
+ };
+ if relative {
+ // For PC relative relocations on some architectures, the
+ // addend does not include the offset required due to the
+ // PC being different from the place of the relocation.
+ // This differs from other file formats, so adjust the
+ // addend here to account for this.
+ let pcrel_offset = match self.architecture {
+ Architecture::I386 => 4,
+ Architecture::X86_64 => match relocation.kind {
+ RelocationKind::MachO {
+ value: macho::X86_64_RELOC_SIGNED_1,
+ ..
+ } => 5,
+ RelocationKind::MachO {
+ value: macho::X86_64_RELOC_SIGNED_2,
+ ..
+ } => 6,
+ RelocationKind::MachO {
+ value: macho::X86_64_RELOC_SIGNED_4,
+ ..
+ } => 8,
+ _ => 4,
+ },
+ // TODO: maybe missing support for some architectures and relocations
+ _ => 0,
+ };
+ relocation.addend += pcrel_offset;
+ }
+ // Aarch64 relocs of these sizes act as if they are double-word length
+ if self.architecture == Architecture::Aarch64 && matches!(relocation.size, 12 | 21 | 26) {
+ relocation.size = 32;
+ }
+ // Check for relocations that use an explicit addend.
+ if self.architecture == Architecture::Aarch64 {
+ if relocation.encoding == RelocationEncoding::AArch64Call {
+ return 0;
+ }
+ if let RelocationKind::MachO { value, .. } = relocation.kind {
+ match value {
+ macho::ARM64_RELOC_BRANCH26
+ | macho::ARM64_RELOC_PAGE21
+ | macho::ARM64_RELOC_PAGEOFF12 => return 0,
+ _ => {}
+ }
+ }
+ }
+ // Signify implicit addend.
+ let constant = relocation.addend;
+ relocation.addend = 0;
+ constant
+ }
+
+ pub(crate) fn macho_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
+ let address_size = self.architecture.address_size().unwrap();
+ let endian = self.endian;
+ let macho32 = MachO32 { endian };
+ let macho64 = MachO64 { endian };
+ let macho: &dyn MachO = match address_size {
+ AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => &macho32,
+ AddressSize::U64 => &macho64,
+ };
+ let pointer_align = address_size.bytes() as usize;
+
+ // Calculate offsets of everything, and build strtab.
+ let mut offset = 0;
+
+ // Calculate size of Mach-O header.
+ offset += macho.mach_header_size();
+
+ // Calculate size of commands.
+ let mut ncmds = 0;
+ let command_offset = offset;
+
+ // Calculate size of segment command and section headers.
+ let segment_command_offset = offset;
+ let segment_command_len =
+ macho.segment_command_size() + self.sections.len() * macho.section_header_size();
+ offset += segment_command_len;
+ ncmds += 1;
+
+ // Calculate size of build version.
+ let build_version_offset = offset;
+ if let Some(version) = &self.macho_build_version {
+ offset += version.cmdsize() as usize;
+ ncmds += 1;
+ }
+
+ // Calculate size of symtab command.
+ let symtab_command_offset = offset;
+ let symtab_command_len = mem::size_of::<macho::SymtabCommand<Endianness>>();
+ offset += symtab_command_len;
+ ncmds += 1;
+
+ // Calculate size of dysymtab command.
+ let dysymtab_command_offset = offset;
+ let dysymtab_command_len = mem::size_of::<macho::DysymtabCommand<Endianness>>();
+ offset += dysymtab_command_len;
+ ncmds += 1;
+
+ let sizeofcmds = offset - command_offset;
+
+ // Calculate size of section data.
+ // Section data can immediately follow the load commands without any alignment padding.
+ let segment_file_offset = offset;
+ let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
+ let mut address = 0;
+ for (index, section) in self.sections.iter().enumerate() {
+ section_offsets[index].index = 1 + index;
+ if !section.is_bss() {
+ address = align_u64(address, section.align);
+ section_offsets[index].address = address;
+ section_offsets[index].offset = segment_file_offset + address as usize;
+ address += section.size;
+ }
+ }
+ let segment_file_size = address as usize;
+ offset += address as usize;
+ for (index, section) in self.sections.iter().enumerate() {
+ if section.is_bss() {
+ debug_assert!(section.data.is_empty());
+ address = align_u64(address, section.align);
+ section_offsets[index].address = address;
+ address += section.size;
+ }
+ }
+
+ // Partition symbols and add symbol strings to strtab.
+ let mut strtab = StringTable::default();
+ let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
+ let mut local_symbols = vec![];
+ let mut external_symbols = vec![];
+ let mut undefined_symbols = vec![];
+ for (index, symbol) in self.symbols.iter().enumerate() {
+ // The unified API allows creating symbols that we don't emit, so filter
+ // them out here.
+ //
+ // Since we don't actually emit the symbol kind, we validate it here too.
+ match symbol.kind {
+ SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {}
+ SymbolKind::File | SymbolKind::Section => continue,
+ SymbolKind::Null | SymbolKind::Label => {
+ return Err(Error(format!(
+ "unimplemented symbol `{}` kind {:?}",
+ symbol.name().unwrap_or(""),
+ symbol.kind
+ )));
+ }
+ }
+ if !symbol.name.is_empty() {
+ symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
+ }
+ if symbol.is_undefined() {
+ undefined_symbols.push(index);
+ } else if symbol.is_local() {
+ local_symbols.push(index);
+ } else {
+ external_symbols.push(index);
+ }
+ }
+
+ external_symbols.sort_by_key(|index| &*self.symbols[*index].name);
+ undefined_symbols.sort_by_key(|index| &*self.symbols[*index].name);
+
+ // Count symbols.
+ let mut nsyms = 0;
+ for index in local_symbols
+ .iter()
+ .copied()
+ .chain(external_symbols.iter().copied())
+ .chain(undefined_symbols.iter().copied())
+ {
+ symbol_offsets[index].index = nsyms;
+ nsyms += 1;
+ }
+
+ // Calculate size of relocations.
+ for (index, section) in self.sections.iter().enumerate() {
+ let count: usize = section
+ .relocations
+ .iter()
+ .map(|reloc| 1 + usize::from(reloc.addend != 0))
+ .sum();
+ if count != 0 {
+ offset = align(offset, pointer_align);
+ section_offsets[index].reloc_offset = offset;
+ section_offsets[index].reloc_count = count;
+ let len = count * mem::size_of::<macho::Relocation<Endianness>>();
+ offset += len;
+ }
+ }
+
+ // Calculate size of symtab.
+ offset = align(offset, pointer_align);
+ let symtab_offset = offset;
+ let symtab_len = nsyms * macho.nlist_size();
+ offset += symtab_len;
+
+ // Calculate size of strtab.
+ let strtab_offset = offset;
+ // Start with null name.
+ let mut strtab_data = vec![0];
+ strtab.write(1, &mut strtab_data);
+ write_align(&mut strtab_data, pointer_align);
+ offset += strtab_data.len();
+
+ // Start writing.
+ buffer
+ .reserve(offset)
+ .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
+
+ // Write file header.
+ let (cputype, mut cpusubtype) = match (self.architecture, self.sub_architecture) {
+ (Architecture::Arm, None) => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL),
+ (Architecture::Aarch64, None) => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL),
+ (Architecture::Aarch64, Some(SubArchitecture::Arm64E)) => {
+ (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E)
+ }
+ (Architecture::Aarch64_Ilp32, None) => {
+ (macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8)
+ }
+ (Architecture::I386, None) => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL),
+ (Architecture::X86_64, None) => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL),
+ (Architecture::PowerPc, None) => {
+ (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL)
+ }
+ (Architecture::PowerPc64, None) => {
+ (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL)
+ }
+ _ => {
+ return Err(Error(format!(
+ "unimplemented architecture {:?} with sub-architecture {:?}",
+ self.architecture, self.sub_architecture
+ )));
+ }
+ };
+
+ if let Some(cpu_subtype) = self.macho_cpu_subtype {
+ cpusubtype = cpu_subtype;
+ }
+
+ let flags = match self.flags {
+ FileFlags::MachO { flags } => flags,
+ _ => 0,
+ };
+ macho.write_mach_header(
+ buffer,
+ MachHeader {
+ cputype,
+ cpusubtype,
+ filetype: macho::MH_OBJECT,
+ ncmds,
+ sizeofcmds: sizeofcmds as u32,
+ flags,
+ },
+ );
+
+ // Write segment command.
+ debug_assert_eq!(segment_command_offset, buffer.len());
+ macho.write_segment_command(
+ buffer,
+ SegmentCommand {
+ cmdsize: segment_command_len as u32,
+ segname: [0; 16],
+ vmaddr: 0,
+ vmsize: address,
+ fileoff: segment_file_offset as u64,
+ filesize: segment_file_size as u64,
+ maxprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
+ initprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
+ nsects: self.sections.len() as u32,
+ flags: 0,
+ },
+ );
+
+ // Write section headers.
+ for (index, section) in self.sections.iter().enumerate() {
+ let mut sectname = [0; 16];
+ sectname
+ .get_mut(..section.name.len())
+ .ok_or_else(|| {
+ Error(format!(
+ "section name `{}` is too long",
+ section.name().unwrap_or(""),
+ ))
+ })?
+ .copy_from_slice(&section.name);
+ let mut segname = [0; 16];
+ segname
+ .get_mut(..section.segment.len())
+ .ok_or_else(|| {
+ Error(format!(
+ "segment name `{}` is too long",
+ section.segment().unwrap_or(""),
+ ))
+ })?
+ .copy_from_slice(&section.segment);
+ let flags = if let SectionFlags::MachO { flags } = section.flags {
+ flags
+ } else {
+ match section.kind {
+ SectionKind::Text => {
+ macho::S_ATTR_PURE_INSTRUCTIONS | macho::S_ATTR_SOME_INSTRUCTIONS
+ }
+ SectionKind::Data => 0,
+ SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel => 0,
+ SectionKind::ReadOnlyString => macho::S_CSTRING_LITERALS,
+ SectionKind::UninitializedData | SectionKind::Common => macho::S_ZEROFILL,
+ SectionKind::Tls => macho::S_THREAD_LOCAL_REGULAR,
+ SectionKind::UninitializedTls => macho::S_THREAD_LOCAL_ZEROFILL,
+ SectionKind::TlsVariables => macho::S_THREAD_LOCAL_VARIABLES,
+ SectionKind::Debug => macho::S_ATTR_DEBUG,
+ SectionKind::OtherString => macho::S_CSTRING_LITERALS,
+ SectionKind::Other | SectionKind::Linker | SectionKind::Metadata => 0,
+ SectionKind::Note | SectionKind::Unknown | SectionKind::Elf(_) => {
+ return Err(Error(format!(
+ "unimplemented section `{}` kind {:?}",
+ section.name().unwrap_or(""),
+ section.kind
+ )));
+ }
+ }
+ };
+ macho.write_section(
+ buffer,
+ SectionHeader {
+ sectname,
+ segname,
+ addr: section_offsets[index].address,
+ size: section.size,
+ offset: section_offsets[index].offset as u32,
+ align: section.align.trailing_zeros(),
+ reloff: section_offsets[index].reloc_offset as u32,
+ nreloc: section_offsets[index].reloc_count as u32,
+ flags,
+ },
+ );
+ }
+
+ // Write build version.
+ if let Some(version) = &self.macho_build_version {
+ debug_assert_eq!(build_version_offset, buffer.len());
+ buffer.write(&macho::BuildVersionCommand {
+ cmd: U32::new(endian, macho::LC_BUILD_VERSION),
+ cmdsize: U32::new(endian, version.cmdsize()),
+ platform: U32::new(endian, version.platform),
+ minos: U32::new(endian, version.minos),
+ sdk: U32::new(endian, version.sdk),
+ ntools: U32::new(endian, 0),
+ });
+ }
+
+ // Write symtab command.
+ debug_assert_eq!(symtab_command_offset, buffer.len());
+ let symtab_command = macho::SymtabCommand {
+ cmd: U32::new(endian, macho::LC_SYMTAB),
+ cmdsize: U32::new(endian, symtab_command_len as u32),
+ symoff: U32::new(endian, symtab_offset as u32),
+ nsyms: U32::new(endian, nsyms as u32),
+ stroff: U32::new(endian, strtab_offset as u32),
+ strsize: U32::new(endian, strtab_data.len() as u32),
+ };
+ buffer.write(&symtab_command);
+
+ // Write dysymtab command.
+ debug_assert_eq!(dysymtab_command_offset, buffer.len());
+ let dysymtab_command = macho::DysymtabCommand {
+ cmd: U32::new(endian, macho::LC_DYSYMTAB),
+ cmdsize: U32::new(endian, dysymtab_command_len as u32),
+ ilocalsym: U32::new(endian, 0),
+ nlocalsym: U32::new(endian, local_symbols.len() as u32),
+ iextdefsym: U32::new(endian, local_symbols.len() as u32),
+ nextdefsym: U32::new(endian, external_symbols.len() as u32),
+ iundefsym: U32::new(
+ endian,
+ local_symbols.len() as u32 + external_symbols.len() as u32,
+ ),
+ nundefsym: U32::new(endian, undefined_symbols.len() as u32),
+ tocoff: U32::default(),
+ ntoc: U32::default(),
+ modtaboff: U32::default(),
+ nmodtab: U32::default(),
+ extrefsymoff: U32::default(),
+ nextrefsyms: U32::default(),
+ indirectsymoff: U32::default(),
+ nindirectsyms: U32::default(),
+ extreloff: U32::default(),
+ nextrel: U32::default(),
+ locreloff: U32::default(),
+ nlocrel: U32::default(),
+ };
+ buffer.write(&dysymtab_command);
+
+ // Write section data.
+ for (index, section) in self.sections.iter().enumerate() {
+ if !section.is_bss() {
+ buffer.resize(section_offsets[index].offset);
+ buffer.write_bytes(&section.data);
+ }
+ }
+ debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len());
+
+ // Write relocations.
+ for (index, section) in self.sections.iter().enumerate() {
+ if !section.relocations.is_empty() {
+ write_align(buffer, pointer_align);
+ debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
+ for reloc in &section.relocations {
+ let r_length = match reloc.size {
+ 8 => 0,
+ 16 => 1,
+ 32 => 2,
+ 64 => 3,
+ _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))),
+ };
+
+ // Write explicit addend.
+ if reloc.addend != 0 {
+ let r_type = match self.architecture {
+ Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => {
+ macho::ARM64_RELOC_ADDEND
+ }
+ _ => {
+ return Err(Error(format!("unimplemented relocation {:?}", reloc)))
+ }
+ };
+
+ let reloc_info = macho::RelocationInfo {
+ r_address: reloc.offset as u32,
+ r_symbolnum: reloc.addend as u32,
+ r_pcrel: false,
+ r_length,
+ r_extern: false,
+ r_type,
+ };
+ buffer.write(&reloc_info.relocation(endian));
+ }
+
+ let r_extern;
+ let r_symbolnum;
+ let symbol = &self.symbols[reloc.symbol.0];
+ if symbol.kind == SymbolKind::Section {
+ r_symbolnum = section_offsets[symbol.section.id().unwrap().0].index as u32;
+ r_extern = false;
+ } else {
+ r_symbolnum = symbol_offsets[reloc.symbol.0].index as u32;
+ r_extern = true;
+ }
+ let (r_pcrel, r_type) = match self.architecture {
+ Architecture::I386 => match reloc.kind {
+ RelocationKind::Absolute => (false, macho::GENERIC_RELOC_VANILLA),
+ _ => {
+ return Err(Error(format!("unimplemented relocation {:?}", reloc)));
+ }
+ },
+ Architecture::X86_64 => match (reloc.kind, reloc.encoding) {
+ (RelocationKind::Absolute, RelocationEncoding::Generic) => {
+ (false, macho::X86_64_RELOC_UNSIGNED)
+ }
+ (RelocationKind::Relative, RelocationEncoding::Generic) => {
+ (true, macho::X86_64_RELOC_SIGNED)
+ }
+ (RelocationKind::Relative, RelocationEncoding::X86RipRelative) => {
+ (true, macho::X86_64_RELOC_SIGNED)
+ }
+ (RelocationKind::Relative, RelocationEncoding::X86Branch) => {
+ (true, macho::X86_64_RELOC_BRANCH)
+ }
+ (RelocationKind::PltRelative, RelocationEncoding::X86Branch) => {
+ (true, macho::X86_64_RELOC_BRANCH)
+ }
+ (RelocationKind::GotRelative, RelocationEncoding::Generic) => {
+ (true, macho::X86_64_RELOC_GOT)
+ }
+ (
+ RelocationKind::GotRelative,
+ RelocationEncoding::X86RipRelativeMovq,
+ ) => (true, macho::X86_64_RELOC_GOT_LOAD),
+ (RelocationKind::MachO { value, relative }, _) => (relative, value),
+ _ => {
+ return Err(Error(format!("unimplemented relocation {:?}", reloc)));
+ }
+ },
+ Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => {
+ match (reloc.kind, reloc.encoding) {
+ (RelocationKind::Absolute, RelocationEncoding::Generic) => {
+ (false, macho::ARM64_RELOC_UNSIGNED)
+ }
+ (RelocationKind::Relative, RelocationEncoding::AArch64Call) => {
+ (true, macho::ARM64_RELOC_BRANCH26)
+ }
+ (
+ RelocationKind::MachO { value, relative },
+ RelocationEncoding::Generic,
+ ) => (relative, value),
+ _ => {
+ return Err(Error(format!(
+ "unimplemented relocation {:?}",
+ reloc
+ )));
+ }
+ }
+ }
+ _ => {
+ if let RelocationKind::MachO { value, relative } = reloc.kind {
+ (relative, value)
+ } else {
+ return Err(Error(format!("unimplemented relocation {:?}", reloc)));
+ }
+ }
+ };
+ let reloc_info = macho::RelocationInfo {
+ r_address: reloc.offset as u32,
+ r_symbolnum,
+ r_pcrel,
+ r_length,
+ r_extern,
+ r_type,
+ };
+ buffer.write(&reloc_info.relocation(endian));
+ }
+ }
+ }
+
+ // Write symtab.
+ write_align(buffer, pointer_align);
+ debug_assert_eq!(symtab_offset, buffer.len());
+ for index in local_symbols
+ .iter()
+ .copied()
+ .chain(external_symbols.iter().copied())
+ .chain(undefined_symbols.iter().copied())
+ {
+ let symbol = &self.symbols[index];
+ // TODO: N_STAB
+ let (mut n_type, n_sect) = match symbol.section {
+ SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0),
+ SymbolSection::Absolute => (macho::N_ABS, 0),
+ SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1),
+ SymbolSection::None | SymbolSection::Common => {
+ return Err(Error(format!(
+ "unimplemented symbol `{}` section {:?}",
+ symbol.name().unwrap_or(""),
+ symbol.section
+ )));
+ }
+ };
+ match symbol.scope {
+ SymbolScope::Unknown | SymbolScope::Compilation => {}
+ SymbolScope::Linkage => {
+ n_type |= macho::N_EXT | macho::N_PEXT;
+ }
+ SymbolScope::Dynamic => {
+ n_type |= macho::N_EXT;
+ }
+ }
+
+ let n_desc = if let SymbolFlags::MachO { n_desc } = symbol.flags {
+ n_desc
+ } else {
+ let mut n_desc = 0;
+ if symbol.weak {
+ if symbol.is_undefined() {
+ n_desc |= macho::N_WEAK_REF;
+ } else {
+ n_desc |= macho::N_WEAK_DEF;
+ }
+ }
+ n_desc
+ };
+
+ let n_value = match symbol.section.id() {
+ Some(section) => section_offsets[section.0].address + symbol.value,
+ None => symbol.value,
+ };
+
+ let n_strx = symbol_offsets[index]
+ .str_id
+ .map(|id| strtab.get_offset(id))
+ .unwrap_or(0);
+
+ macho.write_nlist(
+ buffer,
+ Nlist {
+ n_strx: n_strx as u32,
+ n_type,
+ n_sect: n_sect as u8,
+ n_desc,
+ n_value,
+ },
+ );
+ }
+
+ // Write strtab.
+ debug_assert_eq!(strtab_offset, buffer.len());
+ buffer.write_bytes(&strtab_data);
+
+ debug_assert_eq!(offset, buffer.len());
+
+ Ok(())
+ }
+}
+
+struct MachHeader {
+ cputype: u32,
+ cpusubtype: u32,
+ filetype: u32,
+ ncmds: u32,
+ sizeofcmds: u32,
+ flags: u32,
+}
+
+struct SegmentCommand {
+ cmdsize: u32,
+ segname: [u8; 16],
+ vmaddr: u64,
+ vmsize: u64,
+ fileoff: u64,
+ filesize: u64,
+ maxprot: u32,
+ initprot: u32,
+ nsects: u32,
+ flags: u32,
+}
+
+pub struct SectionHeader {
+ sectname: [u8; 16],
+ segname: [u8; 16],
+ addr: u64,
+ size: u64,
+ offset: u32,
+ align: u32,
+ reloff: u32,
+ nreloc: u32,
+ flags: u32,
+}
+
+struct Nlist {
+ n_strx: u32,
+ n_type: u8,
+ n_sect: u8,
+ n_desc: u16,
+ n_value: u64,
+}
+
+trait MachO {
+ fn mach_header_size(&self) -> usize;
+ fn segment_command_size(&self) -> usize;
+ fn section_header_size(&self) -> usize;
+ fn nlist_size(&self) -> usize;
+ fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, section: MachHeader);
+ fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand);
+ fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader);
+ fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist);
+}
+
+struct MachO32<E> {
+ endian: E,
+}
+
+impl<E: Endian> MachO for MachO32<E> {
+ fn mach_header_size(&self) -> usize {
+ mem::size_of::<macho::MachHeader32<E>>()
+ }
+
+ fn segment_command_size(&self) -> usize {
+ mem::size_of::<macho::SegmentCommand32<E>>()
+ }
+
+ fn section_header_size(&self) -> usize {
+ mem::size_of::<macho::Section32<E>>()
+ }
+
+ fn nlist_size(&self) -> usize {
+ mem::size_of::<macho::Nlist32<E>>()
+ }
+
+ fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
+ let endian = self.endian;
+ let magic = if endian.is_big_endian() {
+ macho::MH_MAGIC
+ } else {
+ macho::MH_CIGAM
+ };
+ let header = macho::MachHeader32 {
+ magic: U32::new(BigEndian, magic),
+ cputype: U32::new(endian, header.cputype),
+ cpusubtype: U32::new(endian, header.cpusubtype),
+ filetype: U32::new(endian, header.filetype),
+ ncmds: U32::new(endian, header.ncmds),
+ sizeofcmds: U32::new(endian, header.sizeofcmds),
+ flags: U32::new(endian, header.flags),
+ };
+ buffer.write(&header);
+ }
+
+ fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
+ let endian = self.endian;
+ let segment = macho::SegmentCommand32 {
+ cmd: U32::new(endian, macho::LC_SEGMENT),
+ cmdsize: U32::new(endian, segment.cmdsize),
+ segname: segment.segname,
+ vmaddr: U32::new(endian, segment.vmaddr as u32),
+ vmsize: U32::new(endian, segment.vmsize as u32),
+ fileoff: U32::new(endian, segment.fileoff as u32),
+ filesize: U32::new(endian, segment.filesize as u32),
+ maxprot: U32::new(endian, segment.maxprot),
+ initprot: U32::new(endian, segment.initprot),
+ nsects: U32::new(endian, segment.nsects),
+ flags: U32::new(endian, segment.flags),
+ };
+ buffer.write(&segment);
+ }
+
+ fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
+ let endian = self.endian;
+ let section = macho::Section32 {
+ sectname: section.sectname,
+ segname: section.segname,
+ addr: U32::new(endian, section.addr as u32),
+ size: U32::new(endian, section.size as u32),
+ offset: U32::new(endian, section.offset),
+ align: U32::new(endian, section.align),
+ reloff: U32::new(endian, section.reloff),
+ nreloc: U32::new(endian, section.nreloc),
+ flags: U32::new(endian, section.flags),
+ reserved1: U32::default(),
+ reserved2: U32::default(),
+ };
+ buffer.write(&section);
+ }
+
+ fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
+ let endian = self.endian;
+ let nlist = macho::Nlist32 {
+ n_strx: U32::new(endian, nlist.n_strx),
+ n_type: nlist.n_type,
+ n_sect: nlist.n_sect,
+ n_desc: U16::new(endian, nlist.n_desc),
+ n_value: U32::new(endian, nlist.n_value as u32),
+ };
+ buffer.write(&nlist);
+ }
+}
+
+struct MachO64<E> {
+ endian: E,
+}
+
+impl<E: Endian> MachO for MachO64<E> {
+ fn mach_header_size(&self) -> usize {
+ mem::size_of::<macho::MachHeader64<E>>()
+ }
+
+ fn segment_command_size(&self) -> usize {
+ mem::size_of::<macho::SegmentCommand64<E>>()
+ }
+
+ fn section_header_size(&self) -> usize {
+ mem::size_of::<macho::Section64<E>>()
+ }
+
+ fn nlist_size(&self) -> usize {
+ mem::size_of::<macho::Nlist64<E>>()
+ }
+
+ fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
+ let endian = self.endian;
+ let magic = if endian.is_big_endian() {
+ macho::MH_MAGIC_64
+ } else {
+ macho::MH_CIGAM_64
+ };
+ let header = macho::MachHeader64 {
+ magic: U32::new(BigEndian, magic),
+ cputype: U32::new(endian, header.cputype),
+ cpusubtype: U32::new(endian, header.cpusubtype),
+ filetype: U32::new(endian, header.filetype),
+ ncmds: U32::new(endian, header.ncmds),
+ sizeofcmds: U32::new(endian, header.sizeofcmds),
+ flags: U32::new(endian, header.flags),
+ reserved: U32::default(),
+ };
+ buffer.write(&header);
+ }
+
+ fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
+ let endian = self.endian;
+ let segment = macho::SegmentCommand64 {
+ cmd: U32::new(endian, macho::LC_SEGMENT_64),
+ cmdsize: U32::new(endian, segment.cmdsize),
+ segname: segment.segname,
+ vmaddr: U64::new(endian, segment.vmaddr),
+ vmsize: U64::new(endian, segment.vmsize),
+ fileoff: U64::new(endian, segment.fileoff),
+ filesize: U64::new(endian, segment.filesize),
+ maxprot: U32::new(endian, segment.maxprot),
+ initprot: U32::new(endian, segment.initprot),
+ nsects: U32::new(endian, segment.nsects),
+ flags: U32::new(endian, segment.flags),
+ };
+ buffer.write(&segment);
+ }
+
+ fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
+ let endian = self.endian;
+ let section = macho::Section64 {
+ sectname: section.sectname,
+ segname: section.segname,
+ addr: U64::new(endian, section.addr),
+ size: U64::new(endian, section.size),
+ offset: U32::new(endian, section.offset),
+ align: U32::new(endian, section.align),
+ reloff: U32::new(endian, section.reloff),
+ nreloc: U32::new(endian, section.nreloc),
+ flags: U32::new(endian, section.flags),
+ reserved1: U32::default(),
+ reserved2: U32::default(),
+ reserved3: U32::default(),
+ };
+ buffer.write(&section);
+ }
+
+ fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
+ let endian = self.endian;
+ let nlist = macho::Nlist64 {
+ n_strx: U32::new(endian, nlist.n_strx),
+ n_type: nlist.n_type,
+ n_sect: nlist.n_sect,
+ n_desc: U16::new(endian, nlist.n_desc),
+ n_value: U64Bytes::new(endian, nlist.n_value),
+ };
+ buffer.write(&nlist);
+ }
+}