Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/object-0.36.7/src/read/mod.rs
Line
Count
Source
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
44
use alloc::borrow::Cow;
45
use alloc::vec::Vec;
46
use core::{fmt, result};
47
48
#[cfg(not(feature = "std"))]
49
use alloc::collections::btree_map::BTreeMap as Map;
50
#[cfg(feature = "std")]
51
use std::collections::hash_map::HashMap as Map;
52
53
pub use crate::common::*;
54
55
mod read_ref;
56
pub use read_ref::*;
57
58
mod read_cache;
59
pub use read_cache::*;
60
61
mod util;
62
pub use util::*;
63
64
#[cfg(any(feature = "elf", feature = "macho"))]
65
mod 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
))]
75
mod any;
76
#[cfg(any(
77
    feature = "coff",
78
    feature = "elf",
79
    feature = "macho",
80
    feature = "pe",
81
    feature = "wasm",
82
    feature = "xcoff"
83
))]
84
pub use any::*;
85
86
#[cfg(feature = "archive")]
87
pub mod archive;
88
89
#[cfg(feature = "coff")]
90
pub mod coff;
91
92
#[cfg(feature = "elf")]
93
pub mod elf;
94
95
#[cfg(feature = "macho")]
96
pub mod macho;
97
98
#[cfg(feature = "pe")]
99
pub mod pe;
100
101
#[cfg(feature = "wasm")]
102
pub mod wasm;
103
104
#[cfg(feature = "xcoff")]
105
pub mod xcoff;
106
107
mod traits;
108
pub use traits::*;
109
110
mod private {
111
    pub trait Sealed {}
112
}
113
114
/// The error type used within the read module.
115
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116
pub struct Error(pub(crate) &'static str);
117
118
impl fmt::Display for Error {
119
    #[inline]
120
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121
0
        f.write_str(self.0)
122
0
    }
123
}
124
125
#[cfg(feature = "std")]
126
impl std::error::Error for Error {}
127
#[cfg(all(not(feature = "std"), core_error))]
128
impl core::error::Error for Error {}
129
130
/// The result type used within the read module.
131
pub type Result<T> = result::Result<T, Error>;
132
133
trait ReadError<T> {
134
    fn read_error(self, error: &'static str) -> Result<T>;
135
}
136
137
impl<T> ReadError<T> for result::Result<T, ()> {
138
0
    fn read_error(self, error: &'static str) -> Result<T> {
139
0
        self.map_err(|()| Error(error))
Unexecuted instantiation: <core::result::Result<core::option::Option<&object::elf::SectionHeader64<object::endian::LittleEndian>>, ()> as object::read::ReadError<core::option::Option<&object::elf::SectionHeader64<object::endian::LittleEndian>>>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<object::read::util::Bytes, ()> as object::read::ReadError<object::read::util::Bytes>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::elf::FileHeader64<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::elf::FileHeader64<object::endian::LittleEndian>>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::elf::NoteHeader32<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::elf::NoteHeader32<object::endian::LittleEndian>>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::elf::SectionHeader64<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::elf::SectionHeader64<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::elf::Sym64<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::elf::Sym64<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::endian::U16Bytes<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::endian::U16Bytes<object::endian::LittleEndian>>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageBaseRelocation, ()> as object::read::ReadError<&object::pe::ImageBaseRelocation>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageExportDirectory, ()> as object::read::ReadError<&object::pe::ImageExportDirectory>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageImportDescriptor, ()> as object::read::ReadError<&object::pe::ImageImportDescriptor>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageResourceDataEntry, ()> as object::read::ReadError<&object::pe::ImageResourceDataEntry>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageResourceDirectory, ()> as object::read::ReadError<&object::pe::ImageResourceDirectory>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&object::pe::ImageDelayloadDescriptor, ()> as object::read::ReadError<&object::pe::ImageDelayloadDescriptor>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::endian::U16Bytes<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::endian::U16Bytes<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::endian::U32Bytes<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::endian::U32Bytes<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::pe::ImageDataDirectory], ()> as object::read::ReadError<&[object::pe::ImageDataDirectory]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::pe::ImageResourceDirectoryEntry], ()> as object::read::ReadError<&[object::pe::ImageResourceDirectoryEntry]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[u8], ()> as object::read::ReadError<&[u8]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<u32, ()> as object::read::ReadError<u32>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<(), ()> as object::read::ReadError<()>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<u64, ()> as object::read::ReadError<u64>>::read_error::{closure#0}
140
0
    }
Unexecuted instantiation: <core::result::Result<core::option::Option<&object::elf::SectionHeader64<object::endian::LittleEndian>>, ()> as object::read::ReadError<core::option::Option<&object::elf::SectionHeader64<object::endian::LittleEndian>>>>::read_error
Unexecuted instantiation: <core::result::Result<object::read::util::Bytes, ()> as object::read::ReadError<object::read::util::Bytes>>::read_error
Unexecuted instantiation: <core::result::Result<&object::elf::FileHeader64<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::elf::FileHeader64<object::endian::LittleEndian>>>::read_error
Unexecuted instantiation: <core::result::Result<&object::elf::NoteHeader32<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::elf::NoteHeader32<object::endian::LittleEndian>>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::elf::SectionHeader64<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::elf::SectionHeader64<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::elf::Sym64<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::elf::Sym64<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&object::endian::U16Bytes<object::endian::LittleEndian>, ()> as object::read::ReadError<&object::endian::U16Bytes<object::endian::LittleEndian>>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageBaseRelocation, ()> as object::read::ReadError<&object::pe::ImageBaseRelocation>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageExportDirectory, ()> as object::read::ReadError<&object::pe::ImageExportDirectory>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageImportDescriptor, ()> as object::read::ReadError<&object::pe::ImageImportDescriptor>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageResourceDataEntry, ()> as object::read::ReadError<&object::pe::ImageResourceDataEntry>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageResourceDirectory, ()> as object::read::ReadError<&object::pe::ImageResourceDirectory>>::read_error
Unexecuted instantiation: <core::result::Result<&object::pe::ImageDelayloadDescriptor, ()> as object::read::ReadError<&object::pe::ImageDelayloadDescriptor>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::endian::U16Bytes<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::endian::U16Bytes<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::endian::U32Bytes<object::endian::LittleEndian>], ()> as object::read::ReadError<&[object::endian::U32Bytes<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::pe::ImageDataDirectory], ()> as object::read::ReadError<&[object::pe::ImageDataDirectory]>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::pe::ImageResourceDirectoryEntry], ()> as object::read::ReadError<&[object::pe::ImageResourceDirectoryEntry]>>::read_error
Unexecuted instantiation: <core::result::Result<&[u8], ()> as object::read::ReadError<&[u8]>>::read_error
Unexecuted instantiation: <core::result::Result<u32, ()> as object::read::ReadError<u32>>::read_error
Unexecuted instantiation: <core::result::Result<(), ()> as object::read::ReadError<()>>::read_error
Unexecuted instantiation: <core::result::Result<u64, ()> as object::read::ReadError<u64>>::read_error
141
}
142
143
impl<T> ReadError<T> for result::Result<T, Error> {
144
0
    fn read_error(self, error: &'static str) -> Result<T> {
145
0
        self.map_err(|_| Error(error))
Unexecuted instantiation: <core::result::Result<&[object::elf::Sym64<object::endian::LittleEndian>], object::read::Error> as object::read::ReadError<&[object::elf::Sym64<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[object::endian::U32Bytes<object::endian::LittleEndian>], object::read::Error> as object::read::ReadError<&[object::endian::U32Bytes<object::endian::LittleEndian>]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<&[u8], object::read::Error> as object::read::ReadError<&[u8]>>::read_error::{closure#0}
Unexecuted instantiation: <core::result::Result<_, object::read::Error> as object::read::ReadError<_>>::read_error::{closure#0}
146
0
    }
Unexecuted instantiation: <core::result::Result<&[object::elf::Sym64<object::endian::LittleEndian>], object::read::Error> as object::read::ReadError<&[object::elf::Sym64<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&[object::endian::U32Bytes<object::endian::LittleEndian>], object::read::Error> as object::read::ReadError<&[object::endian::U32Bytes<object::endian::LittleEndian>]>>::read_error
Unexecuted instantiation: <core::result::Result<&[u8], object::read::Error> as object::read::ReadError<&[u8]>>::read_error
Unexecuted instantiation: <core::result::Result<_, object::read::Error> as object::read::ReadError<_>>::read_error
147
}
148
149
impl<T> ReadError<T> for Option<T> {
150
0
    fn read_error(self, error: &'static str) -> Result<T> {
151
0
        self.ok_or(Error(error))
152
0
    }
Unexecuted instantiation: <core::option::Option<object::read::util::StringTable> as object::read::ReadError<object::read::util::StringTable>>::read_error
Unexecuted instantiation: <core::option::Option<object::endian::LittleEndian> as object::read::ReadError<object::endian::LittleEndian>>::read_error
Unexecuted instantiation: <core::option::Option<&object::elf::SectionHeader64<object::endian::LittleEndian>> as object::read::ReadError<&object::elf::SectionHeader64<object::endian::LittleEndian>>>::read_error
Unexecuted instantiation: <core::option::Option<u64> as object::read::ReadError<u64>>::read_error
Unexecuted instantiation: <core::option::Option<&mut object::read::pe::export::Export> as object::read::ReadError<&mut object::read::pe::export::Export>>::read_error
Unexecuted instantiation: <core::option::Option<&object::endian::U32Bytes<object::endian::LittleEndian>> as object::read::ReadError<&object::endian::U32Bytes<object::endian::LittleEndian>>>::read_error
Unexecuted instantiation: <core::option::Option<&object::pe::ImageSectionHeader> as object::read::ReadError<&object::pe::ImageSectionHeader>>::read_error
Unexecuted instantiation: <core::option::Option<(u32, u32)> as object::read::ReadError<(u32, u32)>>::read_error
Unexecuted instantiation: <core::option::Option<usize> as object::read::ReadError<usize>>::read_error
Unexecuted instantiation: <core::option::Option<u32> as object::read::ReadError<u32>>::read_error
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
))]
162
pub 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
))]
171
pub 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"))]
175
pub 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"))]
180
pub 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"))]
185
pub 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"))]
189
pub 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"))]
193
pub 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]
198
pub 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
283
impl FileKind {
284
    /// Determine a file kind by parsing the start of the file.
285
0
    pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
286
0
        Self::parse_at(data, 0)
287
0
    }
