| use core::mem; |
| |
| use crate::endian::*; |
| use crate::macho; |
| use crate::write::string::*; |
| use crate::write::util::*; |
| use crate::write::*; |
| |
| #[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, |
| symbol: tlv_bootstrap, |
| addend: 0, |
| flags: RelocationFlags::Generic { |
| kind: RelocationKind::Absolute, |
| encoding: RelocationEncoding::Generic, |
| size: address_size * 8, |
| }, |
| }, |
| ) |
| .unwrap(); |
| self.add_relocation( |
| section, |
| Relocation { |
| offset: offset + u64::from(address_size) * 2, |
| symbol: init_symbol_id, |
| addend: 0, |
| flags: RelocationFlags::Generic { |
| kind: RelocationKind::Absolute, |
| encoding: RelocationEncoding::Generic, |
| size: address_size * 8, |
| }, |
| }, |
| ) |
| .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_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { |
| let (kind, encoding, mut size) = if let RelocationFlags::Generic { |
| kind, |
| encoding, |
| size, |
| } = reloc.flags |
| { |
| (kind, encoding, size) |
| } else { |
| return Ok(()); |
| }; |
| // Aarch64 relocs of these sizes act as if they are double-word length |
| if self.architecture == Architecture::Aarch64 && matches!(size, 12 | 21 | 26) { |
| size = 32; |
| } |
| let r_length = match size { |
| 8 => 0, |
| 16 => 1, |
| 32 => 2, |
| 64 => 3, |
| _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))), |
| }; |
| let (r_pcrel, r_type) = match self.architecture { |
| Architecture::I386 => match kind { |
| RelocationKind::Absolute => (false, macho::GENERIC_RELOC_VANILLA), |
| _ => { |
| return Err(Error(format!("unimplemented relocation {:?}", reloc))); |
| } |
| }, |
| Architecture::X86_64 => match (kind, 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) |
| } |
| _ => { |
| return Err(Error(format!("unimplemented relocation {:?}", reloc))); |
| } |
| }, |
| Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => match (kind, encoding) { |
| (RelocationKind::Absolute, RelocationEncoding::Generic) => { |
| (false, macho::ARM64_RELOC_UNSIGNED) |
| } |
| (RelocationKind::Relative, RelocationEncoding::AArch64Call) => { |
| (true, macho::ARM64_RELOC_BRANCH26) |
| } |
| _ => { |
| return Err(Error(format!("unimplemented relocation {:?}", reloc))); |
| } |
| }, |
| _ => { |
| return Err(Error(format!("unimplemented relocation {:?}", reloc))); |
| } |
| }; |
| reloc.flags = RelocationFlags::MachO { |
| r_type, |
| r_pcrel, |
| r_length, |
| }; |
| Ok(()) |
| } |
| |
| pub(crate) fn macho_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> { |
| let (r_type, r_pcrel) = if let RelocationFlags::MachO { |
| r_type, r_pcrel, .. |
| } = relocation.flags |
| { |
| (r_type, r_pcrel) |
| } else { |
| return Err(Error(format!("invalid relocation flags {:?}", relocation))); |
| }; |
| if r_pcrel { |
| // 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 r_type { |
| macho::X86_64_RELOC_SIGNED_1 => 5, |
| macho::X86_64_RELOC_SIGNED_2 => 6, |
| macho::X86_64_RELOC_SIGNED_4 => 8, |
| _ => 4, |
| }, |
| // TODO: maybe missing support for some architectures and relocations |
| _ => 0, |
| }; |
| relocation.addend += pcrel_offset; |
| } |
| // Determine if addend is implicit. |
| let implicit = if self.architecture == Architecture::Aarch64 { |
| match r_type { |
| macho::ARM64_RELOC_BRANCH26 |
| | macho::ARM64_RELOC_PAGE21 |
| | macho::ARM64_RELOC_PAGEOFF12 => false, |
| _ => true, |
| } |
| } else { |
| true |
| }; |
| Ok(implicit) |
| } |
| |
| pub(crate) fn macho_relocation_size(&self, reloc: &Relocation) -> Result<u8> { |
| if let RelocationFlags::MachO { r_length, .. } = reloc.flags { |
| Ok(8 << r_length) |
| } else { |
| Err(Error("invalid relocation flags".into())) |
| } |
| } |
| |
| 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_type, r_pcrel, r_length) = if let RelocationFlags::MachO { |
| r_type, |
| r_pcrel, |
| r_length, |
| } = reloc.flags |
| { |
| (r_type, r_pcrel, r_length) |
| } else { |
| return Err(Error("invalid relocation flags".into())); |
| }; |
| |
| // 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 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); |
| } |
| } |