Source code

Revision control

Copy as Markdown

Other Tools

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,
}
#[derive(Default, Clone, Copy)]
struct SymbolOffsets {
emit: bool,
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 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, mut relocation: &mut Relocation) -> i64 {
let constant = match relocation.kind {
// AArch64Call relocations have special handling for the addend, so don't adjust it
RelocationKind::Relative if relocation.encoding == RelocationEncoding::AArch64Call => 0,
RelocationKind::Relative
| RelocationKind::GotRelative
| RelocationKind::PltRelative => relocation.addend + 4,
_ => relocation.addend,
};
// 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;
}
relocation.addend -= constant;
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;
let build_version_offset = offset;
if let Some(version) = &self.macho_build_version {
offset += version.cmdsize() as usize;
ncmds += 1;
}
// 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 symtab command.
let symtab_command_offset = offset;
let symtab_command_len = mem::size_of::<macho::SymtabCommand<Endianness>>();
offset += symtab_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;
}
}
// Count symbols and add symbol strings to strtab.
let mut strtab = StringTable::default();
let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
let mut nsyms = 0;
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
)));
}
}
symbol_offsets[index].emit = true;
symbol_offsets[index].index = nsyms;
nsyms += 1;
if !symbol.name.is_empty() {
symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
}
}
// 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);
offset += strtab_data.len();
// Calculate size of relocations.
for (index, section) in self.sections.iter().enumerate() {
let count = section.relocations.len();
if count != 0 {
offset = align(offset, 4);
section_offsets[index].reloc_offset = offset;
let len = count * mem::size_of::<macho::Relocation<Endianness>>();
offset += len;
}
}
// Start writing.
buffer
.reserve(offset)
.map_err(|_| Error(String::from("Cannot allocate buffer")))?;
// Write file header.
let (cputype, cpusubtype) = match self.architecture {
Architecture::Arm => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL),
Architecture::Aarch64 => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL),
Architecture::Aarch64_Ilp32 => {
(macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8)
}
Architecture::I386 => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL),
Architecture::X86_64 => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL),
Architecture::PowerPc => (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL),
Architecture::PowerPc64 => (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL),
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
};
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,
},
);
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 segment command.
debug_assert_eq!(segment_command_offset, buffer.len());
macho.write_segment_command(
buffer,
SegmentCommand {
cmdsize: segment_command_len as u32,
segname: [0; 16],
vmaddr: 0,
vmsize: address,
fileoff: segment_file_offset as u64,
filesize: segment_file_size as u64,
maxprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
initprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE,
nsects: self.sections.len() as u32,
flags: 0,
},
);
// Write section headers.
for (index, section) in self.sections.iter().enumerate() {
let mut sectname = [0; 16];
sectname
.get_mut(..section.name.len())
.ok_or_else(|| {
Error(format!(
"section name `{}` is too long",
section.name().unwrap_or(""),
))
})?
.copy_from_slice(&section.name);
let mut segname = [0; 16];
segname
.get_mut(..section.segment.len())
.ok_or_else(|| {
Error(format!(
"segment name `{}` is too long",
section.segment().unwrap_or(""),
))
})?
.copy_from_slice(&section.segment);
let flags = if let SectionFlags::MachO { flags } = section.flags {
flags
} else {
match section.kind {
SectionKind::Text => {
macho::S_ATTR_PURE_INSTRUCTIONS | macho::S_ATTR_SOME_INSTRUCTIONS
}
SectionKind::Data => 0,
SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel => 0,
SectionKind::ReadOnlyString => macho::S_CSTRING_LITERALS,
SectionKind::UninitializedData | SectionKind::Common => macho::S_ZEROFILL,
SectionKind::Tls => macho::S_THREAD_LOCAL_REGULAR,
SectionKind::UninitializedTls => macho::S_THREAD_LOCAL_ZEROFILL,
SectionKind::TlsVariables => macho::S_THREAD_LOCAL_VARIABLES,
SectionKind::Debug => macho::S_ATTR_DEBUG,
SectionKind::OtherString => macho::S_CSTRING_LITERALS,
SectionKind::Other | SectionKind::Linker | SectionKind::Metadata => 0,
SectionKind::Note | SectionKind::Unknown | SectionKind::Elf(_) => {
return Err(Error(format!(
"unimplemented section `{}` kind {:?}",
section.name().unwrap_or(""),
section.kind
)));
}
}
};
macho.write_section(
buffer,
SectionHeader {
sectname,
segname,
addr: section_offsets[index].address,
size: section.size,
offset: section_offsets[index].offset as u32,
align: section.align.trailing_zeros(),
reloff: section_offsets[index].reloc_offset as u32,
nreloc: section.relocations.len() as u32,
flags,
},
);
}
// 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 section data.
for (index, section) in self.sections.iter().enumerate() {
if !section.is_bss() {
buffer.resize(section_offsets[index].offset);
buffer.write_bytes(&section.data);
}
}
debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len());
// Write symtab.
write_align(buffer, pointer_align);
debug_assert_eq!(symtab_offset, buffer.len());
for (index, symbol) in self.symbols.iter().enumerate() {
if !symbol_offsets[index].emit {
continue;
}
// 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);
// Write relocations.
for (index, section) in self.sections.iter().enumerate() {
if !section.relocations.is_empty() {
write_align(buffer, 4);
debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
for reloc in &section.relocations {
let r_extern;
let mut 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_length = match reloc.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 reloc.kind {
RelocationKind::Absolute => (false, macho::GENERIC_RELOC_VANILLA),
_ => {
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
}
},
Architecture::X86_64 => match (reloc.kind, reloc.encoding, reloc.addend) {
(RelocationKind::Absolute, RelocationEncoding::Generic, 0) => {
(false, macho::X86_64_RELOC_UNSIGNED)
}
(RelocationKind::Relative, RelocationEncoding::Generic, -4) => {
(true, macho::X86_64_RELOC_SIGNED)
}
(RelocationKind::Relative, RelocationEncoding::X86RipRelative, -4) => {
(true, macho::X86_64_RELOC_SIGNED)
}
(RelocationKind::Relative, RelocationEncoding::X86Branch, -4) => {
(true, macho::X86_64_RELOC_BRANCH)
}
(RelocationKind::PltRelative, RelocationEncoding::X86Branch, -4) => {
(true, macho::X86_64_RELOC_BRANCH)
}
(RelocationKind::GotRelative, RelocationEncoding::Generic, -4) => {
(true, macho::X86_64_RELOC_GOT)
}
(
RelocationKind::GotRelative,
RelocationEncoding::X86RipRelativeMovq,
-4,
) => (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, reloc.addend) {
(RelocationKind::Absolute, RelocationEncoding::Generic, 0) => {
(false, macho::ARM64_RELOC_UNSIGNED)
}
(RelocationKind::Relative, RelocationEncoding::AArch64Call, 0) => {
(true, macho::ARM64_RELOC_BRANCH26)
}
// Non-zero addend, so we have to encode the addend separately
(
RelocationKind::Relative,
RelocationEncoding::AArch64Call,
value,
) => {
// first emit the BR26 relocation
let reloc_info = macho::RelocationInfo {
r_address: reloc.offset as u32,
r_symbolnum,
r_pcrel: true,
r_length,
r_extern: true,
r_type: macho::ARM64_RELOC_BRANCH26,
};
buffer.write(&reloc_info.relocation(endian));
// set up a separate relocation for the addend
r_symbolnum = value as u32;
(false, macho::ARM64_RELOC_ADDEND)
}
(
RelocationKind::MachO { value, relative },
RelocationEncoding::Generic,
0,
) => (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));
}
}
}
debug_assert_eq!(offset, buffer.len());
Ok(())
}
}
struct MachHeader {
cputype: u32,
cpusubtype: u32,
filetype: u32,
ncmds: u32,
sizeofcmds: u32,
flags: u32,
}
struct SegmentCommand {
cmdsize: u32,
segname: [u8; 16],
vmaddr: u64,
vmsize: u64,
fileoff: u64,
filesize: u64,
maxprot: u32,
initprot: u32,
nsects: u32,
flags: u32,
}
pub struct SectionHeader {
sectname: [u8; 16],
segname: [u8; 16],
addr: u64,
size: u64,
offset: u32,
align: u32,
reloff: u32,
nreloc: u32,
flags: u32,
}
struct Nlist {
n_strx: u32,
n_type: u8,
n_sect: u8,
n_desc: u16,
n_value: u64,
}
trait MachO {
fn mach_header_size(&self) -> usize;
fn segment_command_size(&self) -> usize;
fn section_header_size(&self) -> usize;
fn nlist_size(&self) -> usize;
fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, section: MachHeader);
fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand);
fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader);
fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist);
}
struct MachO32<E> {
endian: E,
}
impl<E: Endian> MachO for MachO32<E> {
fn mach_header_size(&self) -> usize {
mem::size_of::<macho::MachHeader32<E>>()
}
fn segment_command_size(&self) -> usize {
mem::size_of::<macho::SegmentCommand32<E>>()
}
fn section_header_size(&self) -> usize {
mem::size_of::<macho::Section32<E>>()
}
fn nlist_size(&self) -> usize {
mem::size_of::<macho::Nlist32<E>>()
}
fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
let endian = self.endian;
let magic = if endian.is_big_endian() {
macho::MH_MAGIC
} else {
macho::MH_CIGAM
};
let header = macho::MachHeader32 {
magic: U32::new(BigEndian, magic),
cputype: U32::new(endian, header.cputype),
cpusubtype: U32::new(endian, header.cpusubtype),
filetype: U32::new(endian, header.filetype),
ncmds: U32::new(endian, header.ncmds),
sizeofcmds: U32::new(endian, header.sizeofcmds),
flags: U32::new(endian, header.flags),
};
buffer.write(&header);
}
fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
let endian = self.endian;
let segment = macho::SegmentCommand32 {
cmd: U32::new(endian, macho::LC_SEGMENT),
cmdsize: U32::new(endian, segment.cmdsize),
segname: segment.segname,
vmaddr: U32::new(endian, segment.vmaddr as u32),
vmsize: U32::new(endian, segment.vmsize as u32),
fileoff: U32::new(endian, segment.fileoff as u32),
filesize: U32::new(endian, segment.filesize as u32),
maxprot: U32::new(endian, segment.maxprot),
initprot: U32::new(endian, segment.initprot),
nsects: U32::new(endian, segment.nsects),
flags: U32::new(endian, segment.flags),
};
buffer.write(&segment);
}
fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
let endian = self.endian;
let section = macho::Section32 {
sectname: section.sectname,
segname: section.segname,
addr: U32::new(endian, section.addr as u32),
size: U32::new(endian, section.size as u32),
offset: U32::new(endian, section.offset),
align: U32::new(endian, section.align),
reloff: U32::new(endian, section.reloff),
nreloc: U32::new(endian, section.nreloc),
flags: U32::new(endian, section.flags),
reserved1: U32::default(),
reserved2: U32::default(),
};
buffer.write(&section);
}
fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
let endian = self.endian;
let nlist = macho::Nlist32 {
n_strx: U32::new(endian, nlist.n_strx),
n_type: nlist.n_type,
n_sect: nlist.n_sect,
n_desc: U16::new(endian, nlist.n_desc),
n_value: U32::new(endian, nlist.n_value as u32),
};
buffer.write(&nlist);
}
}
struct MachO64<E> {
endian: E,
}
impl<E: Endian> MachO for MachO64<E> {
fn mach_header_size(&self) -> usize {
mem::size_of::<macho::MachHeader64<E>>()
}
fn segment_command_size(&self) -> usize {
mem::size_of::<macho::SegmentCommand64<E>>()
}
fn section_header_size(&self) -> usize {
mem::size_of::<macho::Section64<E>>()
}
fn nlist_size(&self) -> usize {
mem::size_of::<macho::Nlist64<E>>()
}
fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) {
let endian = self.endian;
let magic = if endian.is_big_endian() {
macho::MH_MAGIC_64
} else {
macho::MH_CIGAM_64
};
let header = macho::MachHeader64 {
magic: U32::new(BigEndian, magic),
cputype: U32::new(endian, header.cputype),
cpusubtype: U32::new(endian, header.cpusubtype),
filetype: U32::new(endian, header.filetype),
ncmds: U32::new(endian, header.ncmds),
sizeofcmds: U32::new(endian, header.sizeofcmds),
flags: U32::new(endian, header.flags),
reserved: U32::default(),
};
buffer.write(&header);
}
fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) {
let endian = self.endian;
let segment = macho::SegmentCommand64 {
cmd: U32::new(endian, macho::LC_SEGMENT_64),
cmdsize: U32::new(endian, segment.cmdsize),
segname: segment.segname,
vmaddr: U64::new(endian, segment.vmaddr),
vmsize: U64::new(endian, segment.vmsize),
fileoff: U64::new(endian, segment.fileoff),
filesize: U64::new(endian, segment.filesize),
maxprot: U32::new(endian, segment.maxprot),
initprot: U32::new(endian, segment.initprot),
nsects: U32::new(endian, segment.nsects),
flags: U32::new(endian, segment.flags),
};
buffer.write(&segment);
}
fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) {
let endian = self.endian;
let section = macho::Section64 {
sectname: section.sectname,
segname: section.segname,
addr: U64::new(endian, section.addr),
size: U64::new(endian, section.size),
offset: U32::new(endian, section.offset),
align: U32::new(endian, section.align),
reloff: U32::new(endian, section.reloff),
nreloc: U32::new(endian, section.nreloc),
flags: U32::new(endian, section.flags),
reserved1: U32::default(),
reserved2: U32::default(),
reserved3: U32::default(),
};
buffer.write(&section);
}
fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) {
let endian = self.endian;
let nlist = macho::Nlist64 {
n_strx: U32::new(endian, nlist.n_strx),
n_type: nlist.n_type,
n_sect: nlist.n_sect,
n_desc: U16::new(endian, nlist.n_desc),
n_value: U64Bytes::new(endian, nlist.n_value),
};
buffer.write(&nlist);
}
}