288
289
    /// Determine a file kind by parsing at the given offset.
290
0
    pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> {
291
0
        let magic = data
292
0
            .read_bytes_at(offset, 16)
293
0
            .read_error("Could not read file magic")?;
294
0
        if magic.len() < 16 {
295
0
            return Err(Error("File too short"));
296
0
        }
297
298
0
        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
0
            | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive,
302
            #[cfg(feature = "macho")]
303
0
            [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache,
304
            #[cfg(feature = "elf")]
305
0
            [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
306
            #[cfg(feature = "elf")]
307
0
            [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
308
            #[cfg(feature = "macho")]
309
            [0xfe, 0xed, 0xfa, 0xce, ..]
310
0
            | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
311
            #[cfg(feature = "macho")]
312
            | [0xfe, 0xed, 0xfa, 0xcf, ..]
313
0
            | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
314
            #[cfg(feature = "macho")]
315
0
            [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
316
            #[cfg(feature = "macho")]
317
0
            [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
0
            [b'M', b'Z', ..] if offset == 0 => {
322
                // offset == 0 restriction is because optional_header_magic only looks at offset 0
323
0
                match pe::optional_header_magic(data) {
324
                    Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
325
0
                        FileKind::Pe32
326
                    }
327
                    Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
328
0
                        FileKind::Pe64
329
                    }
330
0
                    _ => 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 x86
342
            | [0x4c, 0x01, ..]
343
            // COFF x86-64
344
0
            | [0x64, 0x86, ..] => FileKind::Coff,
345
            #[cfg(feature = "coff")]
346
0
            [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
347
            #[cfg(feature = "coff")]
348
0
            [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
349
                // offset == 0 restriction is because anon_object_class_id only looks at offset 0
350
0
                match coff::anon_object_class_id(data) {
351
0
                    Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
352
0
                    _ => return Err(Error("Unknown anon object file")),
353
                }
354
            }
355
            #[cfg(feature = "xcoff")]
356
0
            [0x01, 0xdf, ..] => FileKind::Xcoff32,
357
            #[cfg(feature = "xcoff")]
358
0
            [0x01, 0xf7, ..] => FileKind::Xcoff64,
359
0
            _ => return Err(Error("Unknown file magic")),
360
        };
361
0
        Ok(kind)
362
0
    }
363
}
364
365
/// An object kind.
366
///
367
/// Returned by [`Object::kind`].
368
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
369
#[non_exhaustive]
370
pub enum ObjectKind {
371
    /// The object kind is unknown.
372
    Unknown,
373
    /// Relocatable object.
374
    Relocatable,
375
    /// Executable.
376
    Executable,
377
    /// Dynamic shared object.
378
    Dynamic,
379
    /// Core.
380
    Core,
381
}
382
383
/// The index used to identify a section in a file.
384
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
385
pub struct SectionIndex(pub usize);
386
387
impl fmt::Display for SectionIndex {
388
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389
0
        self.0.fmt(f)
390
0
    }
391
}
392
393
/// The index used to identify a symbol in a symbol table.
394
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
395
pub struct SymbolIndex(pub usize);
396
397
impl fmt::Display for SymbolIndex {
398
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399
0
        self.0.fmt(f)
400
0
    }
401
}
402
403
/// The section where an [`ObjectSymbol`] is defined.
404
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
405
#[non_exhaustive]
406
pub enum SymbolSection {
407
    /// The section is unknown.
408
    Unknown,
409
    /// The section is not applicable for this symbol (such as file symbols).
410
    None,
411
    /// The symbol is undefined.
412
    Undefined,
413
    /// The symbol has an absolute value.
414
    Absolute,
415
    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
416
    Common,
417
    /// The symbol is defined in the given section.
418
    Section(SectionIndex),
419
}
420
421
impl SymbolSection {
422
    /// Returns the section index for the section where the symbol is defined.
423
    ///
424
    /// May return `None` if the symbol is not defined in a section.
425
    #[inline]
426
0
    pub fn index(self) -> Option<SectionIndex> {
427
0
        if let SymbolSection::Section(index) = self {
428
0
            Some(index)
429
        } else {
430
0
            None
431
        }
432
0
    }
433
}
434
435
/// An entry in a [`SymbolMap`].
436
pub trait SymbolMapEntry {
437
    /// The symbol address.
438
    fn address(&self) -> u64;
439
}
440
441
/// A map from addresses to symbol information.
442
///
443
/// The symbol information depends on the chosen entry type, such as [`SymbolMapName`].
444
///
445
/// Returned by [`Object::symbol_map`].
446
#[derive(Debug, Default, Clone)]
447
pub struct SymbolMap<T: SymbolMapEntry> {
448
    symbols: Vec<T>,
449
}
450
451
impl<T: SymbolMapEntry> SymbolMap<T> {
452
    /// Construct a new symbol map.
453
    ///
454
    /// This function will sort the symbols by address.
455
0
    pub fn new(mut symbols: Vec<T>) -> Self {
456
0
        symbols.sort_by_key(|s| s.address());
457
0
        SymbolMap { symbols }
458
0
    }
459
460
    /// Get the symbol before the given address.
461
0
    pub fn get(&self, address: u64) -> Option<&T> {
462
0
        let index = match self
463
0
            .symbols
464
0
            .binary_search_by_key(&address, |symbol| symbol.address())
465
        {
466
0
            Ok(index) => index,
467
0
            Err(index) => index.checked_sub(1)?,
468
        };
469
0
        self.symbols.get(index)
470
0
    }
471
472
    /// Get all symbols in the map.
473
    #[inline]
474
0
    pub fn symbols(&self) -> &[T] {
475
0
        &self.symbols
476
0
    }
477
}
478
479
/// The type used for entries in a [`SymbolMap`] that maps from addresses to names.
480
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
481
pub struct SymbolMapName<'data> {
482
    address: u64,
483
    name: &'data str,
484
}
485
486
impl<'data> SymbolMapName<'data> {
487
    /// Construct a `SymbolMapName`.
488
0
    pub fn new(address: u64, name: &'data str) -> Self {
489
0
        SymbolMapName { address, name }
490
0
    }
491
492
    /// The symbol address.
493
    #[inline]
494
0
    pub fn address(&self) -> u64 {
495
0
        self.address
496
0
    }
497
498
    /// The symbol name.
499
    #[inline]
500
0
    pub fn name(&self) -> &'data str {
501
0
        self.name
502
0
    }
503
}
504
505
impl<'data> SymbolMapEntry for SymbolMapName<'data> {
506
    #[inline]
507
0
    fn address(&self) -> u64 {
508
0
        self.address
509
0
    }
510
}
511
512
/// A map from addresses to symbol names and object files.
513
///
514
/// This is derived from STAB entries in Mach-O files.
515
///
516
/// Returned by [`Object::object_map`].
517
#[derive(Debug, Default, Clone)]
518
pub struct ObjectMap<'data> {
519
    symbols: SymbolMap<ObjectMapEntry<'data>>,
