1 //! Interface for reading object files.
3 use alloc
::borrow
::Cow
;
5 use core
::{fmt, result}
;
12 #[cfg(feature = "std")]
14 #[cfg(feature = "std")]
15 pub use read_cache
::*;
37 #[cfg(feature = "archive")]
40 #[cfg(feature = "coff")]
43 #[cfg(feature = "elf")]
46 #[cfg(feature = "macho")]
49 #[cfg(feature = "pe")]
55 #[cfg(feature = "wasm")]
62 /// The error type used within the read module.
63 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
64 pub struct Error(&'
static str);
66 impl fmt
::Display
for Error
{
68 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
73 #[cfg(feature = "std")]
74 impl std
::error
::Error
for Error {}
76 /// The result type used within the read module.
77 pub type Result
<T
> = result
::Result
<T
, Error
>;
80 fn read_error(self, error
: &'
static str) -> Result
<T
>;
83 impl<T
> ReadError
<T
> for result
::Result
<T
, ()> {
84 fn read_error(self, error
: &'
static str) -> Result
<T
> {
85 self.map_err(|()| Error(error
))
89 impl<T
> ReadError
<T
> for result
::Result
<T
, Error
> {
90 fn read_error(self, error
: &'
static str) -> Result
<T
> {
91 self.map_err(|_
| Error(error
))
95 impl<T
> ReadError
<T
> for Option
<T
> {
96 fn read_error(self, error
: &'
static str) -> Result
<T
> {
97 self.ok_or(Error(error
))
101 /// The native executable file for the target platform.
104 not(target_os
= "macos"),
105 target_pointer_width
= "32",
108 pub type NativeFile
<'data
, R
= &'data
[u8]> = elf
::ElfFile32
<'data
, crate::Endianness
, R
>;
110 /// The native executable file for the target platform.
113 not(target_os
= "macos"),
114 target_pointer_width
= "64",
117 pub type NativeFile
<'data
, R
= &'data
[u8]> = elf
::ElfFile64
<'data
, crate::Endianness
, R
>;
119 /// The native executable file for the target platform.
120 #[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
121 pub type NativeFile
<'data
, R
= &'data
[u8]> = macho
::MachOFile32
<'data
, crate::Endianness
, R
>;
123 /// The native executable file for the target platform.
124 #[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
125 pub type NativeFile
<'data
, R
= &'data
[u8]> = macho
::MachOFile64
<'data
, crate::Endianness
, R
>;
127 /// The native executable file for the target platform.
128 #[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
129 pub type NativeFile
<'data
, R
= &'data
[u8]> = pe
::PeFile32
<'data
, R
>;
131 /// The native executable file for the target platform.
132 #[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
133 pub type NativeFile
<'data
, R
= &'data
[u8]> = pe
::PeFile64
<'data
, R
>;
135 /// The native executable file for the target platform.
136 #[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
137 pub type NativeFile
<'data
, R
= &'data
[u8]> = wasm
::WasmFile
<'data
, R
>;
139 /// An object file kind.
140 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
144 #[cfg(feature = "archive")]
146 /// A COFF object file.
147 #[cfg(feature = "coff")]
149 /// A dyld cache file containing Mach-O images.
150 #[cfg(feature = "macho")]
152 /// A 32-bit ELF file.
153 #[cfg(feature = "elf")]
155 /// A 64-bit ELF file.
156 #[cfg(feature = "elf")]
158 /// A 32-bit Mach-O file.
159 #[cfg(feature = "macho")]
161 /// A 64-bit Mach-O file.
162 #[cfg(feature = "macho")]
164 /// A 32-bit Mach-O fat binary.
165 #[cfg(feature = "macho")]
167 /// A 64-bit Mach-O fat binary.
168 #[cfg(feature = "macho")]
170 /// A 32-bit PE file.
171 #[cfg(feature = "pe")]
173 /// A 64-bit PE file.
174 #[cfg(feature = "pe")]
177 #[cfg(feature = "wasm")]
182 /// Determine a file kind by parsing the start of the file.
183 pub fn parse
<'data
, R
: ReadRef
<'data
>>(data
: R
) -> Result
<FileKind
> {
184 Self::parse_at(data
, 0)
187 /// Determine a file kind by parsing at the given offset.
188 pub fn parse_at
<'data
, R
: ReadRef
<'data
>>(data
: R
, offset
: u64) -> Result
<FileKind
> {
190 .read_bytes_at(offset
, 16)
191 .read_error("Could not read file magic")?
;
192 if magic
.len() < 16 {
193 return Err(Error("File too short"));
196 let kind
= match [magic
[0], magic
[1], magic
[2], magic
[3], magic
[4], magic
[5], magic
[6], magic
[7]] {
197 #[cfg(feature = "archive")]
198 [b'
!'
, b'
<'
, b'a'
, b'r'
, b'c'
, b'h'
, b'
>'
, b'
\n'
] => FileKind
::Archive
,
199 #[cfg(feature = "macho")]
200 [b'd'
, b'y'
, b'l'
, b'd'
, b'_'
, b'v'
, b'
1'
, b' '
] => FileKind
::DyldCache
,
201 #[cfg(feature = "elf")]
202 [0x7f, b'E'
, b'L'
, b'F'
, 1, ..] => FileKind
::Elf32
,
203 #[cfg(feature = "elf")]
204 [0x7f, b'E'
, b'L'
, b'F'
, 2, ..] => FileKind
::Elf64
,
205 #[cfg(feature = "macho")]
206 [0xfe, 0xed, 0xfa, 0xce, ..]
207 | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind
::MachO32
,
208 #[cfg(feature = "macho")]
209 | [0xfe, 0xed, 0xfa, 0xcf, ..]
210 | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind
::MachO64
,
211 #[cfg(feature = "macho")]
212 [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind
::MachOFat32
,
213 #[cfg(feature = "macho")]
214 [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind
::MachOFat64
,
215 #[cfg(feature = "wasm")]
216 [0x00, b'a'
, b's'
, b'm'
, ..] => FileKind
::Wasm
,
217 #[cfg(feature = "pe")]
218 [b'M'
, b'Z'
, ..] => {
219 match pe
::optional_header_magic(data
) {
220 Ok(crate::pe
::IMAGE_NT_OPTIONAL_HDR32_MAGIC
) => {
223 Ok(crate::pe
::IMAGE_NT_OPTIONAL_HDR64_MAGIC
) => {
226 _
=> return Err(Error("Unknown MS-DOS file")),
229 // TODO: more COFF machines
230 #[cfg(feature = "coff")]
238 | [0x64, 0x86, ..] => FileKind
::Coff
,
239 _
=> return Err(Error("Unknown file magic")),
245 /// The index used to identify a section of a file.
246 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247 pub struct SectionIndex(pub usize);
249 /// The index used to identify a symbol of a file.
250 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
251 pub struct SymbolIndex(pub usize);
253 /// The section where a symbol is defined.
254 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
256 pub enum SymbolSection
{
257 /// The section is unknown.
259 /// The section is not applicable for this symbol (such as file symbols).
261 /// The symbol is undefined.
263 /// The symbol has an absolute value.
265 /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
267 /// The symbol is defined in the given section.
268 Section(SectionIndex
),
272 /// Returns the section index for the section where the symbol is defined.
274 /// May return `None` if the symbol is not defined in a section.
276 pub fn index(self) -> Option
<SectionIndex
> {
277 if let SymbolSection
::Section(index
) = self {
285 /// An entry in a `SymbolMap`.
286 pub trait SymbolMapEntry
{
287 /// The symbol address.
288 fn address(&self) -> u64;
291 /// A map from addresses to symbols.
292 #[derive(Debug, Default, Clone)]
293 pub struct SymbolMap
<T
: SymbolMapEntry
> {
297 impl<T
: SymbolMapEntry
> SymbolMap
<T
> {
298 /// Construct a new symbol map.
300 /// This function will sort the symbols by address.
301 pub fn new(mut symbols
: Vec
<T
>) -> Self {
302 symbols
.sort_unstable_by_key(|s
| s
.address());
303 SymbolMap { symbols }
306 /// Get the symbol before the given address.
307 pub fn get(&self, address
: u64) -> Option
<&T
> {
308 let index
= match self
310 .binary_search_by_key(&address
, |symbol
| symbol
.address())
313 Err(index
) => index
.checked_sub(1)?
,
315 self.symbols
.get(index
)
318 /// Get all symbols in the map.
320 pub fn symbols(&self) -> &[T
] {
325 /// A `SymbolMap` entry for symbol names.
326 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
327 pub struct SymbolMapName
<'data
> {
332 impl<'data
> SymbolMapName
<'data
> {
333 /// Construct a `SymbolMapName`.
334 pub fn new(address
: u64, name
: &'data
str) -> Self {
335 SymbolMapName { address, name }
338 /// The symbol address.
340 pub fn address(&self) -> u64 {
346 pub fn name(&self) -> &'data
str {
351 impl<'data
> SymbolMapEntry
for SymbolMapName
<'data
> {
353 fn address(&self) -> u64 {
358 /// A map from addresses to symbol names and object files.
360 /// This is derived from STAB entries in Mach-O files.
361 #[derive(Debug, Default, Clone)]
362 pub struct ObjectMap
<'data
> {
363 symbols
: SymbolMap
<ObjectMapEntry
<'data
>>,
364 objects
: Vec
<&'data
[u8]>,
367 impl<'data
> ObjectMap
<'data
> {
368 /// Get the entry containing the given address.
369 pub fn get(&self, address
: u64) -> Option
<&ObjectMapEntry
<'data
>> {
372 .filter(|entry
| entry
.size
== 0 || address
.wrapping_sub(entry
.address
) < entry
.size
)
375 /// Get all symbols in the map.
377 pub fn symbols(&self) -> &[ObjectMapEntry
<'data
>] {
378 self.symbols
.symbols()
381 /// Get all objects in the map.
383 pub fn objects(&self) -> &[&'data
[u8]] {
388 /// A `ObjectMap` entry.
389 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
390 pub struct ObjectMapEntry
<'data
> {
397 impl<'data
> ObjectMapEntry
<'data
> {
398 /// Get the symbol address.
400 pub fn address(&self) -> u64 {
404 /// Get the symbol size.
406 /// This may be 0 if the size is unknown.
408 pub fn size(&self) -> u64 {
412 /// Get the symbol name.
414 pub fn name(&self) -> &'data
[u8] {
418 /// Get the index of the object file name.
420 pub fn object_index(&self) -> usize {
424 /// Get the object file name.
426 pub fn object(&self, map
: &ObjectMap
<'data
>) -> &'data
[u8] {
427 map
.objects
[self.object
]
431 impl<'data
> SymbolMapEntry
for ObjectMapEntry
<'data
> {
433 fn address(&self) -> u64 {
438 /// An imported symbol.
439 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
440 pub struct Import
<'data
> {
442 name
: ByteString
<'data
>,
443 library
: ByteString
<'data
>,
446 impl<'data
> Import
<'data
> {
449 pub fn name(&self) -> &'data
[u8] {
453 /// The name of the library to import the symbol from.
455 pub fn library(&self) -> &'data
[u8] {
460 /// An exported symbol.
461 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
462 pub struct Export
<'data
> {
463 // TODO: and ordinal?
464 name
: ByteString
<'data
>,
468 impl<'data
> Export
<'data
> {
471 pub fn name(&self) -> &'data
[u8] {
475 /// The virtual address of the symbol.
477 pub fn address(&self) -> u64 {
483 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
484 pub struct CodeView
<'data
> {
486 path
: ByteString
<'data
>,
490 impl<'data
> CodeView
<'data
> {
491 /// The path to the PDB as stored in CodeView
493 pub fn path(&self) -> &'data
[u8] {
497 /// The age of the PDB
499 pub fn age(&self) -> u32 {
503 /// The GUID of the PDB.
505 pub fn guid(&self) -> [u8; 16] {
510 /// The target referenced by a relocation.
511 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
513 pub enum RelocationTarget
{
514 /// The target is a symbol.
516 /// The target is a section.
517 Section(SectionIndex
),
518 /// The offset is an absolute address.
522 /// A relocation entry.
524 pub struct Relocation
{
525 kind
: RelocationKind
,
526 encoding
: RelocationEncoding
,
528 target
: RelocationTarget
,
530 implicit_addend
: bool
,
534 /// The operation used to calculate the result of the relocation.
536 pub fn kind(&self) -> RelocationKind
{
540 /// Information about how the result of the relocation operation is encoded in the place.
542 pub fn encoding(&self) -> RelocationEncoding
{
546 /// The size in bits of the place of the relocation.
548 /// If 0, then the size is determined by the relocation kind.
550 pub fn size(&self) -> u8 {
554 /// The target of the relocation.
556 pub fn target(&self) -> RelocationTarget
{
560 /// The addend to use in the relocation calculation.
562 pub fn addend(&self) -> i64 {
566 /// Set the addend to use in the relocation calculation.
568 pub fn set_addend(&mut self, addend
: i64) {
572 /// Returns true if there is an implicit addend stored in the data at the offset
575 pub fn has_implicit_addend(&self) -> bool
{
580 /// A data compression format.
581 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
583 pub enum CompressionFormat
{
584 /// The data is uncompressed.
586 /// The data is compressed, but the compression format is unknown.
590 /// Used for ELF compression and GNU compressed debug information.
594 /// A range in a file that may be compressed.
595 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
596 pub struct CompressedFileRange
{
597 /// The data compression format.
598 pub format
: CompressionFormat
,
599 /// The file offset of the compressed data.
601 /// The compressed data size.
602 pub compressed_size
: u64,
603 /// The uncompressed data size.
604 pub uncompressed_size
: u64,
607 impl CompressedFileRange
{
608 /// Data that is uncompressed.
610 pub fn none(range
: Option
<(u64, u64)>) -> Self {
611 if let Some((offset
, size
)) = range
{
612 CompressedFileRange
{
613 format
: CompressionFormat
::None
,
615 compressed_size
: size
,
616 uncompressed_size
: size
,
619 CompressedFileRange
{
620 format
: CompressionFormat
::None
,
623 uncompressed_size
: 0,
628 /// Convert to `CompressedData` by reading from the file.
629 pub fn data
<'data
, R
: ReadRef
<'data
>>(self, file
: R
) -> Result
<CompressedData
<'data
>> {
631 .read_bytes_at(self.offset
, self.compressed_size
)
632 .read_error("Invalid compressed data size or offset")?
;
636 uncompressed_size
: self.uncompressed_size
,
641 /// Data that may be compressed.
642 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
643 pub struct CompressedData
<'data
> {
644 /// The data compression format.
645 pub format
: CompressionFormat
,
646 /// The compressed data.
647 pub data
: &'data
[u8],
648 /// The uncompressed data size.
649 pub uncompressed_size
: u64,
652 impl<'data
> CompressedData
<'data
> {
653 /// Data that is uncompressed.
655 pub fn none(data
: &'data
[u8]) -> Self {
657 format
: CompressionFormat
::None
,
659 uncompressed_size
: data
.len() as u64,
663 /// Return the uncompressed data.
665 /// Returns an error for invalid data or unsupported compression.
666 /// This includes if the data is compressed but the `compression` feature
667 /// for this crate is disabled.
668 pub fn decompress(self) -> Result
<Cow
<'data
, [u8]>> {
670 CompressionFormat
::None
=> Ok(Cow
::Borrowed(self.data
)),
671 #[cfg(feature = "compression")]
672 CompressionFormat
::Zlib
=> {
673 use core
::convert
::TryInto
;
678 .read_error("Uncompressed data size is too large.")?
;
679 let mut decompressed
= Vec
::with_capacity(size
);
680 let mut decompress
= flate2
::Decompress
::new(true);
685 flate2
::FlushDecompress
::Finish
,
688 .read_error("Invalid zlib compressed data")?
;
689 Ok(Cow
::Owned(decompressed
))
691 _
=> Err(Error("Unsupported compressed data.")),