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, } /// 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::>(); 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::>(); offset += symtab_command_len; ncmds += 1; // Calculate size of dysymtab command. let dysymtab_command_offset = offset; let dysymtab_command_len = mem::size_of::>(); 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::>(); 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 { endian: E, } impl MachO for MachO32 { fn mach_header_size(&self) -> usize { mem::size_of::>() } fn segment_command_size(&self) -> usize { mem::size_of::>() } fn section_header_size(&self) -> usize { mem::size_of::>() } fn nlist_size(&self) -> usize { mem::size_of::>() } 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 { endian: E, } impl MachO for MachO64 { fn mach_header_size(&self) -> usize { mem::size_of::>() } fn segment_command_size(&self) -> usize { mem::size_of::>() } fn section_header_size(&self) -> usize { mem::size_of::>() } fn nlist_size(&self) -> usize { mem::size_of::>() } 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); } }