520
    objects: Vec<ObjectMapFile<'data>>,
521
}
522
523
impl<'data> ObjectMap<'data> {
524
    /// Get the entry containing the given address.
525
0
    pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
526
0
        self.symbols
527
0
            .get(address)
528
0
            .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
529
0
    }
530
531
    /// Get all symbols in the map.
532
    #[inline]
533
0
    pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
534
0
        self.symbols.symbols()
535
0
    }
536
537
    /// Get all objects in the map.
538
    #[inline]
539
0
    pub fn objects(&self) -> &[ObjectMapFile<'data>] {
540
0
        &self.objects
541
0
    }
542
}
543
544
/// A symbol in an [`ObjectMap`].
545
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
546
pub struct ObjectMapEntry<'data> {
547
    address: u64,
548
    size: u64,
549
    name: &'data [u8],
550
    object: usize,
551
}
552
553
impl<'data> ObjectMapEntry<'data> {
554
    /// Get the symbol address.
555
    #[inline]
556
0
    pub fn address(&self) -> u64 {
557
0
        self.address
558
0
    }
559
560
    /// Get the symbol size.
561
    ///
562
    /// This may be 0 if the size is unknown.
563
    #[inline]
564
0
    pub fn size(&self) -> u64 {
565
0
        self.size
566
0
    }
567
568
    /// Get the symbol name.
569
    #[inline]
570
0
    pub fn name(&self) -> &'data [u8] {
571
0
        self.name
572
0
    }
