object/read/
mod.rs

1//! Interface for reading object files.
2//!
3//! ## Unified read API
4//!
5//! The [`Object`] trait provides a unified read API for accessing common features of
6//! object files, such as sections and symbols. There is an implementation of this
7//! trait for [`File`], which allows reading any file format, as well as implementations
8//! for each file format:
9//! [`ElfFile`](elf::ElfFile), [`MachOFile`](macho::MachOFile), [`CoffFile`](coff::CoffFile),
10//! [`PeFile`](pe::PeFile), [`WasmFile`](wasm::WasmFile), [`XcoffFile`](xcoff::XcoffFile).
11//!
12//! ## Low level read API
13//!
14//! The submodules for each file format define helpers that operate on the raw structs.
15//! These can be used instead of the unified API, or in conjunction with it to access
16//! details that are not available via the unified API.
17//!
18//! See the [submodules](#modules) for examples of the low level read API.
19//!
20//! ## Naming Convention
21//!
22//! Types that form part of the unified API for a file format are prefixed with the
23//! name of the file format.
24//!
25//! ## Example for unified read API
26//!  ```no_run
27//! use object::{Object, ObjectSection};
28//! use std::error::Error;
29//! use std::fs;
30//!
31//! /// Reads a file and displays the name of each section.
32//! fn main() -> Result<(), Box<dyn Error>> {
33//! #   #[cfg(all(feature = "read", feature = "std"))] {
34//!     let data = fs::read("path/to/binary")?;
35//!     let file = object::File::parse(&*data)?;
36//!     for section in file.sections() {
37//!         println!("{}", section.name()?);
38//!     }
39//! #   }
40//!     Ok(())
41//! }
42//! ```
43
44use alloc::borrow::Cow;
45use alloc::vec::Vec;
46use core::{fmt, result};
47
48#[cfg(not(feature = "std"))]
49use alloc::collections::btree_map::BTreeMap as Map;
50#[cfg(feature = "std")]
51use std::collections::hash_map::HashMap as Map;
52
53pub use crate::common::*;
54
55mod read_ref;
56pub use read_ref::*;
57
58mod read_cache;
59pub use read_cache::*;
60
61mod util;
62pub use util::*;
63
64#[cfg(any(feature = "elf", feature = "macho"))]
65mod gnu_compression;
66
67#[cfg(any(
68    feature = "coff",
69    feature = "elf",
70    feature = "macho",
71    feature = "pe",
72    feature = "wasm",
73    feature = "xcoff"
74))]
75mod any;
76#[cfg(any(
77    feature = "coff",
78    feature = "elf",
79    feature = "macho",
80    feature = "pe",
81    feature = "wasm",
82    feature = "xcoff"
83))]
84pub use any::*;
85
86#[cfg(feature = "archive")]
87pub mod archive;
88
89#[cfg(feature = "coff")]
90pub mod coff;
91
92#[cfg(feature = "elf")]
93pub mod elf;
94
95#[cfg(feature = "macho")]
96pub mod macho;
97
98#[cfg(feature = "pe")]
99pub mod pe;
100
101#[cfg(feature = "wasm")]
102pub mod wasm;
103
104#[cfg(feature = "xcoff")]
105pub mod xcoff;
106
107mod traits;
108pub use traits::*;
109
110mod private {
111    pub trait Sealed {}
112}
113
114/// The error type used within the read module.
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct Error(pub(crate) &'static str);
117
118impl fmt::Display for Error {
119    #[inline]
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.write_str(self.0)
122    }
123}
124
125#[cfg(feature = "std")]
126impl std::error::Error for Error {}
127
128/// The result type used within the read module.
129pub type Result<T> = result::Result<T, Error>;
130
131trait ReadError<T> {
132    fn read_error(self, error: &'static str) -> Result<T>;
133}
134
135impl<T> ReadError<T> for result::Result<T, ()> {
136    fn read_error(self, error: &'static str) -> Result<T> {
137        self.map_err(|()| Error(error))
138    }
139}
140
141impl<T> ReadError<T> for result::Result<T, Error> {
142    fn read_error(self, error: &'static str) -> Result<T> {
143        self.map_err(|_| Error(error))
144    }
145}
146
147impl<T> ReadError<T> for Option<T> {
148    fn read_error(self, error: &'static str) -> Result<T> {
149        self.ok_or(Error(error))
150    }
151}
152
153/// The native executable file for the target platform.
154#[cfg(all(
155    unix,
156    not(target_os = "macos"),
157    target_pointer_width = "32",
158    feature = "elf"
159))]
160pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::endian::Endianness, R>;
161
162/// The native executable file for the target platform.
163#[cfg(all(
164    unix,
165    not(target_os = "macos"),
166    target_pointer_width = "64",
167    feature = "elf"
168))]
169pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::endian::Endianness, R>;
170
171/// The native executable file for the target platform.
172#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
173pub type NativeFile<'data, R = &'data [u8]> =
174    macho::MachOFile32<'data, crate::endian::Endianness, R>;
175
176/// The native executable file for the target platform.
177#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
178pub type NativeFile<'data, R = &'data [u8]> =
179    macho::MachOFile64<'data, crate::endian::Endianness, R>;
180
181/// The native executable file for the target platform.
182#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
183pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>;
184
185/// The native executable file for the target platform.
186#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
187pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>;
188
189/// The native executable file for the target platform.
190#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
191pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>;
192
193/// A file format kind.
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
195#[non_exhaustive]
196pub enum FileKind {
197    /// A Unix archive.
198    ///
199    /// See [`archive::ArchiveFile`].
200    #[cfg(feature = "archive")]
201    Archive,
202    /// A COFF object file.
203    ///
204    /// See [`coff::CoffFile`].
205    #[cfg(feature = "coff")]
206    Coff,
207    /// A COFF bigobj object file.
208    ///
209    /// This supports a larger number of sections.
210    ///
211    /// See [`coff::CoffBigFile`].
212    #[cfg(feature = "coff")]
213    CoffBig,
214    /// A Windows short import file.
215    ///
216    /// See [`coff::ImportFile`].
217    #[cfg(feature = "coff")]
218    CoffImport,
219    /// A dyld cache file containing Mach-O images.
220    ///
221    /// See [`macho::DyldCache`]
222    #[cfg(feature = "macho")]
223    DyldCache,
224    /// A 32-bit ELF file.
225    ///
226    /// See [`elf::ElfFile32`].
227    #[cfg(feature = "elf")]
228    Elf32,
229    /// A 64-bit ELF file.
230    ///
231    /// See [`elf::ElfFile64`].
232    #[cfg(feature = "elf")]
233    Elf64,
234    /// A 32-bit Mach-O file.
235    ///
236    /// See [`macho::MachOFile32`].
237    #[cfg(feature = "macho")]
238    MachO32,
239    /// A 64-bit Mach-O file.
240    ///
241    /// See [`macho::MachOFile64`].
242    #[cfg(feature = "macho")]
243    MachO64,
244    /// A 32-bit Mach-O fat binary.
245    ///
246    /// See [`macho::MachOFatFile32`].
247    #[cfg(feature = "macho")]
248    MachOFat32,
249    /// A 64-bit Mach-O fat binary.
250    ///
251    /// See [`macho::MachOFatFile64`].
252    #[cfg(feature = "macho")]
253    MachOFat64,
254    /// A 32-bit PE file.
255    ///
256    /// See [`pe::PeFile32`].
257    #[cfg(feature = "pe")]
258    Pe32,
259    /// A 64-bit PE file.
260    ///
261    /// See [`pe::PeFile64`].
262    #[cfg(feature = "pe")]
263    Pe64,
264    /// A Wasm file.
265    ///
266    /// See [`wasm::WasmFile`].
267    #[cfg(feature = "wasm")]
268    Wasm,
269    /// A 32-bit XCOFF file.
270    ///
271    /// See [`xcoff::XcoffFile32`].
272    #[cfg(feature = "xcoff")]
273    Xcoff32,
274    /// A 64-bit XCOFF file.
275    ///
276    /// See [`xcoff::XcoffFile64`].
277    #[cfg(feature = "xcoff")]
278    Xcoff64,
279}
280
281impl FileKind {
282    /// Determine a file kind by parsing the start of the file.
283    pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
284        Self::parse_at(data, 0)
285    }
286
287    /// Determine a file kind by parsing at the given offset.
288    pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> {
289        let magic = data
290            .read_bytes_at(offset, 16)
291            .read_error("Could not read file magic")?;
292        if magic.len() < 16 {
293            return Err(Error("File too short"));
294        }
295
296        let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] {
297            #[cfg(feature = "archive")]
298            [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n']
299            | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive,
300            #[cfg(feature = "macho")]
301            [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache,
302            #[cfg(feature = "elf")]
303            [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
304            #[cfg(feature = "elf")]
305            [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
306            #[cfg(feature = "macho")]
307            [0xfe, 0xed, 0xfa, 0xce, ..]
308            | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
309            #[cfg(feature = "macho")]
310            | [0xfe, 0xed, 0xfa, 0xcf, ..]
311            | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
312            #[cfg(feature = "macho")]
313            [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
314            #[cfg(feature = "macho")]
315            [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64,
316            #[cfg(feature = "wasm")]
317            [0x00, b'a', b's', b'm', _, _, 0x00, 0x00] => FileKind::Wasm,
318            #[cfg(feature = "pe")]
319            [b'M', b'Z', ..] if offset == 0 => {
320                // offset == 0 restriction is because optional_header_magic only looks at offset 0
321                match pe::optional_header_magic(data) {
322                    Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
323                        FileKind::Pe32
324                    }
325                    Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
326                        FileKind::Pe64
327                    }
328                    _ => return Err(Error("Unknown MS-DOS file")),
329                }
330            }
331            // TODO: more COFF machines
332            #[cfg(feature = "coff")]
333            // COFF arm
334            [0xc4, 0x01, ..]
335            // COFF arm64
336            | [0x64, 0xaa, ..]
337            // COFF arm64ec
338            | [0x41, 0xa6, ..]
339            // COFF x86
340            | [0x4c, 0x01, ..]
341            // COFF x86-64
342            | [0x64, 0x86, ..] => FileKind::Coff,
343            #[cfg(feature = "coff")]
344            [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
345            #[cfg(feature = "coff")]
346            [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
347                // offset == 0 restriction is because anon_object_class_id only looks at offset 0
348                match coff::anon_object_class_id(data) {
349                    Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
350                    _ => return Err(Error("Unknown anon object file")),
351                }
352            }
353            #[cfg(feature = "xcoff")]
354            [0x01, 0xdf, ..] => FileKind::Xcoff32,
355            #[cfg(feature = "xcoff")]
356            [0x01, 0xf7, ..] => FileKind::Xcoff64,
357            _ => return Err(Error("Unknown file magic")),
358        };
359        Ok(kind)
360    }
361}
362
363/// An object kind.
364///
365/// Returned by [`Object::kind`].
366#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
367#[non_exhaustive]
368pub enum ObjectKind {
369    /// The object kind is unknown.
370    Unknown,
371    /// Relocatable object.
372    Relocatable,
373    /// Executable.
374    Executable,
375    /// Dynamic shared object.
376    Dynamic,
377    /// Core.
378    Core,
379}
380
381/// The index used to identify a section in a file.
382#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
383pub struct SectionIndex(pub usize);
384
385impl fmt::Display for SectionIndex {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        self.0.fmt(f)
388    }
389}
390
391/// The index used to identify a symbol in a symbol table.
392#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
393pub struct SymbolIndex(pub usize);
394
395impl fmt::Display for SymbolIndex {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        self.0.fmt(f)
398    }
399}
400
401/// The section where an [`ObjectSymbol`] is defined.
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
403#[non_exhaustive]
404pub enum SymbolSection {
405    /// The section is unknown.
406    Unknown,
407    /// The section is not applicable for this symbol (such as file symbols).
408    None,
409    /// The symbol is undefined.
410    Undefined,
411    /// The symbol has an absolute value.
412    Absolute,
413    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
414    Common,
415    /// The symbol is defined in the given section.
416    Section(SectionIndex),
417}
418
419impl SymbolSection {
420    /// Returns the section index for the section where the symbol is defined.
421    ///
422    /// May return `None` if the symbol is not defined in a section.
423    #[inline]
424    pub fn index(self) -> Option<SectionIndex> {
425        if let SymbolSection::Section(index) = self {
426            Some(index)
427        } else {
428            None
429        }
430    }
431}
432
433/// An entry in a [`SymbolMap`].
434pub trait SymbolMapEntry {
435    /// The symbol address.
436    fn address(&self) -> u64;
437}
438
439/// A map from addresses to symbol information.
440///
441/// The symbol information depends on the chosen entry type, such as [`SymbolMapName`].
442///
443/// Returned by [`Object::symbol_map`].
444#[derive(Debug, Default, Clone)]
445pub struct SymbolMap<T: SymbolMapEntry> {
446    symbols: Vec<T>,
447}
448
449impl<T: SymbolMapEntry> SymbolMap<T> {
450    /// Construct a new symbol map.
451    ///
452    /// This function will sort the symbols by address.
453    pub fn new(mut symbols: Vec<T>) -> Self {
454        symbols.sort_by_key(|s| s.address());
455        SymbolMap { symbols }
456    }
457
458    /// Get the symbol before the given address.
459    pub fn get(&self, address: u64) -> Option<&T> {
460        let index = match self
461            .symbols
462            .binary_search_by_key(&address, |symbol| symbol.address())
463        {
464            Ok(index) => index,
465            Err(index) => index.checked_sub(1)?,
466        };
467        self.symbols.get(index)
468    }
469
470    /// Get all symbols in the map.
471    #[inline]
472    pub fn symbols(&self) -> &[T] {
473        &self.symbols
474    }
475}
476
477/// The type used for entries in a [`SymbolMap`] that maps from addresses to names.
478#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
479pub struct SymbolMapName<'data> {
480    address: u64,
481    name: &'data str,
482}
483
484impl<'data> SymbolMapName<'data> {
485    /// Construct a `SymbolMapName`.
486    pub fn new(address: u64, name: &'data str) -> Self {
487        SymbolMapName { address, name }
488    }
489
490    /// The symbol address.
491    #[inline]
492    pub fn address(&self) -> u64 {
493        self.address
494    }
495
496    /// The symbol name.
497    #[inline]
498    pub fn name(&self) -> &'data str {
499        self.name
500    }
501}
502
503impl<'data> SymbolMapEntry for SymbolMapName<'data> {
504    #[inline]
505    fn address(&self) -> u64 {
506        self.address
507    }
508}
509
510/// A map from addresses to symbol names and object files.
511///
512/// This is derived from STAB entries in Mach-O files.
513///
514/// Returned by [`Object::object_map`].
515#[derive(Debug, Default, Clone)]
516pub struct ObjectMap<'data> {
517    symbols: SymbolMap<ObjectMapEntry<'data>>,
518    objects: Vec<ObjectMapFile<'data>>,
519}
520
521impl<'data> ObjectMap<'data> {
522    /// Get the entry containing the given address.
523    pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
524        self.symbols
525            .get(address)
526            .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
527    }
528
529    /// Get all symbols in the map.
530    #[inline]
531    pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
532        self.symbols.symbols()
533    }
534
535    /// Get all objects in the map.
536    #[inline]
537    pub fn objects(&self) -> &[ObjectMapFile<'data>] {
538        &self.objects
539    }
540}
541
542/// A symbol in an [`ObjectMap`].
543#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
544pub struct ObjectMapEntry<'data> {
545    address: u64,
546    size: u64,
547    name: &'data [u8],
548    object: usize,
549}
550
551impl<'data> ObjectMapEntry<'data> {
552    /// Get the symbol address.
553    #[inline]
554    pub fn address(&self) -> u64 {
555        self.address
556    }
557
558    /// Get the symbol size.
559    ///
560    /// This may be 0 if the size is unknown.
561    #[inline]
562    pub fn size(&self) -> u64 {
563        self.size
564    }
565
566    /// Get the symbol name.
567    #[inline]
568    pub fn name(&self) -> &'data [u8] {
569        self.name
570    }
571
572    /// Get the index of the object file name.
573    #[inline]
574    pub fn object_index(&self) -> usize {
575        self.object
576    }
577
578    /// Get the object file name.
579    #[inline]
580    pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
581        &map.objects[self.object]
582    }
583}
584
585impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
586    #[inline]
587    fn address(&self) -> u64 {
588        self.address
589    }
590}
591
592/// An object file name in an [`ObjectMap`].
593#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
594pub struct ObjectMapFile<'data> {
595    path: &'data [u8],
596    member: Option<&'data [u8]>,
597}
598
599impl<'data> ObjectMapFile<'data> {
600    #[cfg(feature = "macho")]
601    fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
602        ObjectMapFile { path, member }
603    }
604
605    /// Get the path to the file containing the object.
606    #[inline]
607    pub fn path(&self) -> &'data [u8] {
608        self.path
609    }
610
611    /// If the file is an archive, get the name of the member containing the object.
612    #[inline]
613    pub fn member(&self) -> Option<&'data [u8]> {
614        self.member
615    }
616}
617
618/// An imported symbol.
619///
620/// Returned by [`Object::imports`].
621#[derive(Debug, Clone, Copy, PartialEq, Eq)]
622pub struct Import<'data> {
623    library: ByteString<'data>,
624    // TODO: or ordinal
625    name: ByteString<'data>,
626}
627
628impl<'data> Import<'data> {
629    /// The symbol name.
630    #[inline]
631    pub fn name(&self) -> &'data [u8] {
632        self.name.0
633    }
634
635    /// The name of the library to import the symbol from.
636    #[inline]
637    pub fn library(&self) -> &'data [u8] {
638        self.library.0
639    }
640}
641
642/// An exported symbol.
643///
644/// Returned by [`Object::exports`].
645#[derive(Debug, Clone, Copy, PartialEq, Eq)]
646pub struct Export<'data> {
647    // TODO: and ordinal?
648    name: ByteString<'data>,
649    address: u64,
650}
651
652impl<'data> Export<'data> {
653    /// The symbol name.
654    #[inline]
655    pub fn name(&self) -> &'data [u8] {
656        self.name.0
657    }
658
659    /// The virtual address of the symbol.
660    #[inline]
661    pub fn address(&self) -> u64 {
662        self.address
663    }
664}
665
666/// PDB information from the debug directory in a PE file.
667#[derive(Debug, Clone, Copy, PartialEq, Eq)]
668pub struct CodeView<'data> {
669    guid: [u8; 16],
670    path: ByteString<'data>,
671    age: u32,
672}
673
674impl<'data> CodeView<'data> {
675    /// The path to the PDB as stored in CodeView.
676    #[inline]
677    pub fn path(&self) -> &'data [u8] {
678        self.path.0
679    }
680
681    /// The age of the PDB.
682    #[inline]
683    pub fn age(&self) -> u32 {
684        self.age
685    }
686
687    /// The GUID of the PDB.
688    #[inline]
689    pub fn guid(&self) -> [u8; 16] {
690        self.guid
691    }
692}
693
694/// The target referenced by a [`Relocation`].
695#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
696#[non_exhaustive]
697pub enum RelocationTarget {
698    /// The target is a symbol.
699    Symbol(SymbolIndex),
700    /// The target is a section.
701    Section(SectionIndex),
702    /// The offset is an absolute address.
703    Absolute,
704}
705
706/// A relocation entry.
707///
708/// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`].
709#[derive(Debug)]
710pub struct Relocation {
711    kind: RelocationKind,
712    encoding: RelocationEncoding,
713    size: u8,
714    target: RelocationTarget,
715    addend: i64,
716    implicit_addend: bool,
717    flags: RelocationFlags,
718}
719
720impl Relocation {
721    /// The operation used to calculate the result of the relocation.
722    #[inline]
723    pub fn kind(&self) -> RelocationKind {
724        self.kind
725    }
726
727    /// Information about how the result of the relocation operation is encoded in the place.
728    #[inline]
729    pub fn encoding(&self) -> RelocationEncoding {
730        self.encoding
731    }
732
733    /// The size in bits of the place of the relocation.
734    ///
735    /// If 0, then the size is determined by the relocation kind.
736    #[inline]
737    pub fn size(&self) -> u8 {
738        self.size
739    }
740
741    /// The target of the relocation.
742    #[inline]
743    pub fn target(&self) -> RelocationTarget {
744        self.target
745    }
746
747    /// The addend to use in the relocation calculation.
748    #[inline]
749    pub fn addend(&self) -> i64 {
750        self.addend
751    }
752
753    /// Set the addend to use in the relocation calculation.
754    #[inline]
755    pub fn set_addend(&mut self, addend: i64) {
756        self.addend = addend;
757    }
758
759    /// Returns true if there is an implicit addend stored in the data at the offset
760    /// to be relocated.
761    #[inline]
762    pub fn has_implicit_addend(&self) -> bool {
763        self.implicit_addend
764    }
765
766    /// Relocation flags that are specific to each file format.
767    ///
768    /// The values returned by `kind`, `encoding` and `size` are derived
769    /// from these flags.
770    #[inline]
771    pub fn flags(&self) -> RelocationFlags {
772        self.flags
773    }
774}
775
776/// A map from section offsets to relocation information.
777///
778/// This can be used to apply relocations to a value at a given section offset.
779/// This is intended for use with DWARF in relocatable object files, and only
780/// supports relocations that are used in DWARF.
781///
782/// Returned by [`ObjectSection::relocation_map`].
783#[derive(Debug, Default)]
784pub struct RelocationMap(Map<u64, RelocationMapEntry>);
785
786impl RelocationMap {
787    /// Construct a new relocation map for a section.
788    ///
789    /// Fails if any relocation cannot be added to the map.
790    /// You can manually use `add` if you need different error handling,
791    /// such as to list all errors or to ignore them.
792    pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
793    where
794        T: Object<'data>,
795    {
796        let mut map = RelocationMap(Map::new());
797        for (offset, relocation) in section.relocations() {
798            map.add(file, offset, relocation)?;
799        }
800        Ok(map)
801    }
802
803    /// Add a single relocation to the map.
804    pub fn add<'data: 'file, 'file, T>(
805        &mut self,
806        file: &'file T,
807        offset: u64,
808        relocation: Relocation,
809    ) -> Result<()>
810    where
811        T: Object<'data>,
812    {
813        let mut entry = RelocationMapEntry {
814            implicit_addend: relocation.has_implicit_addend(),
815            addend: relocation.addend() as u64,
816        };
817        match relocation.kind() {
818            RelocationKind::Absolute => match relocation.target() {
819                RelocationTarget::Symbol(symbol_idx) => {
820                    let symbol = file
821                        .symbol_by_index(symbol_idx)
822                        .read_error("Relocation with invalid symbol")?;
823                    entry.addend = symbol.address().wrapping_add(entry.addend);
824                }
825                RelocationTarget::Section(section_idx) => {
826                    let section = file
827                        .section_by_index(section_idx)
828                        .read_error("Relocation with invalid section")?;
829                    // DWARF parsers expect references to DWARF sections to be section offsets,
830                    // not addresses. Addresses are useful for everything else.
831                    if section.kind() != SectionKind::Debug {
832                        entry.addend = section.address().wrapping_add(entry.addend);
833                    }
834                }
835                _ => {
836                    return Err(Error("Unsupported relocation target"));
837                }
838            },
839            _ => {
840                return Err(Error("Unsupported relocation type"));
841            }
842        }
843        if self.0.insert(offset, entry).is_some() {
844            return Err(Error("Multiple relocations for offset"));
845        }
846        Ok(())
847    }
848
849    /// Relocate a value that was read from the section at the given offset.
850    pub fn relocate(&self, offset: u64, value: u64) -> u64 {
851        if let Some(relocation) = self.0.get(&offset) {
852            if relocation.implicit_addend {
853                // Use the explicit addend too, because it may have the symbol value.
854                value.wrapping_add(relocation.addend)
855            } else {
856                relocation.addend
857            }
858        } else {
859            value
860        }
861    }
862}
863
864#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
865struct RelocationMapEntry {
866    implicit_addend: bool,
867    addend: u64,
868}
869
870/// A data compression format.
871#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
872#[non_exhaustive]
873pub enum CompressionFormat {
874    /// The data is uncompressed.
875    None,
876    /// The data is compressed, but the compression format is unknown.
877    Unknown,
878    /// ZLIB/DEFLATE.
879    ///
880    /// Used for ELF compression and GNU compressed debug information.
881    Zlib,
882    /// Zstandard.
883    ///
884    /// Used for ELF compression.
885    Zstandard,
886}
887
888/// A range in a file that may be compressed.
889///
890/// Returned by [`ObjectSection::compressed_file_range`].
891#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
892pub struct CompressedFileRange {
893    /// The data compression format.
894    pub format: CompressionFormat,
895    /// The file offset of the compressed data.
896    pub offset: u64,
897    /// The compressed data size.
898    pub compressed_size: u64,
899    /// The uncompressed data size.
900    pub uncompressed_size: u64,
901}
902
903impl CompressedFileRange {
904    /// Data that is uncompressed.
905    #[inline]
906    pub fn none(range: Option<(u64, u64)>) -> Self {
907        if let Some((offset, size)) = range {
908            CompressedFileRange {
909                format: CompressionFormat::None,
910                offset,
911                compressed_size: size,
912                uncompressed_size: size,
913            }
914        } else {
915            CompressedFileRange {
916                format: CompressionFormat::None,
917                offset: 0,
918                compressed_size: 0,
919                uncompressed_size: 0,
920            }
921        }
922    }
923
924    /// Convert to [`CompressedData`] by reading from the file.
925    pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
926        let data = file
927            .read_bytes_at(self.offset, self.compressed_size)
928            .read_error("Invalid compressed data size or offset")?;
929        Ok(CompressedData {
930            format: self.format,
931            data,
932            uncompressed_size: self.uncompressed_size,
933        })
934    }
935}
936
937/// Data that may be compressed.
938///
939/// Returned by [`ObjectSection::compressed_data`].
940#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
941pub struct CompressedData<'data> {
942    /// The data compression format.
943    pub format: CompressionFormat,
944    /// The compressed data.
945    pub data: &'data [u8],
946    /// The uncompressed data size.
947    pub uncompressed_size: u64,
948}
949
950impl<'data> CompressedData<'data> {
951    /// Data that is uncompressed.
952    #[inline]
953    pub fn none(data: &'data [u8]) -> Self {
954        CompressedData {
955            format: CompressionFormat::None,
956            data,
957            uncompressed_size: data.len() as u64,
958        }
959    }
960
961    /// Return the uncompressed data.
962    ///
963    /// Returns an error for invalid data or unsupported compression.
964    /// This includes if the data is compressed but the `compression` feature
965    /// for this crate is disabled.
966    pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
967        match self.format {
968            CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
969            #[cfg(feature = "compression")]
970            CompressionFormat::Zlib | CompressionFormat::Zstandard => {
971                use core::convert::TryInto;
972                use std::io::Read;
973                let size = self
974                    .uncompressed_size
975                    .try_into()
976                    .ok()
977                    .read_error("Uncompressed data size is too large.")?;
978                let mut decompressed = Vec::new();
979                decompressed
980                    .try_reserve_exact(size)
981                    .ok()
982                    .read_error("Uncompressed data allocation failed")?;
983
984                match self.format {
985                    CompressionFormat::Zlib => {
986                        let mut decompress = flate2::Decompress::new(true);
987                        decompress
988                            .decompress_vec(
989                                self.data,
990                                &mut decompressed,
991                                flate2::FlushDecompress::Finish,
992                            )
993                            .ok()
994                            .read_error("Invalid zlib compressed data")?;
995                    }
996                    CompressionFormat::Zstandard => {
997                        let mut input = self.data;
998                        while !input.is_empty() {
999                            let mut decoder = match ruzstd::StreamingDecoder::new(&mut input) {
1000                                Ok(decoder) => decoder,
1001                                Err(
1002                                    ruzstd::frame_decoder::FrameDecoderError::ReadFrameHeaderError(
1003                                        ruzstd::frame::ReadFrameHeaderError::SkipFrame {
1004                                            length,
1005                                            ..
1006                                        },
1007                                    ),
1008                                ) => {
1009                                    input = input
1010                                        .get(length as usize..)
1011                                        .read_error("Invalid zstd compressed data")?;
1012                                    continue;
1013                                }
1014                                x => x.ok().read_error("Invalid zstd compressed data")?,
1015                            };
1016                            decoder
1017                                .read_to_end(&mut decompressed)
1018                                .ok()
1019                                .read_error("Invalid zstd compressed data")?;
1020                        }
1021                    }
1022                    _ => unreachable!(),
1023                }
1024                if size != decompressed.len() {
1025                    return Err(Error(
1026                        "Uncompressed data size does not match compression header",
1027                    ));
1028                }
1029
1030                Ok(Cow::Owned(decompressed))
1031            }
1032            _ => Err(Error("Unsupported compressed data.")),
1033        }
1034    }
1035}