diff options
Diffstat (limited to 'vendor/object/src/write/coff')
-rw-r--r-- | vendor/object/src/write/coff/mod.rs | 10 | ||||
-rw-r--r-- | vendor/object/src/write/coff/object.rs | 583 | ||||
-rw-r--r-- | vendor/object/src/write/coff/writer.rs | 518 |
3 files changed, 1111 insertions, 0 deletions
diff --git a/vendor/object/src/write/coff/mod.rs b/vendor/object/src/write/coff/mod.rs new file mode 100644 index 0000000..6e0f5ed --- /dev/null +++ b/vendor/object/src/write/coff/mod.rs @@ -0,0 +1,10 @@ +//! Support for writing COFF files. +//! +//! Provides [`Writer`] for low level writing of COFF files. +//! This is also used to provide COFF support for [`write::Object`](crate::write::Object). + +mod object; +pub use self::object::*; + +mod writer; +pub use writer::*; diff --git a/vendor/object/src/write/coff/object.rs b/vendor/object/src/write/coff/object.rs new file mode 100644 index 0000000..5229665 --- /dev/null +++ b/vendor/object/src/write/coff/object.rs @@ -0,0 +1,583 @@ +use alloc::vec::Vec; + +use crate::pe as coff; +use crate::write::coff::writer; +use crate::write::util::*; +use crate::write::*; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + name: writer::Name, + offset: u32, + reloc_offset: u32, + selection: u8, + associative_section: u32, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + name: writer::Name, + index: u32, + aux_count: u8, +} + +/// Internal format to use for the `.drectve` section containing linker +/// directives for symbol exports. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CoffExportStyle { + /// MSVC format supported by link.exe and LLD. + Msvc, + /// Gnu format supported by GNU LD and LLD. + Gnu, +} + +impl<'a> Object<'a> { + pub(crate) fn coff_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => ( + &[], + &b".rdata"[..], + SectionKind::ReadOnlyData, + SectionFlags::None, + ), + StandardSection::UninitializedData => ( + &[], + &b".bss"[..], + SectionKind::UninitializedData, + SectionFlags::None, + ), + // TLS sections are data sections with a special name. + StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None), + StandardSection::UninitializedTls => { + // Unsupported section. + (&[], &[], SectionKind::UninitializedTls, SectionFlags::None) + } + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables, SectionFlags::None) + } + StandardSection::Common => { + // Unsupported section. + (&[], &[], SectionKind::Common, SectionFlags::None) + } + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note, SectionFlags::None) + } + } + } + + pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + name.push(b'$'); + name.extend_from_slice(value); + name + } + + pub(crate) fn coff_fixup_relocation(&mut self, relocation: &mut Relocation) -> i64 { + if relocation.kind == RelocationKind::GotRelative { + // Use a stub symbol for the relocation instead. + // This isn't really a GOT, but it's a similar purpose. + // TODO: need to handle DLL imports differently? + relocation.kind = RelocationKind::Relative; + relocation.symbol = self.coff_add_stub_symbol(relocation.symbol); + } else if relocation.kind == RelocationKind::PltRelative { + // Windows doesn't need a separate relocation type for + // references to functions in import libraries. + // For convenience, treat this the same as Relative. + relocation.kind = RelocationKind::Relative; + } + + let constant = match self.architecture { + Architecture::I386 | Architecture::Arm | Architecture::Aarch64 => match relocation.kind + { + RelocationKind::Relative => { + // IMAGE_REL_I386_REL32, IMAGE_REL_ARM_REL32, IMAGE_REL_ARM64_REL32 + relocation.addend + 4 + } + _ => relocation.addend, + }, + Architecture::X86_64 => match relocation.kind { + RelocationKind::Relative => { + // IMAGE_REL_AMD64_REL32 through to IMAGE_REL_AMD64_REL32_5 + if relocation.addend <= -4 && relocation.addend >= -9 { + 0 + } else { + relocation.addend + 4 + } + } + _ => relocation.addend, + }, + _ => unimplemented!(), + }; + relocation.addend -= constant; + constant + } + + fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId { + if let Some(stub_id) = self.stub_symbols.get(&symbol_id) { + return *stub_id; + } + let stub_size = self.architecture.address_size().unwrap().bytes(); + + let name = b".rdata$.refptr".to_vec(); + let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData); + let section = self.section_mut(section_id); + section.set_data(vec![0; stub_size as usize], u64::from(stub_size)); + section.relocations = vec![Relocation { + offset: 0, + size: stub_size * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: symbol_id, + addend: 0, + }]; + + let mut name = b".refptr.".to_vec(); + name.extend_from_slice(&self.symbol(symbol_id).name); + let stub_id = self.add_raw_symbol(Symbol { + name, + value: 0, + size: u64::from(stub_size), + kind: SymbolKind::Data, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + self.stub_symbols.insert(symbol_id, stub_id); + + stub_id + } + + /// Appends linker directives to the `.drectve` section to tell the linker + /// to export all symbols with `SymbolScope::Dynamic`. + /// + /// This must be called after all symbols have been defined. + pub fn add_coff_exports(&mut self, style: CoffExportStyle) { + assert_eq!(self.format, BinaryFormat::Coff); + + let mut directives = vec![]; + for symbol in &self.symbols { + if symbol.scope == SymbolScope::Dynamic { + match style { + CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""), + CoffExportStyle::Gnu => directives.extend(b" -export:\""), + } + directives.extend(&symbol.name); + directives.extend(b"\""); + if symbol.kind != SymbolKind::Text { + match style { + CoffExportStyle::Msvc => directives.extend(b",DATA"), + CoffExportStyle::Gnu => directives.extend(b",data"), + } + } + } + } + let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker); + self.append_section_data(drectve, &directives, 1); + } + + pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { + let mut writer = writer::Writer::new(buffer); + + // Add section strings to strtab. + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].name = writer.add_name(§ion.name); + } + + // Set COMDAT flags. + for comdat in &self.comdats { + let symbol = &self.symbols[comdat.symbol.0]; + let comdat_section = match symbol.section { + SymbolSection::Section(id) => id.0, + _ => { + return Err(Error(format!( + "unsupported COMDAT symbol `{}` section {:?}", + symbol.name().unwrap_or(""), + symbol.section + ))); + } + }; + section_offsets[comdat_section].selection = match comdat.kind { + ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES, + ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY, + ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE, + ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH, + ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST, + ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST, + ComdatKind::Unknown => { + return Err(Error(format!( + "unsupported COMDAT symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + comdat.kind + ))); + } + }; + for id in &comdat.sections { + let section = &self.sections[id.0]; + if section.symbol.is_none() { + return Err(Error(format!( + "missing symbol for COMDAT section `{}`", + section.name().unwrap_or(""), + ))); + } + if id.0 != comdat_section { + section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE; + section_offsets[id.0].associative_section = comdat_section as u32 + 1; + } + } + } + + // Reserve symbol indices and add symbol strings to strtab. + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + for (index, symbol) in self.symbols.iter().enumerate() { + symbol_offsets[index].index = writer.reserve_symbol_index(); + let mut name = &*symbol.name; + match symbol.kind { + SymbolKind::File => { + // Name goes in auxiliary symbol records. + symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name); + name = b".file"; + } + SymbolKind::Section if symbol.section.id().is_some() => { + symbol_offsets[index].aux_count = writer.reserve_aux_section(); + } + _ => {} + }; + symbol_offsets[index].name = writer.add_name(name); + } + + // Reserve file ranges. + writer.reserve_file_header(); + writer.reserve_section_headers(self.sections.len() as u16); + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].offset = writer.reserve_section(section.data.len()); + section_offsets[index].reloc_offset = + writer.reserve_relocations(section.relocations.len()); + } + writer.reserve_symtab_strtab(); + + // Start writing. + writer.write_file_header(writer::FileHeader { + machine: match (self.architecture, self.sub_architecture) { + (Architecture::Arm, None) => coff::IMAGE_FILE_MACHINE_ARMNT, + (Architecture::Aarch64, None) => coff::IMAGE_FILE_MACHINE_ARM64, + (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)) => { + coff::IMAGE_FILE_MACHINE_ARM64EC + } + (Architecture::I386, None) => coff::IMAGE_FILE_MACHINE_I386, + (Architecture::X86_64, None) => coff::IMAGE_FILE_MACHINE_AMD64, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?} with sub-architecture {:?}", + self.architecture, self.sub_architecture + ))); + } + }, + time_date_stamp: 0, + characteristics: match self.flags { + FileFlags::Coff { characteristics } => characteristics, + _ => 0, + }, + })?; + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + let mut characteristics = if let SectionFlags::Coff { + characteristics, .. + } = section.flags + { + characteristics + } else { + match section.kind { + SectionKind::Text => { + coff::IMAGE_SCN_CNT_CODE + | coff::IMAGE_SCN_MEM_EXECUTE + | coff::IMAGE_SCN_MEM_READ + } + SectionKind::Data => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_WRITE + } + SectionKind::UninitializedData => { + coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_WRITE + } + SectionKind::ReadOnlyData + | SectionKind::ReadOnlyDataWithRel + | SectionKind::ReadOnlyString => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ + } + SectionKind::Debug | SectionKind::Other | SectionKind::OtherString => { + coff::IMAGE_SCN_CNT_INITIALIZED_DATA + | coff::IMAGE_SCN_MEM_READ + | coff::IMAGE_SCN_MEM_DISCARDABLE + } + SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE, + SectionKind::Common + | SectionKind::Tls + | SectionKind::UninitializedTls + | SectionKind::TlsVariables + | SectionKind::Note + | SectionKind::Unknown + | SectionKind::Metadata + | SectionKind::Elf(_) => { + return Err(Error(format!( + "unimplemented section `{}` kind {:?}", + section.name().unwrap_or(""), + section.kind + ))); + } + } + }; + if section_offsets[index].selection != 0 { + characteristics |= coff::IMAGE_SCN_LNK_COMDAT; + }; + if section.relocations.len() > 0xffff { + characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL; + } + characteristics |= match section.align { + 1 => coff::IMAGE_SCN_ALIGN_1BYTES, + 2 => coff::IMAGE_SCN_ALIGN_2BYTES, + 4 => coff::IMAGE_SCN_ALIGN_4BYTES, + 8 => coff::IMAGE_SCN_ALIGN_8BYTES, + 16 => coff::IMAGE_SCN_ALIGN_16BYTES, + 32 => coff::IMAGE_SCN_ALIGN_32BYTES, + 64 => coff::IMAGE_SCN_ALIGN_64BYTES, + 128 => coff::IMAGE_SCN_ALIGN_128BYTES, + 256 => coff::IMAGE_SCN_ALIGN_256BYTES, + 512 => coff::IMAGE_SCN_ALIGN_512BYTES, + 1024 => coff::IMAGE_SCN_ALIGN_1024BYTES, + 2048 => coff::IMAGE_SCN_ALIGN_2048BYTES, + 4096 => coff::IMAGE_SCN_ALIGN_4096BYTES, + 8192 => coff::IMAGE_SCN_ALIGN_8192BYTES, + _ => { + return Err(Error(format!( + "unimplemented section `{}` align {}", + section.name().unwrap_or(""), + section.align + ))); + } + }; + writer.write_section_header(writer::SectionHeader { + name: section_offsets[index].name, + size_of_raw_data: section.size as u32, + pointer_to_raw_data: section_offsets[index].offset, + pointer_to_relocations: section_offsets[index].reloc_offset, + pointer_to_linenumbers: 0, + number_of_relocations: section.relocations.len() as u32, + number_of_linenumbers: 0, + characteristics, + }); + } + + // Write section data and relocations. + for section in &self.sections { + writer.write_section(§ion.data); + + if !section.relocations.is_empty() { + //debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + writer.write_relocations_count(section.relocations.len()); + for reloc in §ion.relocations { + //assert!(reloc.implicit_addend); + let typ = match self.architecture { + Architecture::I386 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 16, 0) => coff::IMAGE_REL_I386_DIR16, + (RelocationKind::Relative, 16, 0) => coff::IMAGE_REL_I386_REL16, + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_I386_DIR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_I386_DIR32NB, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_I386_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_I386_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_I386_SECREL7, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_I386_REL32, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::X86_64 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_AMD64_ADDR64, + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32NB, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_AMD64_REL32, + (RelocationKind::Relative, 32, -5) => coff::IMAGE_REL_AMD64_REL32_1, + (RelocationKind::Relative, 32, -6) => coff::IMAGE_REL_AMD64_REL32_2, + (RelocationKind::Relative, 32, -7) => coff::IMAGE_REL_AMD64_REL32_3, + (RelocationKind::Relative, 32, -8) => coff::IMAGE_REL_AMD64_REL32_4, + (RelocationKind::Relative, 32, -9) => coff::IMAGE_REL_AMD64_REL32_5, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_AMD64_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Arm => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM_ADDR32NB, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM_REL32, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM_SECREL, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Aarch64 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32NB, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM64_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM64_SECREL, + (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_ARM64_ADDR64, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM64_REL32, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + writer.write_relocation(writer::Relocation { + virtual_address: reloc.offset as u32, + symbol: symbol_offsets[reloc.symbol.0].index, + typ, + }); + } + } + } + + // Write symbols. + for (index, symbol) in self.symbols.iter().enumerate() { + let section_number = match symbol.section { + SymbolSection::None => { + debug_assert_eq!(symbol.kind, SymbolKind::File); + coff::IMAGE_SYM_DEBUG as u16 + } + SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16, + SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16, + SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16, + SymbolSection::Section(id) => id.0 as u16 + 1, + }; + let typ = if symbol.kind == SymbolKind::Text { + coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT + } else { + coff::IMAGE_SYM_TYPE_NULL + }; + let storage_class = match symbol.kind { + SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE, + SymbolKind::Section => { + if symbol.section.id().is_some() { + coff::IMAGE_SYM_CLASS_STATIC + } else { + coff::IMAGE_SYM_CLASS_SECTION + } + } + SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL, + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => { + match symbol.section { + SymbolSection::None => { + return Err(Error(format!( + "missing section for symbol `{}`", + symbol.name().unwrap_or("") + ))); + } + SymbolSection::Undefined | SymbolSection::Common => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + SymbolSection::Absolute | SymbolSection::Section(_) => { + match symbol.scope { + // TODO: does this need aux symbol records too? + _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL, + SymbolScope::Unknown => { + return Err(Error(format!( + "unimplemented symbol `{}` scope {:?}", + symbol.name().unwrap_or(""), + symbol.scope + ))); + } + SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC, + SymbolScope::Linkage | SymbolScope::Dynamic => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + } + } + } + } + SymbolKind::Unknown | SymbolKind::Null => { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + } + }; + let number_of_aux_symbols = symbol_offsets[index].aux_count; + let value = if symbol.section == SymbolSection::Common { + symbol.size as u32 + } else { + symbol.value as u32 + }; + writer.write_symbol(writer::Symbol { + name: symbol_offsets[index].name, + value, + section_number, + typ, + storage_class, + number_of_aux_symbols, + }); + + // Write auxiliary symbols. + match symbol.kind { + SymbolKind::File => { + writer.write_aux_file_name(&symbol.name, number_of_aux_symbols); + } + SymbolKind::Section if symbol.section.id().is_some() => { + debug_assert_eq!(number_of_aux_symbols, 1); + let section_index = symbol.section.id().unwrap().0; + let section = &self.sections[section_index]; + writer.write_aux_section(writer::AuxSymbolSection { + length: section.size as u32, + number_of_relocations: section.relocations.len() as u32, + number_of_linenumbers: 0, + check_sum: checksum(section.data()), + number: section_offsets[section_index].associative_section, + selection: section_offsets[section_index].selection, + }); + } + _ => { + debug_assert_eq!(number_of_aux_symbols, 0); + } + } + } + + writer.write_strtab(); + + debug_assert_eq!(writer.reserved_len(), writer.len()); + + Ok(()) + } +} + +// JamCRC +fn checksum(data: &[u8]) -> u32 { + let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff); + hasher.update(data); + !hasher.finalize() +} diff --git a/vendor/object/src/write/coff/writer.rs b/vendor/object/src/write/coff/writer.rs new file mode 100644 index 0000000..8c7ada3 --- /dev/null +++ b/vendor/object/src/write/coff/writer.rs @@ -0,0 +1,518 @@ +//! Helper for writing COFF files. +use alloc::string::String; +use alloc::vec::Vec; +use core::mem; + +use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; +use crate::pe; +use crate::write::string::{StringId, StringTable}; +use crate::write::util; +use crate::write::{Error, Result, WritableBuffer}; + +/// A helper for writing COFF files. +/// +/// Writing uses a two phase approach. The first phase builds up all of the information +/// that may need to be known ahead of time: +/// - build string table +/// - reserve section indices +/// - reserve symbol indices +/// - reserve file ranges for headers and sections +/// +/// Some of the information has ordering requirements. For example, strings must be added +/// to the string table before reserving the file range for the string table. There are debug +/// asserts to check some of these requirements. +/// +/// The second phase writes everything out in order. Thus the caller must ensure writing +/// is in the same order that file ranges were reserved. There are debug asserts to assist +/// with checking this. +#[allow(missing_debug_implementations)] +pub struct Writer<'a> { + buffer: &'a mut dyn WritableBuffer, + len: usize, + + section_num: u16, + + symtab_offset: u32, + symtab_num: u32, + + strtab: StringTable<'a>, + strtab_len: usize, + strtab_offset: u32, + strtab_data: Vec<u8>, +} + +impl<'a> Writer<'a> { + /// Create a new `Writer`. + pub fn new(buffer: &'a mut dyn WritableBuffer) -> Self { + Writer { + buffer, + len: 0, + + section_num: 0, + + symtab_offset: 0, + symtab_num: 0, + + strtab: StringTable::default(), + strtab_len: 0, + strtab_offset: 0, + strtab_data: Vec::new(), + } + } + + /// Return the current file length that has been reserved. + pub fn reserved_len(&self) -> usize { + self.len + } + + /// Return the current file length that has been written. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Reserve a file range with the given size and starting alignment. + /// + /// Returns the aligned offset of the start of the range. + pub fn reserve(&mut self, len: usize, align_start: usize) -> u32 { + if align_start > 1 { + self.len = util::align(self.len, align_start); + } + let offset = self.len; + self.len += len; + offset as u32 + } + + /// Write alignment padding bytes. + pub fn write_align(&mut self, align_start: usize) { + if align_start > 1 { + util::write_align(self.buffer, align_start); + } + } + + /// Write data. + pub fn write(&mut self, data: &[u8]) { + self.buffer.write_bytes(data); + } + + /// Reserve the file range up to the given file offset. + pub fn reserve_until(&mut self, offset: usize) { + debug_assert!(self.len <= offset); + self.len = offset; + } + + /// Write padding up to the given file offset. + pub fn pad_until(&mut self, offset: usize) { + debug_assert!(self.buffer.len() <= offset); + self.buffer.resize(offset); + } + + /// Reserve the range for the file header. + /// + /// This must be at the start of the file. + pub fn reserve_file_header(&mut self) { + debug_assert_eq!(self.len, 0); + self.reserve(mem::size_of::<pe::ImageFileHeader>(), 1); + } + + /// Write the file header. + /// + /// This must be at the start of the file. + /// + /// Fields that can be derived from known information are automatically set by this function. + pub fn write_file_header(&mut self, header: FileHeader) -> Result<()> { + debug_assert_eq!(self.buffer.len(), 0); + + // Start writing. + self.buffer + .reserve(self.len) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + let header = pe::ImageFileHeader { + machine: U16::new(LE, header.machine), + number_of_sections: U16::new(LE, self.section_num), + time_date_stamp: U32::new(LE, header.time_date_stamp), + pointer_to_symbol_table: U32::new(LE, self.symtab_offset), + number_of_symbols: U32::new(LE, self.symtab_num), + size_of_optional_header: U16::default(), + characteristics: U16::new(LE, header.characteristics), + }; + self.buffer.write(&header); + + Ok(()) + } + + /// Reserve the range for the section headers. + pub fn reserve_section_headers(&mut self, section_num: u16) { + debug_assert_eq!(self.section_num, 0); + self.section_num = section_num; + self.reserve( + section_num as usize * mem::size_of::<pe::ImageSectionHeader>(), + 1, + ); + } + + /// Write a section header. + pub fn write_section_header(&mut self, section: SectionHeader) { + let mut coff_section = pe::ImageSectionHeader { + name: [0; 8], + virtual_size: U32::default(), + virtual_address: U32::default(), + size_of_raw_data: U32::new(LE, section.size_of_raw_data), + pointer_to_raw_data: U32::new(LE, section.pointer_to_raw_data), + pointer_to_relocations: U32::new(LE, section.pointer_to_relocations), + pointer_to_linenumbers: U32::new(LE, section.pointer_to_linenumbers), + number_of_relocations: if section.number_of_relocations > 0xffff { + U16::new(LE, 0xffff) + } else { + U16::new(LE, section.number_of_relocations as u16) + }, + number_of_linenumbers: U16::default(), + characteristics: U32::new(LE, section.characteristics), + }; + match section.name { + Name::Short(name) => coff_section.name = name, + Name::Long(str_id) => { + let mut str_offset = self.strtab.get_offset(str_id); + if str_offset <= 9_999_999 { + let mut name = [0; 7]; + let mut len = 0; + if str_offset == 0 { + name[6] = b'0'; + len = 1; + } else { + while str_offset != 0 { + let rem = (str_offset % 10) as u8; + str_offset /= 10; + name[6 - len] = b'0' + rem; + len += 1; + } + } + coff_section.name = [0; 8]; + coff_section.name[0] = b'/'; + coff_section.name[1..][..len].copy_from_slice(&name[7 - len..]); + } else { + debug_assert!(str_offset as u64 <= 0xf_ffff_ffff); + coff_section.name[0] = b'/'; + coff_section.name[1] = b'/'; + for i in 0..6 { + let rem = (str_offset % 64) as u8; + str_offset /= 64; + let c = match rem { + 0..=25 => b'A' + rem, + 26..=51 => b'a' + rem - 26, + 52..=61 => b'0' + rem - 52, + 62 => b'+', + 63 => b'/', + _ => unreachable!(), + }; + coff_section.name[7 - i] = c; + } + } + } + } + self.buffer.write(&coff_section); + } + + /// Reserve the range for the section data. + /// + /// Returns the aligned offset of the start of the range. + /// Does nothing and returns 0 if the length is zero. + pub fn reserve_section(&mut self, len: usize) -> u32 { + if len == 0 { + return 0; + } + // TODO: not sure what alignment is required here, but this seems to match LLVM + self.reserve(len, 4) + } + + /// Write the alignment bytes prior to section data. + /// + /// This is unneeded if you are using `write_section` or `write_section_zeroes` + /// for the data. + pub fn write_section_align(&mut self) { + util::write_align(self.buffer, 4); + } + + /// Write the section data. + /// + /// Writes alignment bytes prior to the data. + /// Does nothing if the data is empty. + pub fn write_section(&mut self, data: &[u8]) { + if data.is_empty() { + return; + } + self.write_section_align(); + self.buffer.write_bytes(data); + } + + /// Write the section data using zero bytes. + /// + /// Writes alignment bytes prior to the data. + /// Does nothing if the length is zero. + pub fn write_section_zeroes(&mut self, len: usize) { + if len == 0 { + return; + } + self.write_section_align(); + self.buffer.resize(self.buffer.len() + len); + } + + /// Reserve a file range for the given number of relocations. + /// + /// This will automatically reserve an extra relocation if there are more than 0xffff. + /// + /// Returns the offset of the range. + /// Does nothing and returns 0 if the count is zero. + pub fn reserve_relocations(&mut self, mut count: usize) -> u32 { + if count == 0 { + return 0; + } + if count > 0xffff { + count += 1; + } + self.reserve(count * mem::size_of::<pe::ImageRelocation>(), 1) + } + + /// Write a relocation containing the count if required. + /// + /// This should be called before writing the first relocation for a section. + pub fn write_relocations_count(&mut self, count: usize) { + if count > 0xffff { + let coff_relocation = pe::ImageRelocation { + virtual_address: U32Bytes::new(LE, count as u32 + 1), + symbol_table_index: U32Bytes::new(LE, 0), + typ: U16Bytes::new(LE, 0), + }; + self.buffer.write(&coff_relocation); + } + } + + /// Write a relocation. + pub fn write_relocation(&mut self, reloc: Relocation) { + let coff_relocation = pe::ImageRelocation { + virtual_address: U32Bytes::new(LE, reloc.virtual_address), + symbol_table_index: U32Bytes::new(LE, reloc.symbol), + typ: U16Bytes::new(LE, reloc.typ), + }; + self.buffer.write(&coff_relocation); + } + + /// Reserve a symbol table entry. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_symbol_index(&mut self) -> u32 { + debug_assert_eq!(self.symtab_offset, 0); + let index = self.symtab_num; + self.symtab_num += 1; + index + } + + /// Reserve a number of symbol table entries. + pub fn reserve_symbol_indices(&mut self, count: u32) { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_num += count; + } + + /// Write a symbol table entry. + pub fn write_symbol(&mut self, symbol: Symbol) { + let mut coff_symbol = pe::ImageSymbol { + name: [0; 8], + value: U32Bytes::new(LE, symbol.value), + section_number: U16Bytes::new(LE, symbol.section_number), + typ: U16Bytes::new(LE, symbol.typ), + storage_class: symbol.storage_class, + number_of_aux_symbols: symbol.number_of_aux_symbols, + }; + match symbol.name { + Name::Short(name) => coff_symbol.name = name, + Name::Long(str_id) => { + let str_offset = self.strtab.get_offset(str_id); + coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32)); + } + } + self.buffer.write(&coff_symbol); + } + + /// Reserve auxiliary symbols for a file name. + /// + /// Returns the number of auxiliary symbols required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_aux_file_name(&mut self, name: &[u8]) -> u8 { + debug_assert_eq!(self.symtab_offset, 0); + let aux_count = (name.len() + pe::IMAGE_SIZEOF_SYMBOL - 1) / pe::IMAGE_SIZEOF_SYMBOL; + self.symtab_num += aux_count as u32; + aux_count as u8 + } + + /// Write auxiliary symbols for a file name. + pub fn write_aux_file_name(&mut self, name: &[u8], aux_count: u8) { + let aux_len = aux_count as usize * pe::IMAGE_SIZEOF_SYMBOL; + debug_assert!(aux_len >= name.len()); + let old_len = self.buffer.len(); + self.buffer.write_bytes(name); + self.buffer.resize(old_len + aux_len); + } + + /// Reserve an auxiliary symbol for a section. + /// + /// Returns the number of auxiliary symbols required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn reserve_aux_section(&mut self) -> u8 { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_num += 1; + 1 + } + + /// Write an auxiliary symbol for a section. + pub fn write_aux_section(&mut self, section: AuxSymbolSection) { + let aux = pe::ImageAuxSymbolSection { + length: U32Bytes::new(LE, section.length), + number_of_relocations: if section.number_of_relocations > 0xffff { + U16Bytes::new(LE, 0xffff) + } else { + U16Bytes::new(LE, section.number_of_relocations as u16) + }, + number_of_linenumbers: U16Bytes::new(LE, section.number_of_linenumbers), + check_sum: U32Bytes::new(LE, section.check_sum), + number: U16Bytes::new(LE, section.number as u16), + selection: section.selection, + reserved: 0, + high_number: U16Bytes::new(LE, (section.number >> 16) as u16), + }; + self.buffer.write(&aux); + } + + /// Return the number of reserved symbol table entries. + pub fn symbol_count(&self) -> u32 { + self.symtab_num + } + + /// Add a string to the string table. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn add_string(&mut self, name: &'a [u8]) -> StringId { + debug_assert_eq!(self.strtab_offset, 0); + self.strtab.add(name) + } + + /// Add a section or symbol name to the string table if required. + /// + /// This must be called before [`Self::reserve_symtab_strtab`]. + pub fn add_name(&mut self, name: &'a [u8]) -> Name { + if name.len() > 8 { + Name::Long(self.add_string(name)) + } else { + let mut short_name = [0; 8]; + short_name[..name.len()].copy_from_slice(name); + Name::Short(short_name) + } + } + + /// Reserve the range for the symbol table and string table. + /// + /// This must be called after functions that reserve symbol + /// indices or add strings. + pub fn reserve_symtab_strtab(&mut self) { + debug_assert_eq!(self.symtab_offset, 0); + self.symtab_offset = self.reserve(self.symtab_num as usize * pe::IMAGE_SIZEOF_SYMBOL, 1); + + debug_assert_eq!(self.strtab_offset, 0); + // First 4 bytes of strtab are the length. + self.strtab.write(4, &mut self.strtab_data); + self.strtab_len = self.strtab_data.len() + 4; + self.strtab_offset = self.reserve(self.strtab_len, 1); + } + + /// Write the string table. + pub fn write_strtab(&mut self) { + debug_assert_eq!(self.strtab_offset, self.buffer.len() as u32); + self.buffer + .write_bytes(&u32::to_le_bytes(self.strtab_len as u32)); + self.buffer.write_bytes(&self.strtab_data); + } +} + +/// Shortened and native endian version of [`pe::ImageFileHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct FileHeader { + pub machine: u16, + pub time_date_stamp: u32, + pub characteristics: u16, +} + +/// A section or symbol name. +#[derive(Debug, Clone, Copy)] +pub enum Name { + /// An inline name. + Short([u8; 8]), + /// An id of a string table entry. + Long(StringId), +} + +impl Default for Name { + fn default() -> Name { + Name::Short([0; 8]) + } +} + +// From isn't useful. +#[allow(clippy::from_over_into)] +impl<'a> Into<Name> for &'a [u8; 8] { + fn into(self) -> Name { + Name::Short(*self) + } +} + +/// Native endian version of [`pe::ImageSectionHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct SectionHeader { + pub name: Name, + pub size_of_raw_data: u32, + pub pointer_to_raw_data: u32, + pub pointer_to_relocations: u32, + pub pointer_to_linenumbers: u32, + /// This will automatically be clamped if there are more than 0xffff. + pub number_of_relocations: u32, + pub number_of_linenumbers: u16, + pub characteristics: u32, +} + +/// Native endian version of [`pe::ImageSymbol`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct Symbol { + pub name: Name, + pub value: u32, + pub section_number: u16, + pub typ: u16, + pub storage_class: u8, + pub number_of_aux_symbols: u8, +} + +/// Native endian version of [`pe::ImageAuxSymbolSection`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct AuxSymbolSection { + pub length: u32, + /// This will automatically be clamped if there are more than 0xffff. + pub number_of_relocations: u32, + pub number_of_linenumbers: u16, + pub check_sum: u32, + pub number: u32, + pub selection: u8, +} + +/// Native endian version of [`pe::ImageRelocation`]. +#[allow(missing_docs)] +#[derive(Debug, Default, Clone)] +pub struct Relocation { + pub virtual_address: u32, + pub symbol: u32, + pub typ: u16, +} |