diff --git a/src/write/coff/mod.rs b/src/write/coff/mod.rs new file mode 100644 index 00000000..6e0f5edd --- /dev/null +++ b/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/src/write/coff.rs b/src/write/coff/object.rs similarity index 68% rename from src/write/coff.rs rename to src/write/coff/object.rs index bc75788d..225310ec 100644 --- a/src/write/coff.rs +++ b/src/write/coff/object.rs @@ -1,25 +1,23 @@ use alloc::vec::Vec; -use core::mem; -use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; use crate::pe as coff; -use crate::write::string::*; +use crate::write::coff::writer; use crate::write::util::*; use crate::write::*; #[derive(Default, Clone, Copy)] struct SectionOffsets { - offset: usize, - str_id: Option, - reloc_offset: usize, + name: writer::Name, + offset: u32, + reloc_offset: u32, selection: u8, - associative_section: u16, + associative_section: u32, } #[derive(Default, Clone, Copy)] struct SymbolOffsets { - index: usize, - str_id: Option, + name: writer::Name, + index: u32, aux_count: u8, } @@ -188,42 +186,12 @@ impl<'a> Object<'a> { } pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { - // Calculate offsets of everything, and build strtab. - let mut offset = 0; - let mut strtab = StringTable::default(); + let mut writer = writer::Writer::new(buffer); - // COFF header. - offset += mem::size_of::(); - - // Section headers. - offset += self.sections.len() * mem::size_of::(); - - // Calculate size of section data and add section strings to strtab. + // Add section strings to strtab. let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; for (index, section) in self.sections.iter().enumerate() { - if section.name.len() > 8 { - section_offsets[index].str_id = Some(strtab.add(§ion.name)); - } - - let len = section.data.len(); - if len != 0 { - // TODO: not sure what alignment is required here, but this seems to match LLVM - offset = align(offset, 4); - section_offsets[index].offset = offset; - offset += len; - } else { - section_offsets[index].offset = 0; - } - - // Calculate size of relocations. - let mut count = section.relocations.len(); - if count != 0 { - section_offsets[index].reloc_offset = offset; - if count > 0xffff { - count += 1; - } - offset += count * mem::size_of::(); - } + section_offsets[index].name = writer.add_name(§ion.name); } // Set COMDAT flags. @@ -264,84 +232,60 @@ impl<'a> Object<'a> { } if id.0 != comdat_section { section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE; - section_offsets[id.0].associative_section = comdat_section as u16 + 1; + section_offsets[id.0].associative_section = comdat_section as u32 + 1; } } } - // Calculate size of symbols and add symbol strings to strtab. + // Reserve symbol indices and add symbol strings to strtab. let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; - let mut symtab_count = 0; for (index, symbol) in self.symbols.iter().enumerate() { - symbol_offsets[index].index = symtab_count; - symtab_count += 1; + symbol_offsets[index].index = writer.reserve_symbol_index(); + let mut name = &*symbol.name; match symbol.kind { SymbolKind::File => { // Name goes in auxiliary symbol records. - let aux_count = (symbol.name.len() + coff::IMAGE_SIZEOF_SYMBOL - 1) - / coff::IMAGE_SIZEOF_SYMBOL; - symbol_offsets[index].aux_count = aux_count as u8; - symtab_count += aux_count; - // Don't add name to strtab. - continue; + 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 = 1; - symtab_count += 1; + symbol_offsets[index].aux_count = writer.reserve_aux_section(); } _ => {} - } - if symbol.name.len() > 8 { - symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); - } + }; + symbol_offsets[index].name = writer.add_name(name); } - // Calculate size of symtab. - let symtab_offset = offset; - let symtab_len = symtab_count * coff::IMAGE_SIZEOF_SYMBOL; - offset += symtab_len; - - // Calculate size of strtab. - let strtab_offset = offset; - let mut strtab_data = Vec::new(); - // First 4 bytes of strtab are the length. - strtab.write(4, &mut strtab_data); - let strtab_len = strtab_data.len() + 4; - offset += strtab_len; + // 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. - buffer - .reserve(offset) - .map_err(|_| Error(String::from("Cannot allocate buffer")))?; - - // Write file header. - let header = coff::ImageFileHeader { - machine: U16::new( - LE, - match self.architecture { - Architecture::Arm => coff::IMAGE_FILE_MACHINE_ARMNT, - Architecture::Aarch64 => coff::IMAGE_FILE_MACHINE_ARM64, - Architecture::I386 => coff::IMAGE_FILE_MACHINE_I386, - Architecture::X86_64 => coff::IMAGE_FILE_MACHINE_AMD64, - _ => { - return Err(Error(format!( - "unimplemented architecture {:?}", - self.architecture - ))); - } - }, - ), - number_of_sections: U16::new(LE, self.sections.len() as u16), - time_date_stamp: U32::default(), - pointer_to_symbol_table: U32::new(LE, symtab_offset as u32), - number_of_symbols: U32::new(LE, symtab_count as u32), - size_of_optional_header: U16::default(), + writer.write_file_header(writer::FileHeader { + machine: match self.architecture { + Architecture::Arm => coff::IMAGE_FILE_MACHINE_ARMNT, + Architecture::Aarch64 => coff::IMAGE_FILE_MACHINE_ARM64, + Architecture::I386 => coff::IMAGE_FILE_MACHINE_I386, + Architecture::X86_64 => coff::IMAGE_FILE_MACHINE_AMD64, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }, + time_date_stamp: 0, characteristics: match self.flags { - FileFlags::Coff { characteristics } => U16::new(LE, characteristics), - _ => U16::default(), + FileFlags::Coff { characteristics } => characteristics, + _ => 0, }, - }; - buffer.write(&header); + })?; // Write section headers. for (index, section) in self.sections.iter().enumerate() { @@ -423,85 +367,25 @@ impl<'a> Object<'a> { ))); } }; - let mut coff_section = coff::ImageSectionHeader { - name: [0; 8], - virtual_size: U32::default(), - virtual_address: U32::default(), - size_of_raw_data: U32::new(LE, section.size as u32), - pointer_to_raw_data: U32::new(LE, section_offsets[index].offset as u32), - pointer_to_relocations: U32::new(LE, section_offsets[index].reloc_offset as u32), - pointer_to_linenumbers: U32::default(), - number_of_relocations: if section.relocations.len() > 0xffff { - U16::new(LE, 0xffff) - } else { - U16::new(LE, section.relocations.len() as u16) - }, - number_of_linenumbers: U16::default(), - characteristics: U32::new(LE, characteristics), - }; - if section.name.len() <= 8 { - coff_section.name[..section.name.len()].copy_from_slice(§ion.name); - } else { - let mut str_offset = strtab.get_offset(section_offsets[index].str_id.unwrap()); - 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 if 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; - } - } else { - return Err(Error(format!("invalid section name offset {}", str_offset))); - } - } - buffer.write(&coff_section); + 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 (index, section) in self.sections.iter().enumerate() { - let len = section.data.len(); - if len != 0 { - write_align(buffer, 4); - debug_assert_eq!(section_offsets[index].offset, buffer.len()); - buffer.write_bytes(§ion.data); - } + 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()); - if section.relocations.len() > 0xffff { - let coff_relocation = coff::ImageRelocation { - virtual_address: U32Bytes::new(LE, section.relocations.len() as u32 + 1), - symbol_table_index: U32Bytes::new(LE, 0), - typ: U16Bytes::new(LE, 0), - }; - buffer.write(&coff_relocation); - } + //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 { @@ -567,23 +451,17 @@ impl<'a> Object<'a> { ))); } }; - let coff_relocation = coff::ImageRelocation { - virtual_address: U32Bytes::new(LE, reloc.offset as u32), - symbol_table_index: U32Bytes::new( - LE, - symbol_offsets[reloc.symbol.0].index as u32, - ), - typ: U16Bytes::new(LE, typ), - }; - buffer.write(&coff_relocation); + writer.write_relocation(writer::Relocation { + virtual_address: reloc.offset as u32, + symbol: symbol_offsets[reloc.symbol.0].index, + typ, + }); } } } // Write symbols. - debug_assert_eq!(symtab_offset, buffer.len()); for (index, symbol) in self.symbols.iter().enumerate() { - let mut name = &symbol.name[..]; let section_number = match symbol.section { SymbolSection::None => { debug_assert_eq!(symbol.kind, SymbolKind::File); @@ -600,11 +478,7 @@ impl<'a> Object<'a> { coff::IMAGE_SYM_TYPE_NULL }; let storage_class = match symbol.kind { - SymbolKind::File => { - // Name goes in auxiliary symbol records. - name = b".file"; - coff::IMAGE_SYM_CLASS_FILE - } + SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE, SymbolKind::Section => { if symbol.section.id().is_some() { coff::IMAGE_SYM_CLASS_STATIC @@ -657,54 +531,32 @@ impl<'a> Object<'a> { } else { symbol.value as u32 }; - let mut coff_symbol = coff::ImageSymbol { - name: [0; 8], - value: U32Bytes::new(LE, value), - section_number: U16Bytes::new(LE, section_number), - typ: U16Bytes::new(LE, typ), + writer.write_symbol(writer::Symbol { + name: symbol_offsets[index].name, + value, + section_number, + typ, storage_class, number_of_aux_symbols, - }; - if name.len() <= 8 { - coff_symbol.name[..name.len()].copy_from_slice(name); - } else { - let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); - coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32)); - } - buffer.write(&coff_symbol); + }); // Write auxiliary symbols. match symbol.kind { SymbolKind::File => { - let aux_len = number_of_aux_symbols as usize * coff::IMAGE_SIZEOF_SYMBOL; - debug_assert!(aux_len >= symbol.name.len()); - let old_len = buffer.len(); - buffer.write_bytes(&symbol.name); - buffer.resize(old_len + aux_len); + 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]; - let aux = coff::ImageAuxSymbolSection { - length: U32Bytes::new(LE, section.size as u32), - number_of_relocations: if section.relocations.len() > 0xffff { - U16Bytes::new(LE, 0xffff) - } else { - U16Bytes::new(LE, section.relocations.len() as u16) - }, - number_of_linenumbers: U16Bytes::default(), - check_sum: U32Bytes::new(LE, checksum(section.data())), - number: U16Bytes::new( - LE, - section_offsets[section_index].associative_section, - ), + 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, - reserved: 0, - // TODO: bigobj - high_number: U16Bytes::default(), - }; - buffer.write(&aux); + }); } _ => { debug_assert_eq!(number_of_aux_symbols, 0); @@ -712,12 +564,9 @@ impl<'a> Object<'a> { } } - // Write strtab section. - debug_assert_eq!(strtab_offset, buffer.len()); - buffer.write_bytes(&u32::to_le_bytes(strtab_len as u32)); - buffer.write_bytes(&strtab_data); + writer.write_strtab(); - debug_assert_eq!(offset, buffer.len()); + debug_assert_eq!(writer.reserved_len(), writer.len()); Ok(()) } diff --git a/src/write/coff/writer.rs b/src/write/coff/writer.rs new file mode 100644 index 00000000..80a33928 --- /dev/null +++ b/src/write/coff/writer.rs @@ -0,0 +1,516 @@ +//! 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, +} + +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::(), 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::(), + 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::(), 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`]. + 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`]. + 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`]. + 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]) + } +} + +impl<'a> Into 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, +} diff --git a/src/write/mod.rs b/src/write/mod.rs index 15ca29d7..8d3ef17e 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -16,7 +16,7 @@ use crate::{ }; #[cfg(feature = "coff")] -mod coff; +pub mod coff; #[cfg(feature = "coff")] pub use coff::CoffExportStyle; diff --git a/tests/round_trip/macho.rs b/tests/round_trip/macho.rs index 787f03ad..1aa81565 100644 --- a/tests/round_trip/macho.rs +++ b/tests/round_trip/macho.rs @@ -45,7 +45,7 @@ fn issue_552_section_file_alignment() { object.append_section_data(section, &vec![0u8; 1], 32); let bytes = &*object.write().unwrap(); - std::fs::write(&"align.o", &bytes).unwrap(); + //std::fs::write(&"align.o", &bytes).unwrap(); let object = read::File::parse(bytes).unwrap(); let mut sections = object.sections(); diff --git a/tests/round_trip/mod.rs b/tests/round_trip/mod.rs index cd696f60..17b90608 100644 --- a/tests/round_trip/mod.rs +++ b/tests/round_trip/mod.rs @@ -38,6 +38,18 @@ fn coff_x86_64() { section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); + let func2_offset = object.append_section_data(text, &[1; 30], 4); + assert_eq!(func2_offset, 64); + object.add_symbol(write::Symbol { + name: b"func2_long".to_vec(), + value: func2_offset, + size: 32, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: write::SymbolSection::Section(text), + flags: SymbolFlags::None, + }); object .add_relocation( text, @@ -66,7 +78,7 @@ fn coff_x86_64() { assert_eq!(text.name(), Ok(".text")); assert_eq!(text.kind(), SectionKind::Text); assert_eq!(text.address(), 0); - assert_eq!(text.size(), 62); + assert_eq!(text.size(), 94); assert_eq!(&text.data().unwrap()[..30], &[1; 30]); assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); @@ -92,6 +104,16 @@ fn coff_x86_64() { assert_eq!(symbol.is_weak(), false); assert_eq!(symbol.is_undefined(), false); + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("func2_long")); + assert_eq!(symbol.address(), func2_offset); + assert_eq!(symbol.kind(), SymbolKind::Text); + assert_eq!(symbol.section_index(), Some(text_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + let mut relocations = text.relocations(); let (offset, relocation) = relocations.next().unwrap();