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 { 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() }