573
574
    /// Get the index of the object file name.
575
    #[inline]
576
0
    pub fn object_index(&self) -> usize {
577
0
        self.object
578
0
    }
579
580
    /// Get the object file name.
581
    #[inline]
582
0
    pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
583
0
        &map.objects[self.object]
584
0
    }
585
}
586
587
impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
588
    #[inline]
589
0
    fn address(&self) -> u64 {
590
0
        self.address
591
0
    }
592
}
593
594
/// An object file name in an [`ObjectMap`].
595
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
596
pub struct ObjectMapFile<'data> {
597
    path: &'data [u8],
598
    member: Option<&'data [u8]>,
599
}
600
601
impl<'data> ObjectMapFile<'data> {
602
    #[cfg(feature = "macho")]
603
0
    fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
604
0
        ObjectMapFile { path, member }
605
0
    }
606
607
    /// Get the path to the file containing the object.
608
    #[inline]
609
0
    pub fn path(&self) -> &'data [u8] {
610
0
        self.path
611
0
    }
612
613
    /// If the file is an archive, get the name of the member containing the object.
614
    #[inline]
615
0
    pub fn member(&self) -> Option<&'data [u8]> {
616
0
        self.member
617
0
    }
618
}
619
620
/// An imported symbol.
621
///
622
/// Returned by [`Object::imports`].
623
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
624
pub struct Import<'data> {
625
    library: ByteString<'data>,
