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