diff options
Diffstat (limited to 'vendor/object/src/write/macho.rs')
-rw-r--r-- | vendor/object/src/write/macho.rs | 1095 |
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(§ion.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(§ion.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(§ion.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 §ion.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(§ion); + } + + 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(§ion); + } + + 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); + } +} |