summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object/src/write/coff.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/object/src/write/coff.rs
parentInitial commit. (diff)
downloadfirefox-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.rs486
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(&section.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(&section.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(&section.data);
+ }
+
+ if !section.relocations.is_empty() {
+ debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
+ for reloc in &section.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(&section.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()
+}