diff options
Diffstat (limited to 'vendor/object/src/read/mod.rs')
-rw-r--r-- | vendor/object/src/read/mod.rs | 860 |
1 files changed, 0 insertions, 860 deletions
diff --git a/vendor/object/src/read/mod.rs b/vendor/object/src/read/mod.rs deleted file mode 100644 index c13e309..0000000 --- a/vendor/object/src/read/mod.rs +++ /dev/null @@ -1,860 +0,0 @@ -//! Interface for reading object files. -//! -//! ## Unified read API -//! -//! The [`Object`] trait provides a unified read API for accessing common features of -//! object files, such as sections and symbols. There is an implementation of this -//! trait for [`File`], which allows reading any file format, as well as implementations -//! for each file format: -//! [`ElfFile`](elf::ElfFile), [`MachOFile`](macho::MachOFile), [`CoffFile`](coff::CoffFile), -//! [`PeFile`](pe::PeFile), [`WasmFile`](wasm::WasmFile), [`XcoffFile`](xcoff::XcoffFile). -//! -//! ## Low level read API -//! -//! The submodules for each file format define helpers that operate on the raw structs. -//! These can be used instead of the unified API, or in conjunction with it to access -//! details that are not available via the unified API. -//! -//! See the [submodules](#modules) for examples of the low level read API. -//! -//! ## Naming Convention -//! -//! Types that form part of the unified API for a file format are prefixed with the -//! name of the file format. -//! -//! ## Example for unified read API -//! ```no_run -//! use object::{Object, ObjectSection}; -//! use std::error::Error; -//! use std::fs; -//! -//! /// Reads a file and displays the name of each section. -//! fn main() -> Result<(), Box<dyn Error>> { -//! # #[cfg(feature = "std")] { -//! let data = fs::read("path/to/binary")?; -//! let file = object::File::parse(&*data)?; -//! for section in file.sections() { -//! println!("{}", section.name()?); -//! } -//! # } -//! Ok(()) -//! } -//! ``` - -use alloc::borrow::Cow; -use alloc::vec::Vec; -use core::{fmt, result}; - -use crate::common::*; - -mod read_ref; -pub use read_ref::*; - -#[cfg(feature = "std")] -mod read_cache; -#[cfg(feature = "std")] -pub use read_cache::*; - -mod util; -pub use util::*; - -#[cfg(any( - feature = "coff", - feature = "elf", - feature = "macho", - feature = "pe", - feature = "wasm", - feature = "xcoff" -))] -mod any; -#[cfg(any( - feature = "coff", - feature = "elf", - feature = "macho", - feature = "pe", - feature = "wasm", - feature = "xcoff" -))] -pub use any::*; - -#[cfg(feature = "archive")] -pub mod archive; - -#[cfg(feature = "coff")] -pub mod coff; - -#[cfg(feature = "elf")] -pub mod elf; - -#[cfg(feature = "macho")] -pub mod macho; - -#[cfg(feature = "pe")] -pub mod pe; - -#[cfg(feature = "wasm")] -pub mod wasm; - -#[cfg(feature = "xcoff")] -pub mod xcoff; - -mod traits; -pub use traits::*; - -mod private { - pub trait Sealed {} -} - -/// The error type used within the read module. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Error(&'static str); - -impl fmt::Display for Error { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.0) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -/// The result type used within the read module. -pub type Result<T> = result::Result<T, Error>; - -trait ReadError<T> { - fn read_error(self, error: &'static str) -> Result<T>; -} - -impl<T> ReadError<T> for result::Result<T, ()> { - fn read_error(self, error: &'static str) -> Result<T> { - self.map_err(|()| Error(error)) - } -} - -impl<T> ReadError<T> for result::Result<T, Error> { - fn read_error(self, error: &'static str) -> Result<T> { - self.map_err(|_| Error(error)) - } -} - -impl<T> ReadError<T> for Option<T> { - fn read_error(self, error: &'static str) -> Result<T> { - self.ok_or(Error(error)) - } -} - -/// The native executable file for the target platform. -#[cfg(all( - unix, - not(target_os = "macos"), - target_pointer_width = "32", - feature = "elf" -))] -pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::Endianness, R>; - -/// The native executable file for the target platform. -#[cfg(all( - unix, - not(target_os = "macos"), - target_pointer_width = "64", - feature = "elf" -))] -pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::Endianness, R>; - -/// The native executable file for the target platform. -#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))] -pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile32<'data, crate::Endianness, R>; - -/// The native executable file for the target platform. -#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))] -pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile64<'data, crate::Endianness, R>; - -/// The native executable file for the target platform. -#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))] -pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>; - -/// The native executable file for the target platform. -#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))] -pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>; - -/// The native executable file for the target platform. -#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))] -pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>; - -/// A file format kind. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum FileKind { - /// A Unix archive. - /// - /// See [`archive::ArchiveFile`]. - #[cfg(feature = "archive")] - Archive, - /// A COFF object file. - /// - /// See [`coff::CoffFile`]. - #[cfg(feature = "coff")] - Coff, - /// A COFF bigobj object file. - /// - /// This supports a larger number of sections. - /// - /// See [`coff::CoffBigFile`]. - #[cfg(feature = "coff")] - CoffBig, - /// A Windows short import file. - /// - /// See [`coff::ImportFile`]. - #[cfg(feature = "coff")] - CoffImport, - /// A dyld cache file containing Mach-O images. - /// - /// See [`macho::DyldCache`] - #[cfg(feature = "macho")] - DyldCache, - /// A 32-bit ELF file. - /// - /// See [`elf::ElfFile32`]. - #[cfg(feature = "elf")] - Elf32, - /// A 64-bit ELF file. - /// - /// See [`elf::ElfFile64`]. - #[cfg(feature = "elf")] - Elf64, - /// A 32-bit Mach-O file. - /// - /// See [`macho::MachOFile32`]. - #[cfg(feature = "macho")] - MachO32, - /// A 64-bit Mach-O file. - /// - /// See [`macho::MachOFile64`]. - #[cfg(feature = "macho")] - MachO64, - /// A 32-bit Mach-O fat binary. - /// - /// See [`macho::FatHeader::parse_arch32`]. - #[cfg(feature = "macho")] - MachOFat32, - /// A 64-bit Mach-O fat binary. - /// - /// See [`macho::FatHeader::parse_arch64`]. - #[cfg(feature = "macho")] - MachOFat64, - /// A 32-bit PE file. - /// - /// See [`pe::PeFile32`]. - #[cfg(feature = "pe")] - Pe32, - /// A 64-bit PE file. - /// - /// See [`pe::PeFile64`]. - #[cfg(feature = "pe")] - Pe64, - /// A Wasm file. - /// - /// See [`wasm::WasmFile`]. - #[cfg(feature = "wasm")] - Wasm, - /// A 32-bit XCOFF file. - /// - /// See [`xcoff::XcoffFile32`]. - #[cfg(feature = "xcoff")] - Xcoff32, - /// A 64-bit XCOFF file. - /// - /// See [`xcoff::XcoffFile64`]. - #[cfg(feature = "xcoff")] - Xcoff64, -} - -impl FileKind { - /// Determine a file kind by parsing the start of the file. - pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> { - Self::parse_at(data, 0) - } - - /// Determine a file kind by parsing at the given offset. - pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> { - let magic = data - .read_bytes_at(offset, 16) - .read_error("Could not read file magic")?; - if magic.len() < 16 { - return Err(Error("File too short")); - } - - let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] { - #[cfg(feature = "archive")] - [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n'] => FileKind::Archive, - #[cfg(feature = "macho")] - [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache, - #[cfg(feature = "elf")] - [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32, - #[cfg(feature = "elf")] - [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64, - #[cfg(feature = "macho")] - [0xfe, 0xed, 0xfa, 0xce, ..] - | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32, - #[cfg(feature = "macho")] - | [0xfe, 0xed, 0xfa, 0xcf, ..] - | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64, - #[cfg(feature = "macho")] - [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32, - #[cfg(feature = "macho")] - [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64, - #[cfg(feature = "wasm")] - [0x00, b'a', b's', b'm', ..] => FileKind::Wasm, - #[cfg(feature = "pe")] - [b'M', b'Z', ..] if offset == 0 => { - // offset == 0 restriction is because optional_header_magic only looks at offset 0 - match pe::optional_header_magic(data) { - Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => { - FileKind::Pe32 - } - Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => { - FileKind::Pe64 - } - _ => return Err(Error("Unknown MS-DOS file")), - } - } - // TODO: more COFF machines - #[cfg(feature = "coff")] - // COFF arm - [0xc4, 0x01, ..] - // COFF arm64 - | [0x64, 0xaa, ..] - // COFF arm64ec - | [0x41, 0xa6, ..] - // COFF x86 - | [0x4c, 0x01, ..] - // COFF x86-64 - | [0x64, 0x86, ..] => FileKind::Coff, - #[cfg(feature = "coff")] - [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport, - #[cfg(feature = "coff")] - [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => { - // offset == 0 restriction is because anon_object_class_id only looks at offset 0 - match coff::anon_object_class_id(data) { - Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig, - _ => return Err(Error("Unknown anon object file")), - } - } - #[cfg(feature = "xcoff")] - [0x01, 0xdf, ..] => FileKind::Xcoff32, - #[cfg(feature = "xcoff")] - [0x01, 0xf7, ..] => FileKind::Xcoff64, - _ => return Err(Error("Unknown file magic")), - }; - Ok(kind) - } -} - -/// An object kind. -/// -/// Returned by [`Object::kind`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum ObjectKind { - /// The object kind is unknown. - Unknown, - /// Relocatable object. - Relocatable, - /// Executable. - Executable, - /// Dynamic shared object. - Dynamic, - /// Core. - Core, -} - -/// The index used to identify a section in a file. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SectionIndex(pub usize); - -/// The index used to identify a symbol in a symbol table. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SymbolIndex(pub usize); - -/// The section where an [`ObjectSymbol`] is defined. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum SymbolSection { - /// The section is unknown. - Unknown, - /// The section is not applicable for this symbol (such as file symbols). - None, - /// The symbol is undefined. - Undefined, - /// The symbol has an absolute value. - Absolute, - /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. - Common, - /// The symbol is defined in the given section. - Section(SectionIndex), -} - -impl SymbolSection { - /// Returns the section index for the section where the symbol is defined. - /// - /// May return `None` if the symbol is not defined in a section. - #[inline] - pub fn index(self) -> Option<SectionIndex> { - if let SymbolSection::Section(index) = self { - Some(index) - } else { - None - } - } -} - -/// An entry in a [`SymbolMap`]. -pub trait SymbolMapEntry { - /// The symbol address. - fn address(&self) -> u64; -} - -/// A map from addresses to symbol information. -/// -/// The symbol information depends on the chosen entry type, such as [`SymbolMapName`]. -/// -/// Returned by [`Object::symbol_map`]. -#[derive(Debug, Default, Clone)] -pub struct SymbolMap<T: SymbolMapEntry> { - symbols: Vec<T>, -} - -impl<T: SymbolMapEntry> SymbolMap<T> { - /// Construct a new symbol map. - /// - /// This function will sort the symbols by address. - pub fn new(mut symbols: Vec<T>) -> Self { - symbols.sort_by_key(|s| s.address()); - SymbolMap { symbols } - } - - /// Get the symbol before the given address. - pub fn get(&self, address: u64) -> Option<&T> { - let index = match self - .symbols - .binary_search_by_key(&address, |symbol| symbol.address()) - { - Ok(index) => index, - Err(index) => index.checked_sub(1)?, - }; - self.symbols.get(index) - } - - /// Get all symbols in the map. - #[inline] - pub fn symbols(&self) -> &[T] { - &self.symbols - } -} - -/// The type used for entries in a [`SymbolMap`] that maps from addresses to names. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SymbolMapName<'data> { - address: u64, - name: &'data str, -} - -impl<'data> SymbolMapName<'data> { - /// Construct a `SymbolMapName`. - pub fn new(address: u64, name: &'data str) -> Self { - SymbolMapName { address, name } - } - - /// The symbol address. - #[inline] - pub fn address(&self) -> u64 { - self.address - } - - /// The symbol name. - #[inline] - pub fn name(&self) -> &'data str { - self.name - } -} - -impl<'data> SymbolMapEntry for SymbolMapName<'data> { - #[inline] - fn address(&self) -> u64 { - self.address - } -} - -/// A map from addresses to symbol names and object files. -/// -/// This is derived from STAB entries in Mach-O files. -/// -/// Returned by [`Object::object_map`]. -#[derive(Debug, Default, Clone)] -pub struct ObjectMap<'data> { - symbols: SymbolMap<ObjectMapEntry<'data>>, - objects: Vec<&'data [u8]>, -} - -impl<'data> ObjectMap<'data> { - /// Get the entry containing the given address. - pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> { - self.symbols - .get(address) - .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size) - } - - /// Get all symbols in the map. - #[inline] - pub fn symbols(&self) -> &[ObjectMapEntry<'data>] { - self.symbols.symbols() - } - - /// Get all objects in the map. - #[inline] - pub fn objects(&self) -> &[&'data [u8]] { - &self.objects - } -} - -/// An [`ObjectMap`] entry. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ObjectMapEntry<'data> { - address: u64, - size: u64, - name: &'data [u8], - object: usize, -} - -impl<'data> ObjectMapEntry<'data> { - /// Get the symbol address. - #[inline] - pub fn address(&self) -> u64 { - self.address - } - - /// Get the symbol size. - /// - /// This may be 0 if the size is unknown. - #[inline] - pub fn size(&self) -> u64 { - self.size - } - - /// Get the symbol name. - #[inline] - pub fn name(&self) -> &'data [u8] { - self.name - } - - /// Get the index of the object file name. - #[inline] - pub fn object_index(&self) -> usize { - self.object - } - - /// Get the object file name. - #[inline] - pub fn object(&self, map: &ObjectMap<'data>) -> &'data [u8] { - map.objects[self.object] - } -} - -impl<'data> SymbolMapEntry for ObjectMapEntry<'data> { - #[inline] - fn address(&self) -> u64 { - self.address - } -} - -/// An imported symbol. -/// -/// Returned by [`Object::imports`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Import<'data> { - library: ByteString<'data>, - // TODO: or ordinal - name: ByteString<'data>, -} - -impl<'data> Import<'data> { - /// The symbol name. - #[inline] - pub fn name(&self) -> &'data [u8] { - self.name.0 - } - - /// The name of the library to import the symbol from. - #[inline] - pub fn library(&self) -> &'data [u8] { - self.library.0 - } -} - -/// An exported symbol. -/// -/// Returned by [`Object::exports`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Export<'data> { - // TODO: and ordinal? - name: ByteString<'data>, - address: u64, -} - -impl<'data> Export<'data> { - /// The symbol name. - #[inline] - pub fn name(&self) -> &'data [u8] { - self.name.0 - } - - /// The virtual address of the symbol. - #[inline] - pub fn address(&self) -> u64 { - self.address - } -} - -/// PDB information from the debug directory in a PE file. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct CodeView<'data> { - guid: [u8; 16], - path: ByteString<'data>, - age: u32, -} - -impl<'data> CodeView<'data> { - /// The path to the PDB as stored in CodeView. - #[inline] - pub fn path(&self) -> &'data [u8] { - self.path.0 - } - - /// The age of the PDB. - #[inline] - pub fn age(&self) -> u32 { - self.age - } - - /// The GUID of the PDB. - #[inline] - pub fn guid(&self) -> [u8; 16] { - self.guid - } -} - -/// The target referenced by a [`Relocation`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum RelocationTarget { - /// The target is a symbol. - Symbol(SymbolIndex), - /// The target is a section. - Section(SectionIndex), - /// The offset is an absolute address. - Absolute, -} - -/// A relocation entry. -/// -/// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`]. -#[derive(Debug)] -pub struct Relocation { - kind: RelocationKind, - encoding: RelocationEncoding, - size: u8, - target: RelocationTarget, - addend: i64, - implicit_addend: bool, -} - -impl Relocation { - /// The operation used to calculate the result of the relocation. - #[inline] - pub fn kind(&self) -> RelocationKind { - self.kind - } - - /// Information about how the result of the relocation operation is encoded in the place. - #[inline] - pub fn encoding(&self) -> RelocationEncoding { - self.encoding - } - - /// The size in bits of the place of the relocation. - /// - /// If 0, then the size is determined by the relocation kind. - #[inline] - pub fn size(&self) -> u8 { - self.size - } - - /// The target of the relocation. - #[inline] - pub fn target(&self) -> RelocationTarget { - self.target - } - - /// The addend to use in the relocation calculation. - #[inline] - pub fn addend(&self) -> i64 { - self.addend - } - - /// Set the addend to use in the relocation calculation. - #[inline] - pub fn set_addend(&mut self, addend: i64) { - self.addend = addend - } - - /// Returns true if there is an implicit addend stored in the data at the offset - /// to be relocated. - #[inline] - pub fn has_implicit_addend(&self) -> bool { - self.implicit_addend - } -} - -/// A data compression format. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum CompressionFormat { - /// The data is uncompressed. - None, - /// The data is compressed, but the compression format is unknown. - Unknown, - /// ZLIB/DEFLATE. - /// - /// Used for ELF compression and GNU compressed debug information. - Zlib, - /// Zstandard. - /// - /// Used for ELF compression. - Zstandard, -} - -/// A range in a file that may be compressed. -/// -/// Returned by [`ObjectSection::compressed_file_range`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct CompressedFileRange { - /// The data compression format. - pub format: CompressionFormat, - /// The file offset of the compressed data. - pub offset: u64, - /// The compressed data size. - pub compressed_size: u64, - /// The uncompressed data size. - pub uncompressed_size: u64, -} - -impl CompressedFileRange { - /// Data that is uncompressed. - #[inline] - pub fn none(range: Option<(u64, u64)>) -> Self { - if let Some((offset, size)) = range { - CompressedFileRange { - format: CompressionFormat::None, - offset, - compressed_size: size, - uncompressed_size: size, - } - } else { - CompressedFileRange { - format: CompressionFormat::None, - offset: 0, - compressed_size: 0, - uncompressed_size: 0, - } - } - } - - /// Convert to [`CompressedData`] by reading from the file. - pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> { - let data = file - .read_bytes_at(self.offset, self.compressed_size) - .read_error("Invalid compressed data size or offset")?; - Ok(CompressedData { - format: self.format, - data, - uncompressed_size: self.uncompressed_size, - }) - } -} - -/// Data that may be compressed. -/// -/// Returned by [`ObjectSection::compressed_data`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct CompressedData<'data> { - /// The data compression format. - pub format: CompressionFormat, - /// The compressed data. - pub data: &'data [u8], - /// The uncompressed data size. - pub uncompressed_size: u64, -} - -impl<'data> CompressedData<'data> { - /// Data that is uncompressed. - #[inline] - pub fn none(data: &'data [u8]) -> Self { - CompressedData { - format: CompressionFormat::None, - data, - uncompressed_size: data.len() as u64, - } - } - - /// Return the uncompressed data. - /// - /// Returns an error for invalid data or unsupported compression. - /// This includes if the data is compressed but the `compression` feature - /// for this crate is disabled. - pub fn decompress(self) -> Result<Cow<'data, [u8]>> { - match self.format { - CompressionFormat::None => Ok(Cow::Borrowed(self.data)), - #[cfg(feature = "compression")] - CompressionFormat::Zlib => { - use core::convert::TryInto; - let size = self - .uncompressed_size - .try_into() - .ok() - .read_error("Uncompressed data size is too large.")?; - let mut decompressed = Vec::with_capacity(size); - let mut decompress = flate2::Decompress::new(true); - decompress - .decompress_vec( - self.data, - &mut decompressed, - flate2::FlushDecompress::Finish, - ) - .ok() - .read_error("Invalid zlib compressed data")?; - Ok(Cow::Owned(decompressed)) - } - #[cfg(feature = "compression")] - CompressionFormat::Zstandard => { - use core::convert::TryInto; - use std::io::Read; - let size = self - .uncompressed_size - .try_into() - .ok() - .read_error("Uncompressed data size is too large.")?; - let mut decompressed = Vec::with_capacity(size); - let mut decoder = ruzstd::StreamingDecoder::new(self.data) - .ok() - .read_error("Invalid zstd compressed data")?; - decoder - .read_to_end(&mut decompressed) - .ok() - .read_error("Invalid zstd compressed data")?; - Ok(Cow::Owned(decompressed)) - } - _ => Err(Error("Unsupported compressed data.")), - } - } -} |