summaryrefslogtreecommitdiffstats
path: root/third_party/rust/embed-manifest/src/embed/coff.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/embed-manifest/src/embed/coff.rs')
-rw-r--r--third_party/rust/embed-manifest/src/embed/coff.rs192
1 files changed, 192 insertions, 0 deletions
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<W> {
+ /// 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<W: Write + Seek> CoffWriter<W> {
+ /// Create a new instance wrapping a writer.
+ pub fn new(mut writer: W, machine_type: MachineType) -> io::Result<Self> {
+ // 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<u32> {
+ 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<W> {
+ // 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(&timestamp.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
+}