626
    // TODO: or ordinal
627
    name: ByteString<'data>,
628
}
629
630
impl<'data> Import<'data> {
631
    /// The symbol name.
632
    #[inline]
633
0
    pub fn name(&self) -> &'data [u8] {
634
0
        self.name.0
635
0
    }
636
637
    /// The name of the library to import the symbol from.
638
    #[inline]
639
0
    pub fn library(&self) -> &'data [u8] {
640
0
        self.library.0
641
0
    }
642
}
643
644
/// An exported symbol.
645
///
646
/// Returned by [`Object::exports`].
647
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
648
pub struct Export<'data> {
649
    // TODO: and ordinal?
650
    name: ByteString<'data>,
651
    address: u64,
652
}
653
654
impl<'data> Export<'data> {
655
    /// The symbol name.
656
    #[inline]
657
0
    pub fn name(&self) -> &'data [u8] {
658
0
        self.name.0
659
0
    }
660
661
    /// The virtual address of the symbol.
662
    #[inline]
663
0
    pub fn address(&self) -> u64 {
664
0
        self.address
665
0
    }
666
}
667
668
/// PDB information from the debug directory in a PE file.
669
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
670
pub struct CodeView<'data> {
671
    guid: [u8; 16],
672
    path: ByteString<'data>,
