diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/object/src/write/coff.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/object/src/write/coff.rs')
-rw-r--r-- | third_party/rust/object/src/write/coff.rs | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/third_party/rust/object/src/write/coff.rs b/third_party/rust/object/src/write/coff.rs new file mode 100644 index 0000000000..5077bc7f0a --- /dev/null +++ b/third_party/rust/object/src/write/coff.rs @@ -0,0 +1,486 @@ +use crc32fast; +use scroll::ctx::SizeWith; +use scroll::IOwrite; +use std::iter; +use std::string::String; + +use crate::alloc::vec::Vec; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +mod coff { + pub use goblin::pe::characteristic::*; + pub use goblin::pe::header::*; + pub use goblin::pe::relocation::*; + pub use goblin::pe::section_table::*; + pub use goblin::pe::symbol::*; +} + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + offset: usize, + str_id: Option<StringId>, + reloc_offset: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, + aux_count: u8, +} + +impl Object { + pub(crate) fn coff_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => (&[], &b".rdata"[..], SectionKind::ReadOnlyData), + StandardSection::UninitializedData => { + (&[], &b".bss"[..], SectionKind::UninitializedData) + } + StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Tls), + StandardSection::UninitializedTls => { + // Unsupported section. + (&[], &[], SectionKind::UninitializedTls) + } + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables) + } + } + } + + pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + name.push(b'$'); + name.extend(value); + name + } + + pub(crate) fn coff_fixup_relocation(&mut self, mut 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 => match relocation.kind { + RelocationKind::Relative => { + // IMAGE_REL_I386_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.pointer_width().unwrap().bytes(); + + let mut name = b".rdata$.refptr.".to_vec(); + name.extend(&self.symbols[symbol_id.0].name); + 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(&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: Some(section_id), + }); + self.stub_symbols.insert(symbol_id, stub_id); + + stub_id + } + + pub(crate) fn coff_write(&self) -> Result<Vec<u8>, String> { + // Calculate offsets of everything, and build strtab. + let mut offset = 0; + let mut strtab = StringTable::default(); + + // COFF header. + let ctx = scroll::LE; + offset += coff::CoffHeader::size_with(&ctx); + + // Section headers. + offset += self.sections.len() * coff::SectionTable::size_with(&ctx); + + // Calculate size of section data and 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 = offset; + } + + // Calculate size of relocations. + let count = section.relocations.len(); + if count != 0 { + section_offsets[index].reloc_offset = offset; + offset += count * coff::Relocation::size_with(&ctx); + } + } + + // Calculate size of symbols 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; + match symbol.kind { + SymbolKind::File => { + // Name goes in auxilary symbol records. + let aux_count = + (symbol.name.len() + coff::COFF_SYMBOL_SIZE - 1) / coff::COFF_SYMBOL_SIZE; + symbol_offsets[index].aux_count = aux_count as u8; + symtab_count += aux_count; + // Don't add name to strtab. + continue; + } + SymbolKind::Section => { + symbol_offsets[index].aux_count = 1; + symtab_count += 1; + } + _ => {} + } + if symbol.name.len() > 8 { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + } + + // Calculate size of symtab. + let symtab_offset = offset; + let symtab_len = symtab_count * coff::COFF_SYMBOL_SIZE; + 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; + + // Start writing. + let mut buffer = Vec::with_capacity(offset); + + // Write file header. + let header = coff::CoffHeader { + machine: match self.architecture { + Architecture::I386 => coff::COFF_MACHINE_X86, + Architecture::X86_64 => coff::COFF_MACHINE_X86_64, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }, + number_of_sections: self.sections.len() as u16, + time_date_stamp: 0, + pointer_to_symbol_table: symtab_offset as u32, + number_of_symbol_table: symtab_count as u32, + size_of_optional_header: 0, + characteristics: 0, + }; + buffer.iowrite_with(header, ctx).unwrap(); + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + // TODO: IMAGE_SCN_LNK_COMDAT + let characteristics = 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::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::Tls + | SectionKind::UninitializedTls + | SectionKind::TlsVariables + | SectionKind::Unknown + | SectionKind::Metadata => { + return Err(format!("unimplemented section {:?}", section.kind)) + } + }; + let align = 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(format!("unimplemented section align {}", section.align)), + }; + let mut coff_section = coff::SectionTable { + name: [0; 8], + real_name: None, + virtual_size: if section.data.is_empty() { + section.size as u32 + } else { + 0 + }, + virtual_address: 0, + size_of_raw_data: section.data.len() as u32, + pointer_to_raw_data: if section.data.is_empty() { + 0 + } else { + section_offsets[index].offset as u32 + }, + pointer_to_relocations: section_offsets[index].reloc_offset as u32, + pointer_to_linenumbers: 0, + number_of_relocations: section.relocations.len() as u16, + number_of_linenumbers: 0, + characteristics: characteristics | align, + }; + if section.name.len() <= 8 { + coff_section.name[..section.name.len()].copy_from_slice(§ion.name); + } else { + let str_offset = strtab.get_offset(section_offsets[index].str_id.unwrap()); + coff_section.set_name_offset(str_offset).unwrap(); + } + buffer.iowrite_with(coff_section, ctx).unwrap(); + } + + // Write section data and relocations. + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + if len != 0 { + write_align(&mut buffer, 4); + debug_assert_eq!(section_offsets[index].offset, buffer.len()); + buffer.extend(§ion.data); + } + + if !section.relocations.is_empty() { + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.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(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::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7, + (RelocationKind::Coff(x), _, _) => x, + _ => return Err(format!("unimplemented relocation {:?}", reloc)), + }, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + buffer + .iowrite_with( + coff::Relocation { + virtual_address: reloc.offset as u32, + symbol_table_index: symbol_offsets[reloc.symbol.0].index as u32, + typ, + }, + ctx, + ) + .unwrap(); + } + } + } + + // Write symbols. + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + let mut name = &symbol.name[..]; + let mut section_number = symbol.section.map(|x| x.0 + 1).unwrap_or(0) as i16; + 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 => { + // Name goes in auxilary symbol records. + name = b".file"; + section_number = coff::IMAGE_SYM_DEBUG; + coff::IMAGE_SYM_CLASS_FILE + } + SymbolKind::Section => coff::IMAGE_SYM_CLASS_STATIC, + SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL, + SymbolKind::Text | SymbolKind::Data => { + match symbol.scope { + _ if symbol.is_undefined() => coff::IMAGE_SYM_CLASS_EXTERNAL, + // TODO: does this need aux symbol records too? + _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL, + SymbolScope::Unknown => { + return Err(format!("unimplemented symbol scope {:?}", symbol)) + } + SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC, + SymbolScope::Linkage | SymbolScope::Dynamic => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + } + } + _ => return Err(format!("unimplemented symbol {:?}", symbol.kind)), + }; + let number_of_aux_symbols = symbol_offsets[index].aux_count; + let mut coff_symbol = coff::Symbol { + name: [0; 8], + value: symbol.value as u32, + 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.set_name_offset(str_offset as u32); + } + buffer.iowrite_with(coff_symbol, ctx).unwrap(); + + match symbol.kind { + SymbolKind::File => { + let aux_len = number_of_aux_symbols as usize * coff::COFF_SYMBOL_SIZE; + debug_assert!(aux_len >= symbol.name.len()); + buffer.extend(&symbol.name); + buffer.extend(iter::repeat(0).take(aux_len - symbol.name.len())); + } + SymbolKind::Section => { + debug_assert_eq!(number_of_aux_symbols, 1); + let section = &self.sections[symbol.section.unwrap().0]; + buffer + .iowrite_with( + coff::AuxSectionDefinition { + length: section.data.len() as u32, + number_of_relocations: section.relocations.len() as u16, + number_of_line_numbers: 0, + checksum: checksum(§ion.data), + number: section_number as u16, + // TODO: COMDAT + selection: 0, + unused: [0; 3], + }, + ctx, + ) + .unwrap(); + } + _ => { + debug_assert_eq!(number_of_aux_symbols, 0); + } + } + } + + // Write strtab section. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.iowrite_with(strtab_len as u32, ctx).unwrap(); + buffer.extend(&strtab_data); + + Ok(buffer) + } +} + +// JamCRC +fn checksum(data: &[u8]) -> u32 { + let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff); + hasher.update(data); + !hasher.finalize() +} |