From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- third_party/rust/embed-manifest/src/embed/coff.rs | 192 ++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 third_party/rust/embed-manifest/src/embed/coff.rs (limited to 'third_party/rust/embed-manifest/src/embed/coff.rs') diff --git a/third_party/rust/embed-manifest/src/embed/coff.rs b/third_party/rust/embed-manifest/src/embed/coff.rs new file mode 100644 index 0000000000..bf751c3bf5 --- /dev/null +++ b/third_party/rust/embed-manifest/src/embed/coff.rs @@ -0,0 +1,192 @@ +//! Just as much COFF object file support as is needed to write a resource data segment +//! for GNU Windows targets. Inspired by the `write::Object` code from the `object` crate. +//! +//! Integers are converted from u64 to u32 and added without checking because the manifest +//! data cannot get anywhere close to overflowing unless the supplied application name or +//! number of dependencies was extremely long. If this code was used more generally or if +//! the input was less trustworthy then more checked conversions and checked arithmetic +//! would be needed. + +use std::io::{self, Seek, SeekFrom, Write}; +use std::time::SystemTime; + +#[derive(Debug, Clone, Copy)] +pub enum MachineType { + I386, + X86_64, + Aarch64, +} + +impl MachineType { + pub fn machine(&self) -> u16 { + match self { + Self::I386 => 0x014c, + Self::X86_64 => 0x8664, + Self::Aarch64 => 0xaa64, + } + } + + pub fn relocation_type(&self) -> u16 { + match self { + Self::I386 => 7, + Self::X86_64 => 3, + Self::Aarch64 => 2, + } + } +} + +pub struct CoffWriter { + /// wrapped writer or buffer + writer: W, + /// machine type for file header + machine_type: MachineType, + // size in bytes of resource section data + size_of_raw_data: u32, + // number of relocations at the end of the section + number_of_relocations: u16, +} + +impl CoffWriter { + /// Create a new instance wrapping a writer. + pub fn new(mut writer: W, machine_type: MachineType) -> io::Result { + // Add space for file header and section table. + writer.write_all(&[0; 60])?; + Ok(Self { + writer, + machine_type, + size_of_raw_data: 0, + number_of_relocations: 0, + }) + } + + /// Add data to a section and return its offset within the section. + pub fn add_data(&mut self, data: &[u8]) -> io::Result { + let start = self.size_of_raw_data; + self.writer.write_all(data)?; + self.size_of_raw_data = start + data.len() as u32; + Ok(start) + } + + // Pad the resource data to a multiple of `n` bytes. + pub fn align_to(&mut self, n: u32) -> io::Result<()> { + let offset = self.size_of_raw_data % n; + if offset != 0 { + let padding = n - offset; + for _ in 0..padding { + self.writer.write_all(&[0])?; + } + self.size_of_raw_data += padding; + } + Ok(()) + } + + /// Write a relocation for a symbol at the end of the section. + pub fn add_relocation(&mut self, address: u32) -> io::Result<()> { + self.number_of_relocations += 1; + self.writer.write_all(&address.to_le_bytes())?; + self.writer.write_all(&[0, 0, 0, 0])?; + self.writer.write_all(&self.machine_type.relocation_type().to_le_bytes()) + } + + /// Write the object and section headers and write the symbol table. + pub fn finish(mut self) -> io::Result { + // Get the timestamp for the header. `as` is correct here, as the low 32 bits + // should be used. + let timestamp = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_or(0, |d| d.as_secs() as u32); + + // Copy file location of the symbol table. + let pointer_to_symbol_table = self.writer.stream_position()? as u32; + + // Write the symbols and auxiliary data for the section. + self.writer.write_all(b".rsrc\0\0\0")?; // name + self.writer.write_all(&[0, 0, 0, 0])?; // address + self.writer.write_all(&[1, 0])?; // section number (1-based) + self.writer.write_all(&[0, 0, 3, 1])?; // type = 0, class = static, aux symbols = 1 + self.writer.write_all(&self.size_of_raw_data.to_le_bytes())?; + self.writer.write_all(&self.number_of_relocations.to_le_bytes())?; + self.writer.write_all(&[0; 12])?; + + // Write the empty string table. + self.writer.write_all(&[0; 4])?; + + // Write the object header. + let end_of_file = self.writer.seek(SeekFrom::Start(0))?; + self.writer.write_all(&self.machine_type.machine().to_le_bytes())?; + self.writer.write_all(&[1, 0])?; // number of sections + self.writer.write_all(×tamp.to_le_bytes())?; + self.writer.write_all(&pointer_to_symbol_table.to_le_bytes())?; + self.writer.write_all(&[2, 0, 0, 0])?; // number of symbol table entries + self.writer.write_all(&[0; 4])?; // optional header size = 0, characteristics = 0 + + // Write the section header. + self.writer.write_all(b".rsrc\0\0\0")?; + self.writer.write_all(&[0; 8])?; // virtual size = 0 and virtual address = 0 + self.writer.write_all(&self.size_of_raw_data.to_le_bytes())?; + self.writer.write_all(&[60, 0, 0, 0])?; // pointer to raw data + self.writer.write_all(&(self.size_of_raw_data + 60).to_le_bytes())?; // pointer to relocations + self.writer.write_all(&[0; 4])?; // pointer to line numbers + self.writer.write_all(&self.number_of_relocations.to_le_bytes())?; + self.writer.write_all(&[0; 2])?; // number of line numbers + self.writer.write_all(&[0x40, 0, 0x30, 0xc0])?; // characteristics + + // Return the inner writer and dispose of this object. + self.writer.seek(SeekFrom::Start(end_of_file))?; + Ok(self.writer) + } +} + +/// Returns the bytes for a resource directory table. +/// +/// Most of the fields are set to zero, including the timestamp, to aid +/// with making builds reproducible. +/// +/// ```c +/// typedef struct { +/// DWORD Characteristics, +/// DWORD TimeDateStamp, +/// WORD MajorVersion, +/// WORD MinorVersion, +/// WORD NumberOfNamedEntries, +/// WORD NumberOfIdEntries +/// } IMAGE_RESOURCE_DIRECTORY; +/// ``` +pub fn resource_directory_table(number_of_id_entries: u16) -> [u8; 16] { + let mut table = [0; 16]; + table[14..16].copy_from_slice(&number_of_id_entries.to_le_bytes()); + table +} + +/// Returns the bytes for a resource directory entry for an ID. +/// +/// ```c +/// typedef struct { +/// DWORD Name, +/// DWORD OffsetToData +/// } IMAGE_RESOURCE_DIRECTORY_ENTRY; +/// ``` +pub fn resource_directory_id_entry(id: u32, offset: u32, subdirectory: bool) -> [u8; 8] { + let mut entry = [0; 8]; + entry[0..4].copy_from_slice(&id.to_le_bytes()); + let flag: u32 = if subdirectory { 0x80000000 } else { 0 }; + entry[4..8].copy_from_slice(&((offset & 0x7fffffff) | flag).to_le_bytes()); + entry +} + +/// Returns the bytes for a resource data entry. +/// +/// ```c +/// typedef struct { +/// DWORD OffsetToData, +/// DWORD Size, +/// DWORD CodePage, +/// DWORD Reserved +/// } IMAGE_RESOURCE_DATA_ENTRY; +/// ``` +pub fn resource_data_entry(rva: u32, size: u32) -> [u8; 16] { + let mut entry = [0; 16]; + entry[0..4].copy_from_slice(&rva.to_le_bytes()); + entry[4..8].copy_from_slice(&size.to_le_bytes()); + entry +} -- cgit v1.2.3