673
    age: u32,
674
}
675
676
impl<'data> CodeView<'data> {
677
    /// The path to the PDB as stored in CodeView.
678
    #[inline]
679
0
    pub fn path(&self) -> &'data [u8] {
680
0
        self.path.0
681
0
    }
682
683
    /// The age of the PDB.
684
    #[inline]
685
0
    pub fn age(&self) -> u32 {
686
0
        self.age
687
0
    }
688
689
    /// The GUID of the PDB.
690
    #[inline]
691
0
    pub fn guid(&self) -> [u8; 16] {
692
0
        self.guid
693
0
    }
694
}
695
696
/// The target referenced by a [`Relocation`].
697
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
698
#[non_exhaustive]
699
pub enum RelocationTarget {
700
    /// The target is a symbol.
701
    Symbol(SymbolIndex),
702
    /// The target is a section.
703
    Section(SectionIndex),
704
    /// The offset is an absolute address.
705
    Absolute,
706
}
707
708
/// A relocation entry.
709
///
710
/// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`].
711
#[derive(Debug)]
712
pub struct Relocation {
713
    kind: RelocationKind,
714
    encoding: RelocationEncoding,
715
    size: u8,
716
    target: RelocationTarget,
717
    addend: i64,
718
    implicit_addend: bool,
719
    flags: RelocationFlags,
720
}
721
722
impl Relocation {
723
    /// The operation used to calculate the result of the relocation.
724
    #[inline]
725
0
    pub fn kind(&self) -> RelocationKind {
726
0
        self.kind
727
0
    }
728
729
    /// Information about how the result of the relocation operation is encoded in the place.
730
    #[inline]
731
0
    pub fn encoding(&self) -> RelocationEncoding {
732
0
        self.encoding
733
0
    }
734
735
    /// The size in bits of the place of the relocation.
736
    ///
737
    /// If 0, then the size is determined by the relocation kind.
738
    #[inline]
739
0
    pub fn size(&self) -> u8 {
740
0
        self.size
741
0
    }
742
743
    /// The target of the relocation.
744
    #[inline]
745
0
    pub fn target(&self) -> RelocationTarget {
746
0
        self.target
747
0
    }
748
749
    /// The addend to use in the relocation calculation.
750
    #[inline]
751
0
    pub fn addend(&self) -> i64 {
752
0
        self.addend
753
0
    }
754
755
    /// Set the addend to use in the relocation calculation.
756
    #[inline]
757
0
    pub fn set_addend(&mut self, addend: i64) {
758
0
        self.addend = addend;
759
0
    }
760
761
    /// Returns true if there is an implicit addend stored in the data at the offset
762
    /// to be relocated.
763
    #[inline]
764
0
    pub fn has_implicit_addend(&self) -> bool {
765
0
        self.implicit_addend
766
0
    }
767
768
    /// Relocation flags that are specific to each file format.
769
    ///
770
    /// The values returned by `kind`, `encoding` and `size` are derived
771
    /// from these flags.
772
    #[inline]
773
0
    pub fn flags(&self) -> RelocationFlags {
774
0
        self.flags
775
0
    }
776
}
777
778
/// A map from section offsets to relocation information.
779
///
780
/// This can be used to apply relocations to a value at a given section offset.
781
/// This is intended for use with DWARF in relocatable object files, and only
782
/// supports relocations that are used in DWARF.
783
///
784
/// Returned by [`ObjectSection::relocation_map`].
785
#[derive(Debug, Default)]
786
pub struct RelocationMap(Map<u64, RelocationMapEntry>);
787
788
impl RelocationMap {
789
    /// Construct a new relocation map for a section.
790
    ///
791
    /// Fails if any relocation cannot be added to the map.
792
    /// You can manually use `add` if you need different error handling,
793
    /// such as to list all errors or to ignore them.
794
0
    pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
795
0
    where
796
0
        T: Object<'data>,
797
    {
798
0
        let mut map = RelocationMap(Map::new());
799
0
        for (offset, relocation) in section.relocations() {
800
0
            map.add(file, offset, relocation)?;
801
        }
802
0
        Ok(map)
803
0
    }
804
805
    /// Add a single relocation to the map.
806
0
    pub fn add<'data: 'file, 'file, T>(
807
0
        &mut self,
808
0
        file: &'file T,
809
0
        offset: u64,
810
0
        relocation: Relocation,
811
0
    ) -> Result<()>
