summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object/src/read/pe
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/object/src/read/pe
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/object/src/read/pe')
-rw-r--r--third_party/rust/object/src/read/pe/data_directory.rs211
-rw-r--r--third_party/rust/object/src/read/pe/export.rs331
-rw-r--r--third_party/rust/object/src/read/pe/file.rs1029
-rw-r--r--third_party/rust/object/src/read/pe/import.rs332
-rw-r--r--third_party/rust/object/src/read/pe/mod.rs34
-rw-r--r--third_party/rust/object/src/read/pe/relocation.rs90
-rw-r--r--third_party/rust/object/src/read/pe/resource.rs207
-rw-r--r--third_party/rust/object/src/read/pe/rich.rs91
-rw-r--r--third_party/rust/object/src/read/pe/section.rs436
9 files changed, 2761 insertions, 0 deletions
diff --git a/third_party/rust/object/src/read/pe/data_directory.rs b/third_party/rust/object/src/read/pe/data_directory.rs
new file mode 100644
index 0000000000..f5d98774e3
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/data_directory.rs
@@ -0,0 +1,211 @@
+use core::slice;
+
+use crate::read::{Error, ReadError, ReadRef, Result};
+use crate::{pe, LittleEndian as LE};
+
+use super::{
+ DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory,
+ SectionTable,
+};
+
+/// The table of data directories in a PE file.
+#[derive(Debug, Clone, Copy)]
+pub struct DataDirectories<'data> {
+ entries: &'data [pe::ImageDataDirectory],
+}
+
+impl<'data> DataDirectories<'data> {
+ /// Parse the data directory table.
+ ///
+ /// `data` must be the remaining optional data following the
+ /// [optional header](pe::ImageOptionalHeader64). `number` must be from the
+ /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes)
+ /// field of the optional header.
+ pub fn parse(data: &'data [u8], number: u32) -> Result<Self> {
+ let entries = data
+ .read_slice_at(0, number as usize)
+ .read_error("Invalid PE number of RVA and sizes")?;
+ Ok(DataDirectories { entries })
+ }
+
+ /// The number of data directories.
+ #[allow(clippy::len_without_is_empty)]
+ pub fn len(&self) -> usize {
+ self.entries.len()
+ }
+
+ /// Iterator over the data directories.
+ pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> {
+ self.entries.iter()
+ }
+
+ /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants).
+ pub fn enumerate(&self) -> core::iter::Enumerate<slice::Iter<'data, pe::ImageDataDirectory>> {
+ self.entries.iter().enumerate()
+ }
+
+ /// Returns the data directory at the given index.
+ ///
+ /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants.
+ ///
+ /// Returns `None` if the index is larger than the table size,
+ /// or if the entry at the index has a zero virtual address.
+ pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> {
+ self.entries
+ .get(index)
+ .filter(|d| d.virtual_address.get(LE) != 0)
+ }
+
+ /// Returns the unparsed export directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn export_directory<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<&'data pe::ImageExportDirectory>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let export_data = data_dir.data(data, sections)?;
+ ExportTable::parse_directory(export_data).map(Some)
+ }
+
+ /// Returns the partially parsed export directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn export_table<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<ExportTable<'data>>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let export_va = data_dir.virtual_address.get(LE);
+ let export_data = data_dir.data(data, sections)?;
+ ExportTable::parse(export_data, export_va).map(Some)
+ }
+
+ /// Returns the partially parsed import directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn import_table<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<ImportTable<'data>>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let import_va = data_dir.virtual_address.get(LE);
+ let (section_data, section_va) = sections
+ .pe_data_containing(data, import_va)
+ .read_error("Invalid import data dir virtual address")?;
+ Ok(Some(ImportTable::new(section_data, section_va, import_va)))
+ }
+
+ /// Returns the partially parsed delay-load import directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn delay_load_import_table<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<DelayLoadImportTable<'data>>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let import_va = data_dir.virtual_address.get(LE);
+ let (section_data, section_va) = sections
+ .pe_data_containing(data, import_va)
+ .read_error("Invalid import data dir virtual address")?;
+ Ok(Some(DelayLoadImportTable::new(
+ section_data,
+ section_va,
+ import_va,
+ )))
+ }
+
+ /// Returns the blocks in the base relocation directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn relocation_blocks<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<RelocationBlockIterator<'data>>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let reloc_data = data_dir.data(data, sections)?;
+ Ok(Some(RelocationBlockIterator::new(reloc_data)))
+ }
+
+ /// Returns the resource directory.
+ ///
+ /// `data` must be the entire file data.
+ pub fn resource_directory<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<Option<ResourceDirectory<'data>>> {
+ let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let rsrc_data = data_dir.data(data, sections)?;
+ Ok(Some(ResourceDirectory::new(rsrc_data)))
+ }
+}
+
+impl pe::ImageDataDirectory {
+ /// Return the virtual address range of this directory entry.
+ pub fn address_range(&self) -> (u32, u32) {
+ (self.virtual_address.get(LE), self.size.get(LE))
+ }
+
+ /// Return the file offset and size of this directory entry.
+ ///
+ /// This function has some limitations:
+ /// - It requires that the data is contained in a single section.
+ /// - It uses the size field of the directory entry, which is
+ /// not desirable for all data directories.
+ /// - It uses the `virtual_address` of the directory entry as an address,
+ /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`.
+ pub fn file_range<'data>(&self, sections: &SectionTable<'data>) -> Result<(u32, u32)> {
+ let (offset, section_size) = sections
+ .pe_file_range_at(self.virtual_address.get(LE))
+ .read_error("Invalid data dir virtual address")?;
+ let size = self.size.get(LE);
+ if size > section_size {
+ return Err(Error("Invalid data dir size"));
+ }
+ Ok((offset, size))
+ }
+
+ /// Get the data referenced by this directory entry.
+ ///
+ /// This function has some limitations:
+ /// - It requires that the data is contained in a single section.
+ /// - It uses the size field of the directory entry, which is
+ /// not desirable for all data directories.
+ /// - It uses the `virtual_address` of the directory entry as an address,
+ /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`.
+ pub fn data<'data, R: ReadRef<'data>>(
+ &self,
+ data: R,
+ sections: &SectionTable<'data>,
+ ) -> Result<&'data [u8]> {
+ sections
+ .pe_data_at(data, self.virtual_address.get(LE))
+ .read_error("Invalid data dir virtual address")?
+ .get(..self.size.get(LE) as usize)
+ .read_error("Invalid data dir size")
+ }
+}
diff --git a/third_party/rust/object/src/read/pe/export.rs b/third_party/rust/object/src/read/pe/export.rs
new file mode 100644
index 0000000000..88dc78d50b
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/export.rs
@@ -0,0 +1,331 @@
+use alloc::vec::Vec;
+use core::fmt::Debug;
+
+use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result};
+use crate::{pe, LittleEndian as LE, U16Bytes, U32Bytes};
+
+/// Where an export is pointing to.
+#[derive(Clone, Copy)]
+pub enum ExportTarget<'data> {
+ /// The address of the export, relative to the image base.
+ Address(u32),
+ /// Forwarded to an export ordinal in another DLL.
+ ///
+ /// This gives the name of the DLL, and the ordinal.
+ ForwardByOrdinal(&'data [u8], u32),
+ /// Forwarded to an export name in another DLL.
+ ///
+ /// This gives the name of the DLL, and the export name.
+ ForwardByName(&'data [u8], &'data [u8]),
+}
+
+impl<'data> ExportTarget<'data> {
+ /// Returns true if the target is an address.
+ pub fn is_address(&self) -> bool {
+ match self {
+ ExportTarget::Address(_) => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if the export is forwarded to another DLL.
+ pub fn is_forward(&self) -> bool {
+ !self.is_address()
+ }
+}
+
+/// An export from a PE file.
+///
+/// There are multiple kinds of PE exports (with or without a name, and local or forwarded).
+#[derive(Clone, Copy)]
+pub struct Export<'data> {
+ /// The ordinal of the export.
+ ///
+ /// These are sequential, starting at a base specified in the DLL.
+ pub ordinal: u32,
+ /// The name of the export, if known.
+ pub name: Option<&'data [u8]>,
+ /// The target of this export.
+ pub target: ExportTarget<'data>,
+}
+
+impl<'a> Debug for Export<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
+ f.debug_struct("Export")
+ .field("ordinal", &self.ordinal)
+ .field("name", &self.name.map(ByteString))
+ .field("target", &self.target)
+ .finish()
+ }
+}
+
+impl<'a> Debug for ExportTarget<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
+ match self {
+ ExportTarget::Address(address) => write!(f, "Address({:#x})", address),
+ ExportTarget::ForwardByOrdinal(library, ordinal) => write!(
+ f,
+ "ForwardByOrdinal({:?}.#{})",
+ ByteString(library),
+ ordinal
+ ),
+ ExportTarget::ForwardByName(library, name) => write!(
+ f,
+ "ForwardByName({:?}.{:?})",
+ ByteString(library),
+ ByteString(name)
+ ),
+ }
+ }
+}
+
+/// A partially parsed PE export table.
+#[derive(Debug, Clone)]
+pub struct ExportTable<'data> {
+ data: Bytes<'data>,
+ virtual_address: u32,
+ directory: &'data pe::ImageExportDirectory,
+ addresses: &'data [U32Bytes<LE>],
+ names: &'data [U32Bytes<LE>],
+ name_ordinals: &'data [U16Bytes<LE>],
+}
+
+impl<'data> ExportTable<'data> {
+ /// Parse the export table given its section data and address.
+ pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> {
+ let directory = Self::parse_directory(data)?;
+ let data = Bytes(data);
+
+ let mut addresses = &[][..];
+ let address_of_functions = directory.address_of_functions.get(LE);
+ if address_of_functions != 0 {
+ addresses = data
+ .read_slice_at::<U32Bytes<_>>(
+ address_of_functions.wrapping_sub(virtual_address) as usize,
+ directory.number_of_functions.get(LE) as usize,
+ )
+ .read_error("Invalid PE export address table")?;
+ }
+
+ let mut names = &[][..];
+ let mut name_ordinals = &[][..];
+ let address_of_names = directory.address_of_names.get(LE);
+ let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE);
+ if address_of_names != 0 {
+ if address_of_name_ordinals == 0 {
+ return Err(Error("Missing PE export ordinal table"));
+ }
+
+ let number = directory.number_of_names.get(LE) as usize;
+ names = data
+ .read_slice_at::<U32Bytes<_>>(
+ address_of_names.wrapping_sub(virtual_address) as usize,
+ number,
+ )
+ .read_error("Invalid PE export name pointer table")?;
+ name_ordinals = data
+ .read_slice_at::<U16Bytes<_>>(
+ address_of_name_ordinals.wrapping_sub(virtual_address) as usize,
+ number,
+ )
+ .read_error("Invalid PE export ordinal table")?;
+ }
+
+ Ok(ExportTable {
+ data,
+ virtual_address,
+ directory,
+ addresses,
+ names,
+ name_ordinals,
+ })
+ }
+
+ /// Parse the export directory given its section data.
+ pub fn parse_directory(data: &'data [u8]) -> Result<&'data pe::ImageExportDirectory> {
+ data.read_at::<pe::ImageExportDirectory>(0)
+ .read_error("Invalid PE export dir size")
+ }
+
+ /// Returns the header of the export table.
+ pub fn directory(&self) -> &'data pe::ImageExportDirectory {
+ self.directory
+ }
+
+ /// Returns the base value of ordinals.
+ ///
+ /// Adding this to an address index will give an ordinal.
+ pub fn ordinal_base(&self) -> u32 {
+ self.directory.base.get(LE)
+ }
+
+ /// Returns the unparsed address table.
+ ///
+ /// An address table entry may be a local address, or the address of a forwarded export entry.
+ /// See [`Self::is_forward`] and [`Self::target_from_address`].
+ pub fn addresses(&self) -> &'data [U32Bytes<LE>] {
+ self.addresses
+ }
+
+ /// Returns the unparsed name pointer table.
+ ///
+ /// A name pointer table entry can be used with [`Self::name_from_pointer`].
+ pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] {
+ self.names
+ }
+
+ /// Returns the unparsed ordinal table.
+ ///
+ /// An ordinal table entry is a 0-based index into the address table.
+ /// See [`Self::address_by_index`] and [`Self::target_by_index`].
+ pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] {
+ self.name_ordinals
+ }
+
+ /// Returns an iterator for the entries in the name pointer table and ordinal table.
+ ///
+ /// A name pointer table entry can be used with [`Self::name_from_pointer`].
+ ///
+ /// An ordinal table entry is a 0-based index into the address table.
+ /// See [`Self::address_by_index`] and [`Self::target_by_index`].
+ pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data {
+ self.names
+ .iter()
+ .map(|x| x.get(LE))
+ .zip(self.name_ordinals.iter().map(|x| x.get(LE)))
+ }
+
+ /// Returns the export address table entry at the given address index.
+ ///
+ /// This may be a local address, or the address of a forwarded export entry.
+ /// See [`Self::is_forward`] and [`Self::target_from_address`].
+ ///
+ /// `index` is a 0-based index into the export address table.
+ pub fn address_by_index(&self, index: u32) -> Result<u32> {
+ Ok(self
+ .addresses
+ .get(index as usize)
+ .read_error("Invalid PE export address index")?
+ .get(LE))
+ }
+
+ /// Returns the export address table entry at the given ordinal.
+ ///
+ /// This may be a local address, or the address of a forwarded export entry.
+ /// See [`Self::is_forward`] and [`Self::target_from_address`].
+ pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> {
+ self.address_by_index(ordinal.wrapping_sub(self.ordinal_base()))
+ }
+
+ /// Returns the target of the export at the given address index.
+ ///
+ /// `index` is a 0-based index into the export address table.
+ pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> {
+ self.target_from_address(self.address_by_index(index)?)
+ }
+
+ /// Returns the target of the export at the given ordinal.
+ pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> {
+ self.target_from_address(self.address_by_ordinal(ordinal)?)
+ }
+
+ /// Convert an export address table entry into a target.
+ pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> {
+ Ok(if let Some(forward) = self.forward_string(address)? {
+ let i = forward
+ .iter()
+ .position(|x| *x == b'.')
+ .read_error("Missing PE forwarded export separator")?;
+ let library = &forward[..i];
+ match &forward[i + 1..] {
+ [b'#', digits @ ..] => {
+ let ordinal =
+ parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?;
+ ExportTarget::ForwardByOrdinal(library, ordinal)
+ }
+ [] => {
+ return Err(Error("Missing PE forwarded export name"));
+ }
+ name => ExportTarget::ForwardByName(library, name),
+ }
+ } else {
+ ExportTarget::Address(address)
+ })
+ }
+
+ fn forward_offset(&self, address: u32) -> Option<usize> {
+ let offset = address.wrapping_sub(self.virtual_address) as usize;
+ if offset < self.data.len() {
+ Some(offset)
+ } else {
+ None
+ }
+ }
+
+ /// Return true if the export address table entry is a forward.
+ pub fn is_forward(&self, address: u32) -> bool {
+ self.forward_offset(address).is_some()
+ }
+
+ /// Return the forward string if the export address table entry is a forward.
+ pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> {
+ if let Some(offset) = self.forward_offset(address) {
+ self.data
+ .read_string_at(offset)
+ .read_error("Invalid PE forwarded export address")
+ .map(Some)
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Convert an export name pointer table entry into a name.
+ pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> {
+ let offset = name_pointer.wrapping_sub(self.virtual_address);
+ self.data
+ .read_string_at(offset as usize)
+ .read_error("Invalid PE export name pointer")
+ }
+
+ /// Returns the parsed exports in this table.
+ pub fn exports(&self) -> Result<Vec<Export<'data>>> {
+ // First, let's list all exports.
+ let mut exports = Vec::new();
+ let ordinal_base = self.ordinal_base();
+ for (i, address) in self.addresses.iter().enumerate() {
+ // Convert from an array index to an ordinal.
+ let ordinal = ordinal_base.wrapping_add(i as u32);
+ let target = self.target_from_address(address.get(LE))?;
+ exports.push(Export {
+ ordinal,
+ target,
+ // Might be populated later.
+ name: None,
+ });
+ }
+
+ // Now, check whether some (or all) of them have an associated name.
+ // `ordinal_index` is a 0-based index into `addresses`.
+ for (name_pointer, ordinal_index) in self.name_iter() {
+ let name = self.name_from_pointer(name_pointer)?;
+ exports
+ .get_mut(ordinal_index as usize)
+ .read_error("Invalid PE export ordinal")?
+ .name = Some(name);
+ }
+
+ Ok(exports)
+ }
+}
+
+fn parse_ordinal(digits: &[u8]) -> Option<u32> {
+ if digits.is_empty() {
+ return None;
+ }
+ let mut result: u32 = 0;
+ for &c in digits {
+ let x = (c as char).to_digit(10)?;
+ result = result.checked_mul(10)?.checked_add(x)?;
+ }
+ Some(result)
+}
diff --git a/third_party/rust/object/src/read/pe/file.rs b/third_party/rust/object/src/read/pe/file.rs
new file mode 100644
index 0000000000..8dd85131a4
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/file.rs
@@ -0,0 +1,1029 @@
+use alloc::vec::Vec;
+use core::fmt::Debug;
+use core::{mem, str};
+
+use core::convert::TryInto;
+
+use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable};
+use crate::read::{
+ self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator,
+ Object, ObjectComdat, ObjectKind, ReadError, ReadRef, Result, SectionIndex, SymbolIndex,
+};
+use crate::{pe, ByteString, Bytes, CodeView, LittleEndian as LE, Pod, U32};
+
+use super::{
+ DataDirectories, ExportTable, ImageThunkData, ImportTable, PeSection, PeSectionIterator,
+ PeSegment, PeSegmentIterator, RichHeaderInfo, SectionTable,
+};
+
+/// A PE32 (32-bit) image file.
+pub type PeFile32<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders32, R>;
+/// A PE32+ (64-bit) image file.
+pub type PeFile64<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders64, R>;
+
+/// A PE object file.
+#[derive(Debug)]
+pub struct PeFile<'data, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ pub(super) dos_header: &'data pe::ImageDosHeader,
+ pub(super) nt_headers: &'data Pe,
+ pub(super) data_directories: DataDirectories<'data>,
+ pub(super) common: CoffCommon<'data, R>,
+ pub(super) data: R,
+}
+
+impl<'data, Pe, R> PeFile<'data, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ /// Parse the raw PE file data.
+ pub fn parse(data: R) -> Result<Self> {
+ let dos_header = pe::ImageDosHeader::parse(data)?;
+ let mut offset = dos_header.nt_headers_offset().into();
+ let (nt_headers, data_directories) = Pe::parse(data, &mut offset)?;
+ let sections = nt_headers.sections(data, offset)?;
+ let coff_symbols = nt_headers.symbols(data);
+ let image_base = nt_headers.optional_header().image_base();
+
+ Ok(PeFile {
+ dos_header,
+ nt_headers,
+ data_directories,
+ common: CoffCommon {
+ sections,
+ // The PE file format deprecates the COFF symbol table (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image)
+ // We do not want to prevent parsing the rest of the PE file for a corrupt COFF header, but rather return an empty symbol table
+ symbols: coff_symbols.unwrap_or_default(),
+ image_base,
+ },
+ data,
+ })
+ }
+
+ /// Returns this binary data.
+ pub fn data(&self) -> R {
+ self.data
+ }
+
+ /// Return the DOS header of this file.
+ pub fn dos_header(&self) -> &'data pe::ImageDosHeader {
+ self.dos_header
+ }
+
+ /// Return the NT Headers of this file.
+ pub fn nt_headers(&self) -> &'data Pe {
+ self.nt_headers
+ }
+
+ /// Returns information about the rich header of this file (if any).
+ pub fn rich_header_info(&self) -> Option<RichHeaderInfo> {
+ RichHeaderInfo::parse(self.data, self.dos_header.nt_headers_offset().into())
+ }
+
+ /// Returns the section table of this binary.
+ pub fn section_table(&self) -> SectionTable<'data> {
+ self.common.sections
+ }
+
+ /// Returns the data directories of this file.
+ pub fn data_directories(&self) -> DataDirectories<'data> {
+ self.data_directories
+ }
+
+ /// Returns the data directory at the given index.
+ pub fn data_directory(&self, id: usize) -> Option<&'data pe::ImageDataDirectory> {
+ self.data_directories.get(id)
+ }
+
+ /// Returns the export table of this file.
+ ///
+ /// The export table is located using the data directory.
+ pub fn export_table(&self) -> Result<Option<ExportTable<'data>>> {
+ self.data_directories
+ .export_table(self.data, &self.common.sections)
+ }
+
+ /// Returns the import table of this file.
+ ///
+ /// The import table is located using the data directory.
+ pub fn import_table(&self) -> Result<Option<ImportTable<'data>>> {
+ self.data_directories
+ .import_table(self.data, &self.common.sections)
+ }
+
+ pub(super) fn section_alignment(&self) -> u64 {
+ u64::from(self.nt_headers.optional_header().section_alignment())
+ }
+}
+
+impl<'data, Pe, R> read::private::Sealed for PeFile<'data, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Pe, R> Object<'data, 'file> for PeFile<'data, Pe, R>
+where
+ 'data: 'file,
+ Pe: ImageNtHeaders,
+ R: 'file + ReadRef<'data>,
+{
+ type Segment = PeSegment<'data, 'file, Pe, R>;
+ type SegmentIterator = PeSegmentIterator<'data, 'file, Pe, R>;
+ type Section = PeSection<'data, 'file, Pe, R>;
+ type SectionIterator = PeSectionIterator<'data, 'file, Pe, R>;
+ type Comdat = PeComdat<'data, 'file, Pe, R>;
+ type ComdatIterator = PeComdatIterator<'data, 'file, Pe, R>;
+ type Symbol = CoffSymbol<'data, 'file, R>;
+ type SymbolIterator = CoffSymbolIterator<'data, 'file, R>;
+ type SymbolTable = CoffSymbolTable<'data, 'file, R>;
+ type DynamicRelocationIterator = NoDynamicRelocationIterator;
+
+ fn architecture(&self) -> Architecture {
+ match self.nt_headers.file_header().machine.get(LE) {
+ pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
+ pe::IMAGE_FILE_MACHINE_ARM64 => Architecture::Aarch64,
+ pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
+ pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
+ _ => Architecture::Unknown,
+ }
+ }
+
+ #[inline]
+ fn is_little_endian(&self) -> bool {
+ // Only little endian is supported.
+ true
+ }
+
+ #[inline]
+ fn is_64(&self) -> bool {
+ self.nt_headers.is_type_64()
+ }
+
+ fn kind(&self) -> ObjectKind {
+ let characteristics = self.nt_headers.file_header().characteristics.get(LE);
+ if characteristics & pe::IMAGE_FILE_DLL != 0 {
+ ObjectKind::Dynamic
+ } else if characteristics & pe::IMAGE_FILE_SYSTEM != 0 {
+ ObjectKind::Unknown
+ } else {
+ ObjectKind::Executable
+ }
+ }
+
+ fn segments(&'file self) -> PeSegmentIterator<'data, 'file, Pe, R> {
+ PeSegmentIterator {
+ file: self,
+ iter: self.common.sections.iter(),
+ }
+ }
+
+ fn section_by_name_bytes(
+ &'file self,
+ section_name: &[u8],
+ ) -> Option<PeSection<'data, 'file, Pe, R>> {
+ self.common
+ .sections
+ .section_by_name(self.common.symbols.strings(), section_name)
+ .map(|(index, section)| PeSection {
+ file: self,
+ index: SectionIndex(index),
+ section,
+ })
+ }
+
+ fn section_by_index(
+ &'file self,
+ index: SectionIndex,
+ ) -> Result<PeSection<'data, 'file, Pe, R>> {
+ let section = self.common.sections.section(index.0)?;
+ Ok(PeSection {
+ file: self,
+ index,
+ section,
+ })
+ }
+
+ fn sections(&'file self) -> PeSectionIterator<'data, 'file, Pe, R> {
+ PeSectionIterator {
+ file: self,
+ iter: self.common.sections.iter().enumerate(),
+ }
+ }
+
+ fn comdats(&'file self) -> PeComdatIterator<'data, 'file, Pe, R> {
+ PeComdatIterator { file: self }
+ }
+
+ fn symbol_by_index(&'file self, index: SymbolIndex) -> Result<CoffSymbol<'data, 'file, R>> {
+ let symbol = self.common.symbols.symbol(index.0)?;
+ Ok(CoffSymbol {
+ file: &self.common,
+ index,
+ symbol,
+ })
+ }
+
+ fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> {
+ CoffSymbolIterator {
+ file: &self.common,
+ index: 0,
+ }
+ }
+
+ fn symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> {
+ Some(CoffSymbolTable { file: &self.common })
+ }
+
+ fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> {
+ CoffSymbolIterator {
+ file: &self.common,
+ // Hack: don't return any.
+ index: self.common.symbols.len(),
+ }
+ }
+
+ fn dynamic_symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> {
+ None
+ }
+
+ fn dynamic_relocations(&'file self) -> Option<NoDynamicRelocationIterator> {
+ None
+ }
+
+ fn imports(&self) -> Result<Vec<Import<'data>>> {
+ let mut imports = Vec::new();
+ if let Some(import_table) = self.import_table()? {
+ let mut import_descs = import_table.descriptors()?;
+ while let Some(import_desc) = import_descs.next()? {
+ let library = import_table.name(import_desc.name.get(LE))?;
+ let mut first_thunk = import_desc.original_first_thunk.get(LE);
+ if first_thunk == 0 {
+ first_thunk = import_desc.first_thunk.get(LE);
+ }
+ let mut thunks = import_table.thunks(first_thunk)?;
+ while let Some(thunk) = thunks.next::<Pe>()? {
+ if !thunk.is_ordinal() {
+ let (_hint, name) = import_table.hint_name(thunk.address())?;
+ imports.push(Import {
+ library: ByteString(library),
+ name: ByteString(name),
+ });
+ }
+ }
+ }
+ }
+ Ok(imports)
+ }
+
+ fn exports(&self) -> Result<Vec<Export<'data>>> {
+ let mut exports = Vec::new();
+ if let Some(export_table) = self.export_table()? {
+ for (name_pointer, address_index) in export_table.name_iter() {
+ let name = export_table.name_from_pointer(name_pointer)?;
+ let address = export_table.address_by_index(address_index.into())?;
+ if !export_table.is_forward(address) {
+ exports.push(Export {
+ name: ByteString(name),
+ address: self.common.image_base.wrapping_add(address.into()),
+ })
+ }
+ }
+ }
+ Ok(exports)
+ }
+
+ fn pdb_info(&self) -> Result<Option<CodeView>> {
+ let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) {
+ Some(data_dir) => data_dir,
+ None => return Ok(None),
+ };
+ let debug_data = data_dir.data(self.data, &self.common.sections).map(Bytes)?;
+ let debug_data_size = data_dir.size.get(LE) as usize;
+
+ let count = debug_data_size / mem::size_of::<pe::ImageDebugDirectory>();
+ let rem = debug_data_size % mem::size_of::<pe::ImageDebugDirectory>();
+ if rem != 0 || count < 1 {
+ return Err(Error("Invalid PE debug dir size"));
+ }
+
+ let debug_dirs = debug_data
+ .read_slice_at::<pe::ImageDebugDirectory>(0, count)
+ .read_error("Invalid PE debug dir size")?;
+
+ for debug_dir in debug_dirs {
+ if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW {
+ continue;
+ }
+
+ let info = self
+ .data
+ .read_slice_at::<u8>(
+ debug_dir.pointer_to_raw_data.get(LE) as u64,
+ debug_dir.size_of_data.get(LE) as usize,
+ )
+ .read_error("Invalid CodeView Info address")?;
+
+ let mut info = Bytes(info);
+
+ let sig = info
+ .read_bytes(4)
+ .read_error("Invalid CodeView signature")?;
+ if sig.0 != b"RSDS" {
+ continue;
+ }
+
+ let guid: [u8; 16] = info
+ .read_bytes(16)
+ .read_error("Invalid CodeView GUID")?
+ .0
+ .try_into()
+ .unwrap();
+
+ let age = info.read::<U32<LE>>().read_error("Invalid CodeView Age")?;
+
+ let path = info
+ .read_string()
+ .read_error("Invalid CodeView file path")?;
+
+ return Ok(Some(CodeView {
+ path: ByteString(path),
+ guid,
+ age: age.get(LE),
+ }));
+ }
+ Ok(None)
+ }
+
+ fn has_debug_symbols(&self) -> bool {
+ self.section_by_name(".debug_info").is_some()
+ }
+
+ fn relative_address_base(&self) -> u64 {
+ self.common.image_base
+ }
+
+ fn entry(&self) -> u64 {
+ u64::from(self.nt_headers.optional_header().address_of_entry_point())
+ .wrapping_add(self.common.image_base)
+ }
+
+ fn flags(&self) -> FileFlags {
+ FileFlags::Coff {
+ characteristics: self.nt_headers.file_header().characteristics.get(LE),
+ }
+ }
+}
+
+/// An iterator over the COMDAT section groups of a `PeFile32`.
+pub type PeComdatIterator32<'data, 'file, R = &'data [u8]> =
+ PeComdatIterator<'data, 'file, pe::ImageNtHeaders32, R>;
+/// An iterator over the COMDAT section groups of a `PeFile64`.
+pub type PeComdatIterator64<'data, 'file, R = &'data [u8]> =
+ PeComdatIterator<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// An iterator over the COMDAT section groups of a `PeFile`.
+#[derive(Debug)]
+pub struct PeComdatIterator<'data, 'file, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ #[allow(unused)]
+ file: &'file PeFile<'data, Pe, R>,
+}
+
+impl<'data, 'file, Pe, R> Iterator for PeComdatIterator<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type Item = PeComdat<'data, 'file, Pe, R>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+}
+
+/// A COMDAT section group of a `PeFile32`.
+pub type PeComdat32<'data, 'file, R = &'data [u8]> =
+ PeComdat<'data, 'file, pe::ImageNtHeaders32, R>;
+/// A COMDAT section group of a `PeFile64`.
+pub type PeComdat64<'data, 'file, R = &'data [u8]> =
+ PeComdat<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// A COMDAT section group of a `PeFile`.
+#[derive(Debug)]
+pub struct PeComdat<'data, 'file, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ #[allow(unused)]
+ file: &'file PeFile<'data, Pe, R>,
+}
+
+impl<'data, 'file, Pe, R> read::private::Sealed for PeComdat<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Pe, R> ObjectComdat<'data> for PeComdat<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type SectionIterator = PeComdatSectionIterator<'data, 'file, Pe, R>;
+
+ #[inline]
+ fn kind(&self) -> ComdatKind {
+ unreachable!();
+ }
+
+ #[inline]
+ fn symbol(&self) -> SymbolIndex {
+ unreachable!();
+ }
+
+ #[inline]
+ fn name_bytes(&self) -> Result<&[u8]> {
+ unreachable!();
+ }
+
+ #[inline]
+ fn name(&self) -> Result<&str> {
+ unreachable!();
+ }
+
+ #[inline]
+ fn sections(&self) -> Self::SectionIterator {
+ unreachable!();
+ }
+}
+
+/// An iterator over the sections in a COMDAT section group of a `PeFile32`.
+pub type PeComdatSectionIterator32<'data, 'file, R = &'data [u8]> =
+ PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>;
+/// An iterator over the sections in a COMDAT section group of a `PeFile64`.
+pub type PeComdatSectionIterator64<'data, 'file, R = &'data [u8]> =
+ PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// An iterator over the sections in a COMDAT section group of a `PeFile`.
+#[derive(Debug)]
+pub struct PeComdatSectionIterator<'data, 'file, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ #[allow(unused)]
+ file: &'file PeFile<'data, Pe, R>,
+}
+
+impl<'data, 'file, Pe, R> Iterator for PeComdatSectionIterator<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type Item = SectionIndex;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+}
+
+impl pe::ImageDosHeader {
+ /// Read the DOS header.
+ ///
+ /// Also checks that the `e_magic` field in the header is valid.
+ pub fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> {
+ // DOS header comes first.
+ let dos_header = data
+ .read_at::<pe::ImageDosHeader>(0)
+ .read_error("Invalid DOS header size or alignment")?;
+ if dos_header.e_magic.get(LE) != pe::IMAGE_DOS_SIGNATURE {
+ return Err(Error("Invalid DOS magic"));
+ }
+ Ok(dos_header)
+ }
+
+ /// Return the file offset of the nt_headers.
+ #[inline]
+ pub fn nt_headers_offset(&self) -> u32 {
+ self.e_lfanew.get(LE)
+ }
+}
+
+/// Find the optional header and read the `optional_header.magic`.
+///
+/// It can be useful to know this magic value before trying to
+/// fully parse the NT headers.
+pub fn optional_header_magic<'data, R: ReadRef<'data>>(data: R) -> Result<u16> {
+ let dos_header = pe::ImageDosHeader::parse(data)?;
+ // NT headers are at an offset specified in the DOS header.
+ let offset = dos_header.nt_headers_offset().into();
+ // It doesn't matter which NT header type is used for the purpose
+ // of reading the optional header magic.
+ let nt_headers = data
+ .read_at::<pe::ImageNtHeaders32>(offset)
+ .read_error("Invalid NT headers offset, size, or alignment")?;
+ if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE {
+ return Err(Error("Invalid PE magic"));
+ }
+ Ok(nt_headers.optional_header().magic())
+}
+
+/// A trait for generic access to `ImageNtHeaders32` and `ImageNtHeaders64`.
+#[allow(missing_docs)]
+pub trait ImageNtHeaders: Debug + Pod {
+ type ImageOptionalHeader: ImageOptionalHeader;
+ type ImageThunkData: ImageThunkData;
+
+ /// Return true if this type is a 64-bit header.
+ ///
+ /// This is a property of the type, not a value in the header data.
+ fn is_type_64(&self) -> bool;
+
+ /// Return true if the magic field in the optional header is valid.
+ fn is_valid_optional_magic(&self) -> bool;
+
+ /// Return the signature
+ fn signature(&self) -> u32;
+
+ /// Return the file header.
+ fn file_header(&self) -> &pe::ImageFileHeader;
+
+ /// Return the optional header.
+ fn optional_header(&self) -> &Self::ImageOptionalHeader;
+
+ // Provided methods.
+
+ /// Read the NT headers, including the data directories.
+ ///
+ /// `data` must be for the entire file.
+ ///
+ /// `offset` must be headers offset, which can be obtained from `ImageDosHeader::nt_headers_offset`.
+ /// It is updated to point after the optional header, which is where the section headers are located.
+ ///
+ /// Also checks that the `signature` and `magic` fields in the headers are valid.
+ fn parse<'data, R: ReadRef<'data>>(
+ data: R,
+ offset: &mut u64,
+ ) -> read::Result<(&'data Self, DataDirectories<'data>)> {
+ // Note that this does not include the data directories in the optional header.
+ let nt_headers = data
+ .read::<Self>(offset)
+ .read_error("Invalid PE headers offset or size")?;
+ if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE {
+ return Err(Error("Invalid PE magic"));
+ }
+ if !nt_headers.is_valid_optional_magic() {
+ return Err(Error("Invalid PE optional header magic"));
+ }
+
+ // Read the rest of the optional header, and then read the data directories from that.
+ let optional_data_size =
+ u64::from(nt_headers.file_header().size_of_optional_header.get(LE))
+ .checked_sub(mem::size_of::<Self::ImageOptionalHeader>() as u64)
+ .read_error("PE optional header size is too small")?;
+ let optional_data = data
+ .read_bytes(offset, optional_data_size)
+ .read_error("Invalid PE optional header size")?;
+ let data_directories = DataDirectories::parse(
+ optional_data,
+ nt_headers.optional_header().number_of_rva_and_sizes(),
+ )?;
+
+ Ok((nt_headers, data_directories))
+ }
+
+ /// Read the section table.
+ ///
+ /// `data` must be for the entire file.
+ /// `offset` must be after the optional file header.
+ #[inline]
+ fn sections<'data, R: ReadRef<'data>>(
+ &self,
+ data: R,
+ offset: u64,
+ ) -> read::Result<SectionTable<'data>> {
+ SectionTable::parse(self.file_header(), data, offset)
+ }
+
+ /// Read the COFF symbol table and string table.
+ ///
+ /// `data` must be the entire file data.
+ #[inline]
+ fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<SymbolTable<'data, R>> {
+ SymbolTable::parse(self.file_header(), data)
+ }
+}
+
+/// A trait for generic access to `ImageOptionalHeader32` and `ImageOptionalHeader64`.
+#[allow(missing_docs)]
+pub trait ImageOptionalHeader: Debug + Pod {
+ // Standard fields.
+ fn magic(&self) -> u16;
+ fn major_linker_version(&self) -> u8;
+ fn minor_linker_version(&self) -> u8;
+ fn size_of_code(&self) -> u32;
+ fn size_of_initialized_data(&self) -> u32;
+ fn size_of_uninitialized_data(&self) -> u32;
+ fn address_of_entry_point(&self) -> u32;
+ fn base_of_code(&self) -> u32;
+ fn base_of_data(&self) -> Option<u32>;
+
+ // NT additional fields.
+ fn image_base(&self) -> u64;
+ fn section_alignment(&self) -> u32;
+ fn file_alignment(&self) -> u32;
+ fn major_operating_system_version(&self) -> u16;
+ fn minor_operating_system_version(&self) -> u16;
+ fn major_image_version(&self) -> u16;
+ fn minor_image_version(&self) -> u16;
+ fn major_subsystem_version(&self) -> u16;
+ fn minor_subsystem_version(&self) -> u16;
+ fn win32_version_value(&self) -> u32;
+ fn size_of_image(&self) -> u32;
+ fn size_of_headers(&self) -> u32;
+ fn check_sum(&self) -> u32;
+ fn subsystem(&self) -> u16;
+ fn dll_characteristics(&self) -> u16;
+ fn size_of_stack_reserve(&self) -> u64;
+ fn size_of_stack_commit(&self) -> u64;
+ fn size_of_heap_reserve(&self) -> u64;
+ fn size_of_heap_commit(&self) -> u64;
+ fn loader_flags(&self) -> u32;
+ fn number_of_rva_and_sizes(&self) -> u32;
+}
+
+impl ImageNtHeaders for pe::ImageNtHeaders32 {
+ type ImageOptionalHeader = pe::ImageOptionalHeader32;
+ type ImageThunkData = pe::ImageThunkData32;
+
+ #[inline]
+ fn is_type_64(&self) -> bool {
+ false
+ }
+
+ #[inline]
+ fn is_valid_optional_magic(&self) -> bool {
+ self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC
+ }
+
+ #[inline]
+ fn signature(&self) -> u32 {
+ self.signature.get(LE)
+ }
+
+ #[inline]
+ fn file_header(&self) -> &pe::ImageFileHeader {
+ &self.file_header
+ }
+
+ #[inline]
+ fn optional_header(&self) -> &Self::ImageOptionalHeader {
+ &self.optional_header
+ }
+}
+
+impl ImageOptionalHeader for pe::ImageOptionalHeader32 {
+ #[inline]
+ fn magic(&self) -> u16 {
+ self.magic.get(LE)
+ }
+
+ #[inline]
+ fn major_linker_version(&self) -> u8 {
+ self.major_linker_version
+ }
+
+ #[inline]
+ fn minor_linker_version(&self) -> u8 {
+ self.minor_linker_version
+ }
+
+ #[inline]
+ fn size_of_code(&self) -> u32 {
+ self.size_of_code.get(LE)
+ }
+
+ #[inline]
+ fn size_of_initialized_data(&self) -> u32 {
+ self.size_of_initialized_data.get(LE)
+ }
+
+ #[inline]
+ fn size_of_uninitialized_data(&self) -> u32 {
+ self.size_of_uninitialized_data.get(LE)
+ }
+
+ #[inline]
+ fn address_of_entry_point(&self) -> u32 {
+ self.address_of_entry_point.get(LE)
+ }
+
+ #[inline]
+ fn base_of_code(&self) -> u32 {
+ self.base_of_code.get(LE)
+ }
+
+ #[inline]
+ fn base_of_data(&self) -> Option<u32> {
+ Some(self.base_of_data.get(LE))
+ }
+
+ #[inline]
+ fn image_base(&self) -> u64 {
+ self.image_base.get(LE).into()
+ }
+
+ #[inline]
+ fn section_alignment(&self) -> u32 {
+ self.section_alignment.get(LE)
+ }
+
+ #[inline]
+ fn file_alignment(&self) -> u32 {
+ self.file_alignment.get(LE)
+ }
+
+ #[inline]
+ fn major_operating_system_version(&self) -> u16 {
+ self.major_operating_system_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_operating_system_version(&self) -> u16 {
+ self.minor_operating_system_version.get(LE)
+ }
+
+ #[inline]
+ fn major_image_version(&self) -> u16 {
+ self.major_image_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_image_version(&self) -> u16 {
+ self.minor_image_version.get(LE)
+ }
+
+ #[inline]
+ fn major_subsystem_version(&self) -> u16 {
+ self.major_subsystem_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_subsystem_version(&self) -> u16 {
+ self.minor_subsystem_version.get(LE)
+ }
+
+ #[inline]
+ fn win32_version_value(&self) -> u32 {
+ self.win32_version_value.get(LE)
+ }
+
+ #[inline]
+ fn size_of_image(&self) -> u32 {
+ self.size_of_image.get(LE)
+ }
+
+ #[inline]
+ fn size_of_headers(&self) -> u32 {
+ self.size_of_headers.get(LE)
+ }
+
+ #[inline]
+ fn check_sum(&self) -> u32 {
+ self.check_sum.get(LE)
+ }
+
+ #[inline]
+ fn subsystem(&self) -> u16 {
+ self.subsystem.get(LE)
+ }
+
+ #[inline]
+ fn dll_characteristics(&self) -> u16 {
+ self.dll_characteristics.get(LE)
+ }
+
+ #[inline]
+ fn size_of_stack_reserve(&self) -> u64 {
+ self.size_of_stack_reserve.get(LE).into()
+ }
+
+ #[inline]
+ fn size_of_stack_commit(&self) -> u64 {
+ self.size_of_stack_commit.get(LE).into()
+ }
+
+ #[inline]
+ fn size_of_heap_reserve(&self) -> u64 {
+ self.size_of_heap_reserve.get(LE).into()
+ }
+
+ #[inline]
+ fn size_of_heap_commit(&self) -> u64 {
+ self.size_of_heap_commit.get(LE).into()
+ }
+
+ #[inline]
+ fn loader_flags(&self) -> u32 {
+ self.loader_flags.get(LE)
+ }
+
+ #[inline]
+ fn number_of_rva_and_sizes(&self) -> u32 {
+ self.number_of_rva_and_sizes.get(LE)
+ }
+}
+
+impl ImageNtHeaders for pe::ImageNtHeaders64 {
+ type ImageOptionalHeader = pe::ImageOptionalHeader64;
+ type ImageThunkData = pe::ImageThunkData64;
+
+ #[inline]
+ fn is_type_64(&self) -> bool {
+ true
+ }
+
+ #[inline]
+ fn is_valid_optional_magic(&self) -> bool {
+ self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC
+ }
+
+ #[inline]
+ fn signature(&self) -> u32 {
+ self.signature.get(LE)
+ }
+
+ #[inline]
+ fn file_header(&self) -> &pe::ImageFileHeader {
+ &self.file_header
+ }
+
+ #[inline]
+ fn optional_header(&self) -> &Self::ImageOptionalHeader {
+ &self.optional_header
+ }
+}
+
+impl ImageOptionalHeader for pe::ImageOptionalHeader64 {
+ #[inline]
+ fn magic(&self) -> u16 {
+ self.magic.get(LE)
+ }
+
+ #[inline]
+ fn major_linker_version(&self) -> u8 {
+ self.major_linker_version
+ }
+
+ #[inline]
+ fn minor_linker_version(&self) -> u8 {
+ self.minor_linker_version
+ }
+
+ #[inline]
+ fn size_of_code(&self) -> u32 {
+ self.size_of_code.get(LE)
+ }
+
+ #[inline]
+ fn size_of_initialized_data(&self) -> u32 {
+ self.size_of_initialized_data.get(LE)
+ }
+
+ #[inline]
+ fn size_of_uninitialized_data(&self) -> u32 {
+ self.size_of_uninitialized_data.get(LE)
+ }
+
+ #[inline]
+ fn address_of_entry_point(&self) -> u32 {
+ self.address_of_entry_point.get(LE)
+ }
+
+ #[inline]
+ fn base_of_code(&self) -> u32 {
+ self.base_of_code.get(LE)
+ }
+
+ #[inline]
+ fn base_of_data(&self) -> Option<u32> {
+ None
+ }
+
+ #[inline]
+ fn image_base(&self) -> u64 {
+ self.image_base.get(LE)
+ }
+
+ #[inline]
+ fn section_alignment(&self) -> u32 {
+ self.section_alignment.get(LE)
+ }
+
+ #[inline]
+ fn file_alignment(&self) -> u32 {
+ self.file_alignment.get(LE)
+ }
+
+ #[inline]
+ fn major_operating_system_version(&self) -> u16 {
+ self.major_operating_system_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_operating_system_version(&self) -> u16 {
+ self.minor_operating_system_version.get(LE)
+ }
+
+ #[inline]
+ fn major_image_version(&self) -> u16 {
+ self.major_image_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_image_version(&self) -> u16 {
+ self.minor_image_version.get(LE)
+ }
+
+ #[inline]
+ fn major_subsystem_version(&self) -> u16 {
+ self.major_subsystem_version.get(LE)
+ }
+
+ #[inline]
+ fn minor_subsystem_version(&self) -> u16 {
+ self.minor_subsystem_version.get(LE)
+ }
+
+ #[inline]
+ fn win32_version_value(&self) -> u32 {
+ self.win32_version_value.get(LE)
+ }
+
+ #[inline]
+ fn size_of_image(&self) -> u32 {
+ self.size_of_image.get(LE)
+ }
+
+ #[inline]
+ fn size_of_headers(&self) -> u32 {
+ self.size_of_headers.get(LE)
+ }
+
+ #[inline]
+ fn check_sum(&self) -> u32 {
+ self.check_sum.get(LE)
+ }
+
+ #[inline]
+ fn subsystem(&self) -> u16 {
+ self.subsystem.get(LE)
+ }
+
+ #[inline]
+ fn dll_characteristics(&self) -> u16 {
+ self.dll_characteristics.get(LE)
+ }
+
+ #[inline]
+ fn size_of_stack_reserve(&self) -> u64 {
+ self.size_of_stack_reserve.get(LE)
+ }
+
+ #[inline]
+ fn size_of_stack_commit(&self) -> u64 {
+ self.size_of_stack_commit.get(LE)
+ }
+
+ #[inline]
+ fn size_of_heap_reserve(&self) -> u64 {
+ self.size_of_heap_reserve.get(LE)
+ }
+
+ #[inline]
+ fn size_of_heap_commit(&self) -> u64 {
+ self.size_of_heap_commit.get(LE)
+ }
+
+ #[inline]
+ fn loader_flags(&self) -> u32 {
+ self.loader_flags.get(LE)
+ }
+
+ #[inline]
+ fn number_of_rva_and_sizes(&self) -> u32 {
+ self.number_of_rva_and_sizes.get(LE)
+ }
+}
diff --git a/third_party/rust/object/src/read/pe/import.rs b/third_party/rust/object/src/read/pe/import.rs
new file mode 100644
index 0000000000..a5535dc367
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/import.rs
@@ -0,0 +1,332 @@
+use core::fmt::Debug;
+use core::mem;
+
+use crate::read::{Bytes, ReadError, Result};
+use crate::{pe, LittleEndian as LE, Pod, U16Bytes};
+
+use super::ImageNtHeaders;
+
+/// Information for parsing a PE import table.
+#[derive(Debug, Clone)]
+pub struct ImportTable<'data> {
+ section_data: Bytes<'data>,
+ section_address: u32,
+ import_address: u32,
+}
+
+impl<'data> ImportTable<'data> {
+ /// Create a new import table parser.
+ ///
+ /// The import descriptors start at `import_address`.
+ /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is
+ /// ignored by the Windows loader, and so descriptors will be parsed until a null entry.
+ ///
+ /// `section_data` should be from the section containing `import_address`, and
+ /// `section_address` should be the address of that section. Pointers within the
+ /// descriptors and thunks may point to anywhere within the section data.
+ pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
+ ImportTable {
+ section_data: Bytes(section_data),
+ section_address,
+ import_address,
+ }
+ }
+
+ /// Return an iterator for the import descriptors.
+ pub fn descriptors(&self) -> Result<ImportDescriptorIterator<'data>> {
+ let offset = self.import_address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE import descriptor address")?;
+ Ok(ImportDescriptorIterator { data })
+ }
+
+ /// Return a library name given its address.
+ ///
+ /// This address may be from [`pe::ImageImportDescriptor::name`].
+ pub fn name(&self, address: u32) -> Result<&'data [u8]> {
+ self.section_data
+ .read_string_at(address.wrapping_sub(self.section_address) as usize)
+ .read_error("Invalid PE import descriptor name")
+ }
+
+ /// Return a list of thunks given its address.
+ ///
+ /// This address may be from [`pe::ImageImportDescriptor::original_first_thunk`]
+ /// or [`pe::ImageImportDescriptor::first_thunk`].
+ pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
+ let offset = address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE import thunk table address")?;
+ Ok(ImportThunkList { data })
+ }
+
+ /// Parse a thunk.
+ pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> {
+ if thunk.is_ordinal() {
+ Ok(Import::Ordinal(thunk.ordinal()))
+ } else {
+ let (hint, name) = self.hint_name(thunk.address())?;
+ Ok(Import::Name(hint, name))
+ }
+ }
+
+ /// Return the hint and name at the given address.
+ ///
+ /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`].
+ ///
+ /// The hint is an index into the export name pointer table in the target library.
+ pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
+ let offset = address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE import thunk address")?;
+ let hint = data
+ .read::<U16Bytes<LE>>()
+ .read_error("Missing PE import thunk hint")?
+ .get(LE);
+ let name = data
+ .read_string()
+ .read_error("Missing PE import thunk name")?;
+ Ok((hint, name))
+ }
+}
+
+/// A fallible iterator for the descriptors in the import data directory.
+#[derive(Debug, Clone)]
+pub struct ImportDescriptorIterator<'data> {
+ data: Bytes<'data>,
+}
+
+impl<'data> ImportDescriptorIterator<'data> {
+ /// Return the next descriptor.
+ ///
+ /// Returns `Ok(None)` when a null descriptor is found.
+ pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> {
+ let import_desc = self
+ .data
+ .read::<pe::ImageImportDescriptor>()
+ .read_error("Missing PE null import descriptor")?;
+ if import_desc.is_null() {
+ Ok(None)
+ } else {
+ Ok(Some(import_desc))
+ }
+ }
+}
+
+/// A list of import thunks.
+///
+/// These may be in the import lookup table, or the import address table.
+#[derive(Debug, Clone)]
+pub struct ImportThunkList<'data> {
+ data: Bytes<'data>,
+}
+
+impl<'data> ImportThunkList<'data> {
+ /// Get the thunk at the given index.
+ pub fn get<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> {
+ let thunk = self
+ .data
+ .read_at(index * mem::size_of::<Pe::ImageThunkData>())
+ .read_error("Invalid PE import thunk index")?;
+ Ok(*thunk)
+ }
+
+ /// Return the first thunk in the list, and update `self` to point after it.
+ ///
+ /// Returns `Ok(None)` when a null thunk is found.
+ pub fn next<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> {
+ let thunk = self
+ .data
+ .read::<Pe::ImageThunkData>()
+ .read_error("Missing PE null import thunk")?;
+ if thunk.address() == 0 {
+ Ok(None)
+ } else {
+ Ok(Some(*thunk))
+ }
+ }
+}
+
+/// A parsed import thunk.
+#[derive(Debug, Clone, Copy)]
+pub enum Import<'data> {
+ /// Import by ordinal.
+ Ordinal(u16),
+ /// Import by name.
+ ///
+ /// Includes a hint for the index into the export name pointer table in the target library.
+ Name(u16, &'data [u8]),
+}
+
+/// A trait for generic access to [`pe::ImageThunkData32`] and [`pe::ImageThunkData64`].
+#[allow(missing_docs)]
+pub trait ImageThunkData: Debug + Pod {
+ /// Return the raw thunk value.
+ fn raw(self) -> u64;
+
+ /// Returns true if the ordinal flag is set.
+ fn is_ordinal(self) -> bool;
+
+ /// Return the ordinal portion of the thunk.
+ ///
+ /// Does not check the ordinal flag.
+ fn ordinal(self) -> u16;
+
+ /// Return the RVA portion of the thunk.
+ ///
+ /// Does not check the ordinal flag.
+ fn address(self) -> u32;
+}
+
+impl ImageThunkData for pe::ImageThunkData64 {
+ fn raw(self) -> u64 {
+ self.0.get(LE)
+ }
+
+ fn is_ordinal(self) -> bool {
+ self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG64 != 0
+ }
+
+ fn ordinal(self) -> u16 {
+ self.0.get(LE) as u16
+ }
+
+ fn address(self) -> u32 {
+ self.0.get(LE) as u32 & 0x7fff_ffff
+ }
+}
+
+impl ImageThunkData for pe::ImageThunkData32 {
+ fn raw(self) -> u64 {
+ self.0.get(LE).into()
+ }
+
+ fn is_ordinal(self) -> bool {
+ self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG32 != 0
+ }
+
+ fn ordinal(self) -> u16 {
+ self.0.get(LE) as u16
+ }
+
+ fn address(self) -> u32 {
+ self.0.get(LE) & 0x7fff_ffff
+ }
+}
+
+/// Information for parsing a PE delay-load import table.
+#[derive(Debug, Clone)]
+pub struct DelayLoadImportTable<'data> {
+ section_data: Bytes<'data>,
+ section_address: u32,
+ import_address: u32,
+}
+
+impl<'data> DelayLoadImportTable<'data> {
+ /// Create a new delay load import table parser.
+ ///
+ /// The import descriptors start at `import_address`.
+ /// This table works in the same way the import table does: descriptors will be
+ /// parsed until a null entry.
+ ///
+ /// `section_data` should be from the section containing `import_address`, and
+ /// `section_address` should be the address of that section. Pointers within the
+ /// descriptors and thunks may point to anywhere within the section data.
+ pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
+ DelayLoadImportTable {
+ section_data: Bytes(section_data),
+ section_address,
+ import_address,
+ }
+ }
+
+ /// Return an iterator for the import descriptors.
+ pub fn descriptors(&self) -> Result<DelayLoadDescriptorIterator<'data>> {
+ let offset = self.import_address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE delay-load import descriptor address")?;
+ Ok(DelayLoadDescriptorIterator { data })
+ }
+
+ /// Return a library name given its address.
+ ///
+ /// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`].
+ pub fn name(&self, address: u32) -> Result<&'data [u8]> {
+ self.section_data
+ .read_string_at(address.wrapping_sub(self.section_address) as usize)
+ .read_error("Invalid PE import descriptor name")
+ }
+
+ /// Return a list of thunks given its address.
+ ///
+ /// This address may be from the INT, i.e. from
+ /// [`pe::ImageDelayloadDescriptor::import_name_table_rva`].
+ ///
+ /// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used
+ /// by the delay loader at runtime to store values, and thus do not point inside the same
+ /// section as the INT. Calling this function on those addresses will fail.
+ pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
+ let offset = address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE delay load import thunk table address")?;
+ Ok(ImportThunkList { data })
+ }
+
+ /// Parse a thunk.
+ pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> {
+ if thunk.is_ordinal() {
+ Ok(Import::Ordinal(thunk.ordinal()))
+ } else {
+ let (hint, name) = self.hint_name(thunk.address())?;
+ Ok(Import::Name(hint, name))
+ }
+ }
+
+ /// Return the hint and name at the given address.
+ ///
+ /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`].
+ ///
+ /// The hint is an index into the export name pointer table in the target library.
+ pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
+ let offset = address.wrapping_sub(self.section_address);
+ let mut data = self.section_data;
+ data.skip(offset as usize)
+ .read_error("Invalid PE delay load import thunk address")?;
+ let hint = data
+ .read::<U16Bytes<LE>>()
+ .read_error("Missing PE delay load import thunk hint")?
+ .get(LE);
+ let name = data
+ .read_string()
+ .read_error("Missing PE delay load import thunk name")?;
+ Ok((hint, name))
+ }
+}
+
+/// A fallible iterator for the descriptors in the delay-load data directory.
+#[derive(Debug, Clone)]
+pub struct DelayLoadDescriptorIterator<'data> {
+ data: Bytes<'data>,
+}
+
+impl<'data> DelayLoadDescriptorIterator<'data> {
+ /// Return the next descriptor.
+ ///
+ /// Returns `Ok(None)` when a null descriptor is found.
+ pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> {
+ let import_desc = self
+ .data
+ .read::<pe::ImageDelayloadDescriptor>()
+ .read_error("Missing PE null delay-load import descriptor")?;
+ if import_desc.is_null() {
+ Ok(None)
+ } else {
+ Ok(Some(import_desc))
+ }
+ }
+}
diff --git a/third_party/rust/object/src/read/pe/mod.rs b/third_party/rust/object/src/read/pe/mod.rs
new file mode 100644
index 0000000000..2b7cc5d7a0
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/mod.rs
@@ -0,0 +1,34 @@
+//! Support for reading PE files.
+//!
+//! Defines traits to abstract over the difference between PE32/PE32+,
+//! and implements read functionality in terms of these traits.
+//!
+//! This module reuses some of the COFF functionality.
+//!
+//! Also provides `PeFile` and related types which implement the `Object` trait.
+
+mod file;
+pub use file::*;
+
+mod section;
+pub use section::*;
+
+mod data_directory;
+pub use data_directory::*;
+
+mod export;
+pub use export::*;
+
+mod import;
+pub use import::*;
+
+mod relocation;
+pub use relocation::*;
+
+mod resource;
+pub use resource::*;
+
+mod rich;
+pub use rich::*;
+
+pub use super::coff::{SectionTable, SymbolTable};
diff --git a/third_party/rust/object/src/read/pe/relocation.rs b/third_party/rust/object/src/read/pe/relocation.rs
new file mode 100644
index 0000000000..06215bd1a7
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/relocation.rs
@@ -0,0 +1,90 @@
+use core::slice;
+
+use crate::endian::{LittleEndian as LE, U16};
+use crate::pe;
+use crate::read::{Bytes, Error, ReadError, Result};
+
+/// An iterator over the relocation blocks in the `.reloc` section of a PE file.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct RelocationBlockIterator<'data> {
+ data: Bytes<'data>,
+}
+
+impl<'data> RelocationBlockIterator<'data> {
+ /// Construct a new iterator from the data of the `.reloc` section.
+ pub fn new(data: &'data [u8]) -> Self {
+ RelocationBlockIterator { data: Bytes(data) }
+ }
+
+ /// Read the next relocation page.
+ pub fn next(&mut self) -> Result<Option<RelocationIterator<'data>>> {
+ if self.data.is_empty() {
+ return Ok(None);
+ }
+ let header = self
+ .data
+ .read::<pe::ImageBaseRelocation>()
+ .read_error("Invalid PE reloc section size")?;
+ let virtual_address = header.virtual_address.get(LE);
+ let size = header.size_of_block.get(LE);
+ if size <= 8 || size & 3 != 0 {
+ return Err(Error("Invalid PE reloc block size"));
+ }
+ let count = (size - 8) / 2;
+ let relocs = self
+ .data
+ .read_slice::<U16<LE>>(count as usize)
+ .read_error("Invalid PE reloc block size")?
+ .iter();
+ Ok(Some(RelocationIterator {
+ virtual_address,
+ size,
+ relocs,
+ }))
+ }
+}
+
+/// An iterator of the relocations in a block in the `.reloc` section of a PE file.
+#[derive(Debug, Clone)]
+pub struct RelocationIterator<'data> {
+ virtual_address: u32,
+ size: u32,
+ relocs: slice::Iter<'data, U16<LE>>,
+}
+
+impl<'data> RelocationIterator<'data> {
+ /// Return the virtual address of the page that this block of relocations applies to.
+ pub fn virtual_address(&self) -> u32 {
+ self.virtual_address
+ }
+
+ /// Return the size in bytes of this block of relocations.
+ pub fn size(&self) -> u32 {
+ self.size
+ }
+}
+
+impl<'data> Iterator for RelocationIterator<'data> {
+ type Item = Relocation;
+
+ fn next(&mut self) -> Option<Relocation> {
+ loop {
+ let reloc = self.relocs.next()?.get(LE);
+ if reloc != 0 {
+ return Some(Relocation {
+ virtual_address: self.virtual_address.wrapping_add((reloc & 0xfff) as u32),
+ typ: reloc >> 12,
+ });
+ }
+ }
+ }
+}
+
+/// A relocation in the `.reloc` section of a PE file.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Relocation {
+ /// The virtual address of the relocation.
+ pub virtual_address: u32,
+ /// One of the `pe::IMAGE_REL_BASED_*` constants.
+ pub typ: u16,
+}
diff --git a/third_party/rust/object/src/read/pe/resource.rs b/third_party/rust/object/src/read/pe/resource.rs
new file mode 100644
index 0000000000..e667f0d98b
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/resource.rs
@@ -0,0 +1,207 @@
+use alloc::string::String;
+use core::char;
+
+use crate::read::{ReadError, ReadRef, Result};
+use crate::{pe, LittleEndian as LE, U16Bytes};
+
+/// The `.rsrc` section of a PE file.
+#[derive(Debug, Clone, Copy)]
+pub struct ResourceDirectory<'data> {
+ data: &'data [u8],
+}
+
+impl<'data> ResourceDirectory<'data> {
+ /// Construct from the data of the `.rsrc` section.
+ pub fn new(data: &'data [u8]) -> Self {
+ ResourceDirectory { data }
+ }
+
+ /// Parses the root resource directory.
+ pub fn root(&self) -> Result<ResourceDirectoryTable<'data>> {
+ ResourceDirectoryTable::parse(self.data, 0)
+ }
+}
+
+/// A table of resource entries.
+#[derive(Debug, Clone)]
+pub struct ResourceDirectoryTable<'data> {
+ /// The table header.
+ pub header: &'data pe::ImageResourceDirectory,
+ /// The table entries.
+ pub entries: &'data [pe::ImageResourceDirectoryEntry],
+}
+
+impl<'data> ResourceDirectoryTable<'data> {
+ fn parse(data: &'data [u8], offset: u32) -> Result<Self> {
+ let mut offset = u64::from(offset);
+ let header = data
+ .read::<pe::ImageResourceDirectory>(&mut offset)
+ .read_error("Invalid resource table header")?;
+ let entries_count = header.number_of_id_entries.get(LE) as usize
+ + header.number_of_named_entries.get(LE) as usize;
+ let entries = data
+ .read_slice::<pe::ImageResourceDirectoryEntry>(&mut offset, entries_count)
+ .read_error("Invalid resource table entries")?;
+ Ok(Self { header, entries })
+ }
+}
+
+impl pe::ImageResourceDirectoryEntry {
+ /// Returns true if the entry has a name, rather than an ID.
+ pub fn has_name(&self) -> bool {
+ self.name_or_id.get(LE) & pe::IMAGE_RESOURCE_NAME_IS_STRING != 0
+ }
+
+ /// Returns the section offset of the name.
+ ///
+ /// Valid if `has_name()` returns true.
+ fn name(&self) -> ResourceName {
+ let offset = self.name_or_id.get(LE) & !pe::IMAGE_RESOURCE_NAME_IS_STRING;
+ ResourceName { offset }
+ }
+
+ /// Returns the ID.
+ ///
+ /// Valid if `has_string_name()` returns false.
+ fn id(&self) -> u16 {
+ (self.name_or_id.get(LE) & 0x0000_FFFF) as u16
+ }
+
+ /// Returns the entry name
+ pub fn name_or_id(&self) -> ResourceNameOrId {
+ if self.has_name() {
+ ResourceNameOrId::Name(self.name())
+ } else {
+ ResourceNameOrId::Id(self.id())
+ }
+ }
+
+ /// Returns true if the entry is a subtable.
+ pub fn is_table(&self) -> bool {
+ self.offset_to_data_or_directory.get(LE) & pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0
+ }
+
+ /// Returns the section offset of the associated table or data.
+ pub fn data_offset(&self) -> u32 {
+ self.offset_to_data_or_directory.get(LE) & !pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY
+ }
+
+ /// Returns the data associated to this directory entry.
+ pub fn data<'data>(
+ &self,
+ section: ResourceDirectory<'data>,
+ ) -> Result<ResourceDirectoryEntryData<'data>> {
+ if self.is_table() {
+ ResourceDirectoryTable::parse(section.data, self.data_offset())
+ .map(ResourceDirectoryEntryData::Table)
+ } else {
+ section
+ .data
+ .read_at::<pe::ImageResourceDataEntry>(self.data_offset().into())
+ .read_error("Invalid resource entry")
+ .map(ResourceDirectoryEntryData::Data)
+ }
+ }
+}
+
+/// Data associated with a resource directory entry.
+#[derive(Debug, Clone)]
+pub enum ResourceDirectoryEntryData<'data> {
+ /// A subtable entry.
+ Table(ResourceDirectoryTable<'data>),
+ /// A resource data entry.
+ Data(&'data pe::ImageResourceDataEntry),
+}
+
+impl<'data> ResourceDirectoryEntryData<'data> {
+ /// Converts to an option of table.
+ ///
+ /// Helper for iterator filtering.
+ pub fn table(self) -> Option<ResourceDirectoryTable<'data>> {
+ match self {
+ Self::Table(dir) => Some(dir),
+ _ => None,
+ }
+ }
+
+ /// Converts to an option of data entry.
+ ///
+ /// Helper for iterator filtering.
+ pub fn data(self) -> Option<&'data pe::ImageResourceDataEntry> {
+ match self {
+ Self::Data(rsc) => Some(rsc),
+ _ => None,
+ }
+ }
+}
+
+/// A resource name.
+#[derive(Debug, Clone, Copy)]
+pub struct ResourceName {
+ offset: u32,
+}
+
+impl ResourceName {
+ /// Converts to a `String`.
+ pub fn to_string_lossy(&self, directory: ResourceDirectory) -> Result<String> {
+ let d = self.data(directory)?.iter().map(|c| c.get(LE));
+
+ Ok(char::decode_utf16(d)
+ .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
+ .collect::<String>())
+ }
+
+ /// Returns the string unicode buffer.
+ pub fn data<'data>(
+ &self,
+ directory: ResourceDirectory<'data>,
+ ) -> Result<&'data [U16Bytes<LE>]> {
+ let mut offset = u64::from(self.offset);
+ let len = directory
+ .data
+ .read::<U16Bytes<LE>>(&mut offset)
+ .read_error("Invalid resource name offset")?;
+ directory
+ .data
+ .read_slice::<U16Bytes<LE>>(&mut offset, len.get(LE).into())
+ .read_error("Invalid resource name length")
+ }
+
+ /// Returns the string buffer as raw bytes.
+ pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> {
+ self.data(directory).map(crate::pod::bytes_of_slice)
+ }
+}
+
+/// A resource name or ID.
+///
+/// Can be either a string or a numeric ID.
+#[derive(Debug)]
+pub enum ResourceNameOrId {
+ /// A resource name.
+ Name(ResourceName),
+ /// A resource ID.
+ Id(u16),
+}
+
+impl ResourceNameOrId {
+ /// Converts to an option of name.
+ ///
+ /// Helper for iterator filtering.
+ pub fn name(self) -> Option<ResourceName> {
+ match self {
+ Self::Name(name) => Some(name),
+ _ => None,
+ }
+ }
+
+ /// Converts to an option of ID.
+ ///
+ /// Helper for iterator filtering.
+ pub fn id(self) -> Option<u16> {
+ match self {
+ Self::Id(id) => Some(id),
+ _ => None,
+ }
+ }
+}
diff --git a/third_party/rust/object/src/read/pe/rich.rs b/third_party/rust/object/src/read/pe/rich.rs
new file mode 100644
index 0000000000..687dfc9950
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/rich.rs
@@ -0,0 +1,91 @@
+//! PE rich header handling
+
+use core::mem;
+
+use crate::pod::bytes_of_slice;
+use crate::read::Bytes;
+use crate::{pe, LittleEndian as LE, ReadRef, U32};
+
+/// Parsed information about a Rich Header.
+#[derive(Debug, Clone, Copy)]
+pub struct RichHeaderInfo<'data> {
+ /// The offset at which the rich header starts.
+ pub offset: usize,
+ /// The length (in bytes) of the rich header.
+ ///
+ /// This includes the payload, but also the 16-byte start sequence and the
+ /// 8-byte final "Rich" and XOR key.
+ pub length: usize,
+ /// The XOR key used to mask the rich header.
+ ///
+ /// Unless the file has been tampered with, it should be equal to a checksum
+ /// of the file header.
+ pub xor_key: u32,
+ masked_entries: &'data [pe::MaskedRichHeaderEntry],
+}
+
+/// A PE rich header entry after it has been unmasked.
+///
+/// See [`pe::MaskedRichHeaderEntry`].
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct RichHeaderEntry {
+ /// ID of the component.
+ pub comp_id: u32,
+ /// Number of times this component has been used when building this PE.
+ pub count: u32,
+}
+
+impl<'data> RichHeaderInfo<'data> {
+ /// Try to locate a rich header and its entries in the current PE file.
+ pub fn parse<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> {
+ // Locate the rich header, if any.
+ // It ends with the "Rich" string and an XOR key, before the NT header.
+ let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?;
+ let end_marker_offset = memmem(data.0, b"Rich", 4)?;
+ let xor_key = *data.read_at::<U32<LE>>(end_marker_offset + 4).ok()?;
+
+ // It starts at the masked "DanS" string and 3 masked zeroes.
+ let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE));
+ let start_header = [masked_start_marker, xor_key, xor_key, xor_key];
+ let start_sequence = bytes_of_slice(&start_header);
+ let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?;
+
+ // Extract the items between the markers.
+ let items_offset = start_marker_offset + start_sequence.len();
+ let items_len = end_marker_offset - items_offset;
+ let item_count = items_len / mem::size_of::<pe::MaskedRichHeaderEntry>();
+ let items = data.read_slice_at(items_offset, item_count).ok()?;
+ Some(RichHeaderInfo {
+ offset: start_marker_offset,
+ // Includes "Rich" marker and the XOR key.
+ length: end_marker_offset - start_marker_offset + 8,
+ xor_key: xor_key.get(LE),
+ masked_entries: items,
+ })
+ }
+
+ /// Returns an iterator over the unmasked entries.
+ pub fn unmasked_entries(&self) -> impl Iterator<Item = RichHeaderEntry> + 'data {
+ let xor_key = self.xor_key;
+ self.masked_entries
+ .iter()
+ .map(move |entry| RichHeaderEntry {
+ comp_id: entry.masked_comp_id.get(LE) ^ xor_key,
+ count: entry.masked_count.get(LE) ^ xor_key,
+ })
+ }
+}
+
+/// Find the offset of the first occurence of needle in the data.
+///
+/// The offset must have the given alignment.
+fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> {
+ let mut offset = 0;
+ loop {
+ if data.get(offset..)?.get(..needle.len())? == needle {
+ return Some(offset);
+ }
+ offset += align;
+ }
+}
diff --git a/third_party/rust/object/src/read/pe/section.rs b/third_party/rust/object/src/read/pe/section.rs
new file mode 100644
index 0000000000..439d42dac1
--- /dev/null
+++ b/third_party/rust/object/src/read/pe/section.rs
@@ -0,0 +1,436 @@
+use core::marker::PhantomData;
+use core::{cmp, iter, slice, str};
+
+use crate::endian::LittleEndian as LE;
+use crate::pe;
+use crate::pe::ImageSectionHeader;
+use crate::read::{
+ self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef,
+ Relocation, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags,
+};
+
+use super::{ImageNtHeaders, PeFile, SectionTable};
+
+/// An iterator over the loadable sections of a `PeFile32`.
+pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> =
+ PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>;
+/// An iterator over the loadable sections of a `PeFile64`.
+pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> =
+ PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// An iterator over the loadable sections of a `PeFile`.
+#[derive(Debug)]
+pub struct PeSegmentIterator<'data, 'file, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ pub(super) file: &'file PeFile<'data, Pe, R>,
+ pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>,
+}
+
+impl<'data, 'file, Pe, R> Iterator for PeSegmentIterator<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type Item = PeSegment<'data, 'file, Pe, R>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|section| PeSegment {
+ file: self.file,
+ section,
+ })
+ }
+}
+
+/// A loadable section of a `PeFile32`.
+pub type PeSegment32<'data, 'file, R = &'data [u8]> =
+ PeSegment<'data, 'file, pe::ImageNtHeaders32, R>;
+/// A loadable section of a `PeFile64`.
+pub type PeSegment64<'data, 'file, R = &'data [u8]> =
+ PeSegment<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// A loadable section of a `PeFile`.
+#[derive(Debug)]
+pub struct PeSegment<'data, 'file, Pe, R = &'data [u8]>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ file: &'file PeFile<'data, Pe, R>,
+ section: &'data pe::ImageSectionHeader,
+}
+
+impl<'data, 'file, Pe, R> read::private::Sealed for PeSegment<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Pe, R> ObjectSegment<'data> for PeSegment<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ #[inline]
+ fn address(&self) -> u64 {
+ u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base)
+ }
+
+ #[inline]
+ fn size(&self) -> u64 {
+ u64::from(self.section.virtual_size.get(LE))
+ }
+
+ #[inline]
+ fn align(&self) -> u64 {
+ self.file.section_alignment()
+ }
+
+ #[inline]
+ fn file_range(&self) -> (u64, u64) {
+ let (offset, size) = self.section.pe_file_range();
+ (u64::from(offset), u64::from(size))
+ }
+
+ fn data(&self) -> Result<&'data [u8]> {
+ self.section.pe_data(self.file.data)
+ }
+
+ fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
+ Ok(read::util::data_range(
+ self.data()?,
+ self.address(),
+ address,
+ size,
+ ))
+ }
+
+ #[inline]
+ fn name_bytes(&self) -> Result<Option<&[u8]>> {
+ self.section
+ .name(self.file.common.symbols.strings())
+ .map(Some)
+ }
+
+ #[inline]
+ fn name(&self) -> Result<Option<&str>> {
+ let name = self.section.name(self.file.common.symbols.strings())?;
+ Ok(Some(
+ str::from_utf8(name)
+ .ok()
+ .read_error("Non UTF-8 PE section name")?,
+ ))
+ }
+
+ #[inline]
+ fn flags(&self) -> SegmentFlags {
+ let characteristics = self.section.characteristics.get(LE);
+ SegmentFlags::Coff { characteristics }
+ }
+}
+
+/// An iterator over the sections of a `PeFile32`.
+pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> =
+ PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>;
+/// An iterator over the sections of a `PeFile64`.
+pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> =
+ PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// An iterator over the sections of a `PeFile`.
+#[derive(Debug)]
+pub struct PeSectionIterator<'data, 'file, Pe, R = &'data [u8]>
+where
+ 'data: 'file,
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ pub(super) file: &'file PeFile<'data, Pe, R>,
+ pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>,
+}
+
+impl<'data, 'file, Pe, R> Iterator for PeSectionIterator<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type Item = PeSection<'data, 'file, Pe, R>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|(index, section)| PeSection {
+ file: self.file,
+ index: SectionIndex(index + 1),
+ section,
+ })
+ }
+}
+
+/// A section of a `PeFile32`.
+pub type PeSection32<'data, 'file, R = &'data [u8]> =
+ PeSection<'data, 'file, pe::ImageNtHeaders32, R>;
+/// A section of a `PeFile64`.
+pub type PeSection64<'data, 'file, R = &'data [u8]> =
+ PeSection<'data, 'file, pe::ImageNtHeaders64, R>;
+
+/// A section of a `PeFile`.
+#[derive(Debug)]
+pub struct PeSection<'data, 'file, Pe, R = &'data [u8]>
+where
+ 'data: 'file,
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ pub(super) file: &'file PeFile<'data, Pe, R>,
+ pub(super) index: SectionIndex,
+ pub(super) section: &'data pe::ImageSectionHeader,
+}
+
+impl<'data, 'file, Pe, R> read::private::Sealed for PeSection<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Pe, R> ObjectSection<'data> for PeSection<'data, 'file, Pe, R>
+where
+ Pe: ImageNtHeaders,
+ R: ReadRef<'data>,
+{
+ type RelocationIterator = PeRelocationIterator<'data, 'file, R>;
+
+ #[inline]
+ fn index(&self) -> SectionIndex {
+ self.index
+ }
+
+ #[inline]
+ fn address(&self) -> u64 {
+ u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base)
+ }
+
+ #[inline]
+ fn size(&self) -> u64 {
+ u64::from(self.section.virtual_size.get(LE))
+ }
+
+ #[inline]
+ fn align(&self) -> u64 {
+ self.file.section_alignment()
+ }
+
+ #[inline]
+ fn file_range(&self) -> Option<(u64, u64)> {
+ let (offset, size) = self.section.pe_file_range();
+ if size == 0 {
+ None
+ } else {
+ Some((u64::from(offset), u64::from(size)))
+ }
+ }
+
+ fn data(&self) -> Result<&'data [u8]> {
+ self.section.pe_data(self.file.data)
+ }
+
+ fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
+ Ok(read::util::data_range(
+ self.data()?,
+ self.address(),
+ address,
+ size,
+ ))
+ }
+
+ #[inline]
+ fn compressed_file_range(&self) -> Result<CompressedFileRange> {
+ Ok(CompressedFileRange::none(self.file_range()))
+ }
+
+ #[inline]
+ fn compressed_data(&self) -> Result<CompressedData<'data>> {
+ self.data().map(CompressedData::none)
+ }
+
+ #[inline]
+ fn name_bytes(&self) -> Result<&[u8]> {
+ self.section.name(self.file.common.symbols.strings())
+ }
+
+ #[inline]
+ fn name(&self) -> Result<&str> {
+ let name = self.name_bytes()?;
+ str::from_utf8(name)
+ .ok()
+ .read_error("Non UTF-8 PE section name")
+ }
+
+ #[inline]
+ fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
+ Ok(None)
+ }
+
+ #[inline]
+ fn segment_name(&self) -> Result<Option<&str>> {
+ Ok(None)
+ }
+
+ #[inline]
+ fn kind(&self) -> SectionKind {
+ self.section.kind()
+ }
+
+ fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> {
+ PeRelocationIterator(PhantomData)
+ }
+
+ fn flags(&self) -> SectionFlags {
+ SectionFlags::Coff {
+ characteristics: self.section.characteristics.get(LE),
+ }
+ }
+}
+
+impl<'data> SectionTable<'data> {
+ /// Return the file offset of the given virtual address, and the size up
+ /// to the end of the section containing it.
+ ///
+ /// Returns `None` if no section contains the address.
+ pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
+ self.iter().find_map(|section| section.pe_file_range_at(va))
+ }
+
+ /// Return the data starting at the given virtual address, up to the end of the
+ /// section containing it.
+ ///
+ /// Ignores sections with invalid data.
+ ///
+ /// Returns `None` if no section contains the address.
+ pub fn pe_data_at<R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> {
+ self.iter().find_map(|section| section.pe_data_at(data, va))
+ }
+
+ /// Return the data of the section that contains the given virtual address in a PE file.
+ ///
+ /// Also returns the virtual address of that section.
+ ///
+ /// Ignores sections with invalid data.
+ pub fn pe_data_containing<R: ReadRef<'data>>(
+ &self,
+ data: R,
+ va: u32,
+ ) -> Option<(&'data [u8], u32)> {
+ self.iter()
+ .find_map(|section| section.pe_data_containing(data, va))
+ }
+
+ /// Return the section that contains a given virtual address.
+ pub fn section_containing(&self, va: u32) -> Option<&'data ImageSectionHeader> {
+ self.iter().find(|section| section.contains_rva(va))
+ }
+}
+
+impl pe::ImageSectionHeader {
+ /// Return the offset and size of the section in a PE file.
+ ///
+ /// The size of the range will be the minimum of the file size and virtual size.
+ pub fn pe_file_range(&self) -> (u32, u32) {
+ // Pointer and size will be zero for uninitialized data; we don't need to validate this.
+ let offset = self.pointer_to_raw_data.get(LE);
+ let size = cmp::min(self.virtual_size.get(LE), self.size_of_raw_data.get(LE));
+ (offset, size)
+ }
+
+ /// Return the file offset of the given virtual address, and the remaining size up
+ /// to the end of the section.
+ ///
+ /// Returns `None` if the section does not contain the address.
+ pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
+ let section_va = self.virtual_address.get(LE);
+ let offset = va.checked_sub(section_va)?;
+ let (section_offset, section_size) = self.pe_file_range();
+ // Address must be within section (and not at its end).
+ if offset < section_size {
+ Some((section_offset.checked_add(offset)?, section_size - offset))
+ } else {
+ None
+ }
+ }
+
+ /// Return the virtual address and size of the section.
+ pub fn pe_address_range(&self) -> (u32, u32) {
+ (self.virtual_address.get(LE), self.virtual_size.get(LE))
+ }
+
+ /// Return the section data in a PE file.
+ ///
+ /// The length of the data will be the minimum of the file size and virtual size.
+ pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R) -> Result<&'data [u8]> {
+ let (offset, size) = self.pe_file_range();
+ data.read_bytes_at(offset.into(), size.into())
+ .read_error("Invalid PE section offset or size")
+ }
+
+ /// Return the data starting at the given virtual address, up to the end of the
+ /// section.
+ ///
+ /// Ignores sections with invalid data.
+ ///
+ /// Returns `None` if the section does not contain the address.
+ pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> {
+ let (offset, size) = self.pe_file_range_at(va)?;
+ data.read_bytes_at(offset.into(), size.into()).ok()
+ }
+
+ /// Tests whether a given RVA is part of this section
+ pub fn contains_rva(&self, va: u32) -> bool {
+ let section_va = self.virtual_address.get(LE);
+ match va.checked_sub(section_va) {
+ None => false,
+ Some(offset) => {
+ // Address must be within section (and not at its end).
+ offset < self.virtual_size.get(LE)
+ }
+ }
+ }
+
+ /// Return the section data if it contains the given virtual address.
+ ///
+ /// Also returns the virtual address of that section.
+ ///
+ /// Ignores sections with invalid data.
+ pub fn pe_data_containing<'data, R: ReadRef<'data>>(
+ &self,
+ data: R,
+ va: u32,
+ ) -> Option<(&'data [u8], u32)> {
+ let section_va = self.virtual_address.get(LE);
+ let offset = va.checked_sub(section_va)?;
+ let (section_offset, section_size) = self.pe_file_range();
+ // Address must be within section (and not at its end).
+ if offset < section_size {
+ let section_data = data
+ .read_bytes_at(section_offset.into(), section_size.into())
+ .ok()?;
+ Some((section_data, section_va))
+ } else {
+ None
+ }
+ }
+}
+
+/// An iterator over the relocations in an `PeSection`.
+#[derive(Debug)]
+pub struct PeRelocationIterator<'data, 'file, R = &'data [u8]>(
+ PhantomData<(&'data (), &'file (), R)>,
+);
+
+impl<'data, 'file, R> Iterator for PeRelocationIterator<'data, 'file, R> {
+ type Item = (u64, Relocation);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+}