812
0
    where
813
0
        T: Object<'data>,
814
    {
815
0
        let mut entry = RelocationMapEntry {
816
0
            implicit_addend: relocation.has_implicit_addend(),
817
0
            addend: relocation.addend() as u64,
818
0
        };
819
0
        match relocation.kind() {
820
0
            RelocationKind::Absolute => match relocation.target() {
821
0
                RelocationTarget::Symbol(symbol_idx) => {
822
0
                    let symbol = file
823
0
                        .symbol_by_index(symbol_idx)
824
0
                        .read_error("Relocation with invalid symbol")?;
825
0
                    entry.addend = symbol.address().wrapping_add(entry.addend);
826
                }
827
0
                RelocationTarget::Section(section_idx) => {
828
0
                    let section = file
829
0
                        .section_by_index(section_idx)
830
0
                        .read_error("Relocation with invalid section")?;
831
                    // DWARF parsers expect references to DWARF sections to be section offsets,
832
                    // not addresses. Addresses are useful for everything else.
833
0
                    if section.kind() != SectionKind::Debug {
834
0
                        entry.addend = section.address().wrapping_add(entry.addend);
835
0
                    }
836
                }
837
                _ => {
838
0
                    return Err(Error("Unsupported relocation target"));
839
                }
840
            },
841
            _ => {
842
0
                return Err(Error("Unsupported relocation type"));
843
            }
844
        }
845
0
        if self.0.insert(offset, entry).is_some() {
846
0
            return Err(Error("Multiple relocations for offset"));
847
0
        }
848
0
        Ok(())
849
0
    }
850
851
    /// Relocate a value that was read from the section at the given offset.
852
0
    pub fn relocate(&self, offset: u64, value: u64) -> u64 {
853
0
        if let Some(relocation) = self.0.get(&offset) {
854
0
            if relocation.implicit_addend {
855
                // Use the explicit addend too, because it may have the symbol value.
856
0
                value.wrapping_add(relocation.addend)
857
            } else {
858
0
                relocation.addend
859
            }
860
        } else {
861
0
            value
862
        }
863
0
    }
864
}
865
866
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
867
struct RelocationMapEntry {
868
    implicit_addend: bool,
869
    addend: u64,
870
}
871
872
/// A data compression format.
873
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
874
#[non_exhaustive]
875
pub enum CompressionFormat {
876
    /// The data is uncompressed.
877
    None,
878
    /// The data is compressed, but the compression format is unknown.
879
    Unknown,
880
    /// ZLIB/DEFLATE.
881
    ///
882
    /// Used for ELF compression and GNU compressed debug information.
883
    Zlib,
884
    /// Zstandard.
885
    ///
886
    /// Used for ELF compression.
887
    Zstandard,
888
}
889
890
/// A range in a file that may be compressed.
891
///
892
/// Returned by [`ObjectSection::compressed_file_range`].
893
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
894
pub struct CompressedFileRange {
895
    /// The data compression format.
896
    pub format: CompressionFormat,
897
    /// The file offset of the compressed data.
898
    pub offset: u64,
899
    /// The compressed data size.
900
    pub compressed_size: u64,
901
    /// The uncompressed data size.
902
    pub uncompressed_size: u64,
903
}
904
905
impl CompressedFileRange {
906
    /// Data that is uncompressed.
907
    #[inline]
908
0
    pub fn none(range: Option<(u64, u64)>) -> Self {
909
0
        if let Some((offset, size)) = range {
910
0
            CompressedFileRange {
911
0
                format: CompressionFormat::None,
912
0
                offset,
913
0
                compressed_size: size,
914
0
                uncompressed_size: size,
915
0
            }
916
        } else {
917
0
            CompressedFileRange {
918
0
                format: CompressionFormat::None,
919
0
                offset: 0,
920
0
                compressed_size: 0,
921
0
                uncompressed_size: 0,
922
0
            }
923
        }
924
0
    }
925
926
    /// Convert to [`CompressedData`] by reading from the file.
927
0
    pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
928
0
        let data = file
929
0
            .read_bytes_at(self.offset, self.compressed_size)
930
0
            .read_error("Invalid compressed data size or offset")?;
931
0
        Ok(CompressedData {
932
0
            format: self.format,
933
0
            data,
934
0
            uncompressed_size: self.uncompressed_size,
935
0
        })
936
0
    }
937
}
938
939
/// Data that may be compressed.
940
///
941
/// Returned by [`ObjectSection::compressed_data`].
942
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
943
pub struct CompressedData<'data> {
944
    /// The data compression format.
945
    pub format: CompressionFormat,
946
    /// The compressed data.
947
    pub data: &'data [u8],
948
    /// The uncompressed data size.
949
    pub uncompressed_size: u64,
950
}
951
952
impl<'data> CompressedData<'data> {
953
    /// Data that is uncompressed.
954
    #[inline]
955
0
    pub fn none(data: &'data [u8]) -> Self {
956
0
        CompressedData {
957
0
            format: CompressionFormat::None,
958
0
            data,
959
0
            uncompressed_size: data.len() as u64,
960
0
        }
961
0
    }
962
963
    /// Return the uncompressed data.
964
    ///
965
    /// Returns an error for invalid data or unsupported compression.
966
    /// This includes if the data is compressed but the `compression` feature
967
    /// for this crate is disabled.
968
0
    pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
969
0
        match self.format {
970
0
            CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
971
            #[cfg(feature = "compression")]
972
            CompressionFormat::Zlib | CompressionFormat::Zstandard => {
973
                use core::convert::TryInto;
974
                use std::io::Read;
975
                let size = self
976
                    .uncompressed_size
977
                    .try_into()
978
                    .ok()
979
                    .read_error("Uncompressed data size is too large.")?;
980
                let mut decompressed = Vec::new();
981
                decompressed
982
                    .try_reserve_exact(size)
983
                    .ok()
984
                    .read_error("Uncompressed data allocation failed")?;
985
986
                match self.format {
987
                    CompressionFormat::Zlib => {
988
                        let mut decompress = flate2::Decompress::new(true);
989
                        decompress
990
                            .decompress_vec(
991
                                self.data,
992
                                &mut decompressed,
993
                                flate2::FlushDecompress::Finish,
994
                            )
995
                            .ok()
996
                            .read_error("Invalid zlib compressed data")?;
997
                    }
998
                    CompressionFormat::Zstandard => {
999
                        let mut input = self.data;
1000
                        while !input.is_empty() {
1001
                            let mut decoder = match ruzstd::StreamingDecoder::new(&mut input) {
1002
                                Ok(decoder) => decoder,
1003
                                Err(
1004
                                    ruzstd::frame_decoder::FrameDecoderError::ReadFrameHeaderError(
1005
                                        ruzstd::frame::ReadFrameHeaderError::SkipFrame {
1006
                                            length,
1007
                                            ..
1008
                                        },
1009
                                    ),
1010
                                ) => {
1011
                                    input = input
1012
                                        .get(length as usize..)
1013
                                        .read_error("Invalid zstd compressed data")?;
1014
                                    continue;
1015
                                }
1016
                                x => x.ok().read_error("Invalid zstd compressed data")?,
1017
                            };
1018
                            decoder
1019
                                .read_to_end(&mut decompressed)
1020
                                .ok()
1021
                                .read_error("Invalid zstd compressed data")?;
1022
                        }
1023
                    }
1024
                    _ => unreachable!(),
1025
                }
1026
                if size != decompressed.len() {
1027
                    return Err(Error(
1028
                        "Uncompressed data size does not match compression header",
1029
                    ));
1030
                }
1031
1032
                Ok(Cow::Owned(decompressed))
1033
            }
1034
0
            _ => Err(Error("Unsupported compressed data.")),
1035
        }
1036
0
    }
1037
}