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 (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 | use crate::common::*; |
49 | |
50 | mod read_ref; |
51 | pub use read_ref::*; |
52 | |
53 | #[cfg (feature = "std" )] |
54 | mod read_cache; |
55 | #[cfg (feature = "std" )] |
56 | pub use read_cache::*; |
57 | |
58 | mod util; |
59 | pub use util::*; |
60 | |
61 | #[cfg (any( |
62 | feature = "coff" , |
63 | feature = "elf" , |
64 | feature = "macho" , |
65 | feature = "pe" , |
66 | feature = "wasm" , |
67 | feature = "xcoff" |
68 | ))] |
69 | mod any; |
70 | #[cfg (any( |
71 | feature = "coff" , |
72 | feature = "elf" , |
73 | feature = "macho" , |
74 | feature = "pe" , |
75 | feature = "wasm" , |
76 | feature = "xcoff" |
77 | ))] |
78 | pub use any::*; |
79 | |
80 | #[cfg (feature = "archive" )] |
81 | pub mod archive; |
82 | |
83 | #[cfg (feature = "coff" )] |
84 | pub mod coff; |
85 | |
86 | #[cfg (feature = "elf" )] |
87 | pub mod elf; |
88 | |
89 | #[cfg (feature = "macho" )] |
90 | pub mod macho; |
91 | |
92 | #[cfg (feature = "pe" )] |
93 | pub mod pe; |
94 | |
95 | #[cfg (feature = "wasm" )] |
96 | pub mod wasm; |
97 | |
98 | #[cfg (feature = "xcoff" )] |
99 | pub mod xcoff; |
100 | |
101 | mod traits; |
102 | pub use traits::*; |
103 | |
104 | mod private { |
105 | pub trait Sealed {} |
106 | } |
107 | |
108 | /// The error type used within the read module. |
109 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
110 | pub struct Error(&'static str); |
111 | |
112 | impl fmt::Display for Error { |
113 | #[inline ] |
114 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
115 | f.write_str(self.0) |
116 | } |
117 | } |
118 | |
119 | #[cfg (feature = "std" )] |
120 | impl std::error::Error for Error {} |
121 | |
122 | /// The result type used within the read module. |
123 | pub type Result<T> = result::Result<T, Error>; |
124 | |
125 | trait ReadError<T> { |
126 | fn read_error(self, error: &'static str) -> Result<T>; |
127 | } |
128 | |
129 | impl<T> ReadError<T> for result::Result<T, ()> { |
130 | fn read_error(self, error: &'static str) -> Result<T> { |
131 | self.map_err(|()| Error(error)) |
132 | } |
133 | } |
134 | |
135 | impl<T> ReadError<T> for result::Result<T, Error> { |
136 | fn read_error(self, error: &'static str) -> Result<T> { |
137 | self.map_err(|_| Error(error)) |
138 | } |
139 | } |
140 | |
141 | impl<T> ReadError<T> for Option<T> { |
142 | fn read_error(self, error: &'static str) -> Result<T> { |
143 | self.ok_or(err:Error(error)) |
144 | } |
145 | } |
146 | |
147 | /// The native executable file for the target platform. |
148 | #[cfg (all( |
149 | unix, |
150 | not(target_os = "macos" ), |
151 | target_pointer_width = "32" , |
152 | feature = "elf" |
153 | ))] |
154 | pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::Endianness, R>; |
155 | |
156 | /// The native executable file for the target platform. |
157 | #[cfg (all( |
158 | unix, |
159 | not(target_os = "macos" ), |
160 | target_pointer_width = "64" , |
161 | feature = "elf" |
162 | ))] |
163 | pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::Endianness, R>; |
164 | |
165 | /// The native executable file for the target platform. |
166 | #[cfg (all(target_os = "macos" , target_pointer_width = "32" , feature = "macho" ))] |
167 | pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile32<'data, crate::Endianness, R>; |
168 | |
169 | /// The native executable file for the target platform. |
170 | #[cfg (all(target_os = "macos" , target_pointer_width = "64" , feature = "macho" ))] |
171 | pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile64<'data, crate::Endianness, R>; |
172 | |
173 | /// The native executable file for the target platform. |
174 | #[cfg (all(target_os = "windows" , target_pointer_width = "32" , feature = "pe" ))] |
175 | pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>; |
176 | |
177 | /// The native executable file for the target platform. |
178 | #[cfg (all(target_os = "windows" , target_pointer_width = "64" , feature = "pe" ))] |
179 | pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>; |
180 | |
181 | /// The native executable file for the target platform. |
182 | #[cfg (all(feature = "wasm" , target_arch = "wasm32" , feature = "wasm" ))] |
183 | pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>; |
184 | |
185 | /// A file format kind. |
186 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
187 | #[non_exhaustive ] |
188 | pub enum FileKind { |
189 | /// A Unix archive. |
190 | /// |
191 | /// See [`archive::ArchiveFile`]. |
192 | #[cfg (feature = "archive" )] |
193 | Archive, |
194 | /// A COFF object file. |
195 | /// |
196 | /// See [`coff::CoffFile`]. |
197 | #[cfg (feature = "coff" )] |
198 | Coff, |
199 | /// A COFF bigobj object file. |
200 | /// |
201 | /// This supports a larger number of sections. |
202 | /// |
203 | /// See [`coff::CoffBigFile`]. |
204 | #[cfg (feature = "coff" )] |
205 | CoffBig, |
206 | /// A Windows short import file. |
207 | /// |
208 | /// See [`coff::ImportFile`]. |
209 | #[cfg (feature = "coff" )] |
210 | CoffImport, |
211 | /// A dyld cache file containing Mach-O images. |
212 | /// |
213 | /// See [`macho::DyldCache`] |
214 | #[cfg (feature = "macho" )] |
215 | DyldCache, |
216 | /// A 32-bit ELF file. |
217 | /// |
218 | /// See [`elf::ElfFile32`]. |
219 | #[cfg (feature = "elf" )] |
220 | Elf32, |
221 | /// A 64-bit ELF file. |
222 | /// |
223 | /// See [`elf::ElfFile64`]. |
224 | #[cfg (feature = "elf" )] |
225 | Elf64, |
226 | /// A 32-bit Mach-O file. |
227 | /// |
228 | /// See [`macho::MachOFile32`]. |
229 | #[cfg (feature = "macho" )] |
230 | MachO32, |
231 | /// A 64-bit Mach-O file. |
232 | /// |
233 | /// See [`macho::MachOFile64`]. |
234 | #[cfg (feature = "macho" )] |
235 | MachO64, |
236 | /// A 32-bit Mach-O fat binary. |
237 | /// |
238 | /// See [`macho::FatHeader::parse_arch32`]. |
239 | #[cfg (feature = "macho" )] |
240 | MachOFat32, |
241 | /// A 64-bit Mach-O fat binary. |
242 | /// |
243 | /// See [`macho::FatHeader::parse_arch64`]. |
244 | #[cfg (feature = "macho" )] |
245 | MachOFat64, |
246 | /// A 32-bit PE file. |
247 | /// |
248 | /// See [`pe::PeFile32`]. |
249 | #[cfg (feature = "pe" )] |
250 | Pe32, |
251 | /// A 64-bit PE file. |
252 | /// |
253 | /// See [`pe::PeFile64`]. |
254 | #[cfg (feature = "pe" )] |
255 | Pe64, |
256 | /// A Wasm file. |
257 | /// |
258 | /// See [`wasm::WasmFile`]. |
259 | #[cfg (feature = "wasm" )] |
260 | Wasm, |
261 | /// A 32-bit XCOFF file. |
262 | /// |
263 | /// See [`xcoff::XcoffFile32`]. |
264 | #[cfg (feature = "xcoff" )] |
265 | Xcoff32, |
266 | /// A 64-bit XCOFF file. |
267 | /// |
268 | /// See [`xcoff::XcoffFile64`]. |
269 | #[cfg (feature = "xcoff" )] |
270 | Xcoff64, |
271 | } |
272 | |
273 | impl FileKind { |
274 | /// Determine a file kind by parsing the start of the file. |
275 | pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> { |
276 | Self::parse_at(data, 0) |
277 | } |
278 | |
279 | /// Determine a file kind by parsing at the given offset. |
280 | pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> { |
281 | let magic = data |
282 | .read_bytes_at(offset, 16) |
283 | .read_error("Could not read file magic" )?; |
284 | if magic.len() < 16 { |
285 | return Err(Error("File too short" )); |
286 | } |
287 | |
288 | let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] { |
289 | #[cfg (feature = "archive" )] |
290 | [b'!' , b'<' , b'a' , b'r' , b'c' , b'h' , b'>' , b' \n' ] => FileKind::Archive, |
291 | #[cfg (feature = "macho" )] |
292 | [b'd' , b'y' , b'l' , b'd' , b'_' , b'v' , b'1' , b' ' ] => FileKind::DyldCache, |
293 | #[cfg (feature = "elf" )] |
294 | [0x7f, b'E' , b'L' , b'F' , 1, ..] => FileKind::Elf32, |
295 | #[cfg (feature = "elf" )] |
296 | [0x7f, b'E' , b'L' , b'F' , 2, ..] => FileKind::Elf64, |
297 | #[cfg (feature = "macho" )] |
298 | [0xfe, 0xed, 0xfa, 0xce, ..] |
299 | | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32, |
300 | #[cfg (feature = "macho" )] |
301 | | [0xfe, 0xed, 0xfa, 0xcf, ..] |
302 | | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64, |
303 | #[cfg (feature = "macho" )] |
304 | [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32, |
305 | #[cfg (feature = "macho" )] |
306 | [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64, |
307 | #[cfg (feature = "wasm" )] |
308 | [0x00, b'a' , b's' , b'm' , ..] => FileKind::Wasm, |
309 | #[cfg (feature = "pe" )] |
310 | [b'M' , b'Z' , ..] if offset == 0 => { |
311 | // offset == 0 restriction is because optional_header_magic only looks at offset 0 |
312 | match pe::optional_header_magic(data) { |
313 | Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => { |
314 | FileKind::Pe32 |
315 | } |
316 | Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => { |
317 | FileKind::Pe64 |
318 | } |
319 | _ => return Err(Error("Unknown MS-DOS file" )), |
320 | } |
321 | } |
322 | // TODO: more COFF machines |
323 | #[cfg (feature = "coff" )] |
324 | // COFF arm |
325 | [0xc4, 0x01, ..] |
326 | // COFF arm64 |
327 | | [0x64, 0xaa, ..] |
328 | // COFF arm64ec |
329 | | [0x41, 0xa6, ..] |
330 | // COFF x86 |
331 | | [0x4c, 0x01, ..] |
332 | // COFF x86-64 |
333 | | [0x64, 0x86, ..] => FileKind::Coff, |
334 | #[cfg (feature = "coff" )] |
335 | [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport, |
336 | #[cfg (feature = "coff" )] |
337 | [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => { |
338 | // offset == 0 restriction is because anon_object_class_id only looks at offset 0 |
339 | match coff::anon_object_class_id(data) { |
340 | Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig, |
341 | _ => return Err(Error("Unknown anon object file" )), |
342 | } |
343 | } |
344 | #[cfg (feature = "xcoff" )] |
345 | [0x01, 0xdf, ..] => FileKind::Xcoff32, |
346 | #[cfg (feature = "xcoff" )] |
347 | [0x01, 0xf7, ..] => FileKind::Xcoff64, |
348 | _ => return Err(Error("Unknown file magic" )), |
349 | }; |
350 | Ok(kind) |
351 | } |
352 | } |
353 | |
354 | /// An object kind. |
355 | /// |
356 | /// Returned by [`Object::kind`]. |
357 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
358 | #[non_exhaustive ] |
359 | pub enum ObjectKind { |
360 | /// The object kind is unknown. |
361 | Unknown, |
362 | /// Relocatable object. |
363 | Relocatable, |
364 | /// Executable. |
365 | Executable, |
366 | /// Dynamic shared object. |
367 | Dynamic, |
368 | /// Core. |
369 | Core, |
370 | } |
371 | |
372 | /// The index used to identify a section in a file. |
373 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
374 | pub struct SectionIndex(pub usize); |
375 | |
376 | /// The index used to identify a symbol in a symbol table. |
377 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
378 | pub struct SymbolIndex(pub usize); |
379 | |
380 | /// The section where an [`ObjectSymbol`] is defined. |
381 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
382 | #[non_exhaustive ] |
383 | pub enum SymbolSection { |
384 | /// The section is unknown. |
385 | Unknown, |
386 | /// The section is not applicable for this symbol (such as file symbols). |
387 | None, |
388 | /// The symbol is undefined. |
389 | Undefined, |
390 | /// The symbol has an absolute value. |
391 | Absolute, |
392 | /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. |
393 | Common, |
394 | /// The symbol is defined in the given section. |
395 | Section(SectionIndex), |
396 | } |
397 | |
398 | impl SymbolSection { |
399 | /// Returns the section index for the section where the symbol is defined. |
400 | /// |
401 | /// May return `None` if the symbol is not defined in a section. |
402 | #[inline ] |
403 | pub fn index(self) -> Option<SectionIndex> { |
404 | if let SymbolSection::Section(index: SectionIndex) = self { |
405 | Some(index) |
406 | } else { |
407 | None |
408 | } |
409 | } |
410 | } |
411 | |
412 | /// An entry in a [`SymbolMap`]. |
413 | pub trait SymbolMapEntry { |
414 | /// The symbol address. |
415 | fn address(&self) -> u64; |
416 | } |
417 | |
418 | /// A map from addresses to symbol information. |
419 | /// |
420 | /// The symbol information depends on the chosen entry type, such as [`SymbolMapName`]. |
421 | /// |
422 | /// Returned by [`Object::symbol_map`]. |
423 | #[derive (Debug, Default, Clone)] |
424 | pub struct SymbolMap<T: SymbolMapEntry> { |
425 | symbols: Vec<T>, |
426 | } |
427 | |
428 | impl<T: SymbolMapEntry> SymbolMap<T> { |
429 | /// Construct a new symbol map. |
430 | /// |
431 | /// This function will sort the symbols by address. |
432 | pub fn new(mut symbols: Vec<T>) -> Self { |
433 | symbols.sort_by_key(|s| s.address()); |
434 | SymbolMap { symbols } |
435 | } |
436 | |
437 | /// Get the symbol before the given address. |
438 | pub fn get(&self, address: u64) -> Option<&T> { |
439 | let index = match self |
440 | .symbols |
441 | .binary_search_by_key(&address, |symbol| symbol.address()) |
442 | { |
443 | Ok(index) => index, |
444 | Err(index) => index.checked_sub(1)?, |
445 | }; |
446 | self.symbols.get(index) |
447 | } |
448 | |
449 | /// Get all symbols in the map. |
450 | #[inline ] |
451 | pub fn symbols(&self) -> &[T] { |
452 | &self.symbols |
453 | } |
454 | } |
455 | |
456 | /// The type used for entries in a [`SymbolMap`] that maps from addresses to names. |
457 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
458 | pub struct SymbolMapName<'data> { |
459 | address: u64, |
460 | name: &'data str, |
461 | } |
462 | |
463 | impl<'data> SymbolMapName<'data> { |
464 | /// Construct a `SymbolMapName`. |
465 | pub fn new(address: u64, name: &'data str) -> Self { |
466 | SymbolMapName { address, name } |
467 | } |
468 | |
469 | /// The symbol address. |
470 | #[inline ] |
471 | pub fn address(&self) -> u64 { |
472 | self.address |
473 | } |
474 | |
475 | /// The symbol name. |
476 | #[inline ] |
477 | pub fn name(&self) -> &'data str { |
478 | self.name |
479 | } |
480 | } |
481 | |
482 | impl<'data> SymbolMapEntry for SymbolMapName<'data> { |
483 | #[inline ] |
484 | fn address(&self) -> u64 { |
485 | self.address |
486 | } |
487 | } |
488 | |
489 | /// A map from addresses to symbol names and object files. |
490 | /// |
491 | /// This is derived from STAB entries in Mach-O files. |
492 | /// |
493 | /// Returned by [`Object::object_map`]. |
494 | #[derive (Debug, Default, Clone)] |
495 | pub struct ObjectMap<'data> { |
496 | symbols: SymbolMap<ObjectMapEntry<'data>>, |
497 | objects: Vec<&'data [u8]>, |
498 | } |
499 | |
500 | impl<'data> ObjectMap<'data> { |
501 | /// Get the entry containing the given address. |
502 | pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> { |
503 | self.symbols |
504 | .get(address) |
505 | .filter(|entry: &&ObjectMapEntry<'_>| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size) |
506 | } |
507 | |
508 | /// Get all symbols in the map. |
509 | #[inline ] |
510 | pub fn symbols(&self) -> &[ObjectMapEntry<'data>] { |
511 | self.symbols.symbols() |
512 | } |
513 | |
514 | /// Get all objects in the map. |
515 | #[inline ] |
516 | pub fn objects(&self) -> &[&'data [u8]] { |
517 | &self.objects |
518 | } |
519 | } |
520 | |
521 | /// An [`ObjectMap`] entry. |
522 | #[derive (Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] |
523 | pub struct ObjectMapEntry<'data> { |
524 | address: u64, |
525 | size: u64, |
526 | name: &'data [u8], |
527 | object: usize, |
528 | } |
529 | |
530 | impl<'data> ObjectMapEntry<'data> { |
531 | /// Get the symbol address. |
532 | #[inline ] |
533 | pub fn address(&self) -> u64 { |
534 | self.address |
535 | } |
536 | |
537 | /// Get the symbol size. |
538 | /// |
539 | /// This may be 0 if the size is unknown. |
540 | #[inline ] |
541 | pub fn size(&self) -> u64 { |
542 | self.size |
543 | } |
544 | |
545 | /// Get the symbol name. |
546 | #[inline ] |
547 | pub fn name(&self) -> &'data [u8] { |
548 | self.name |
549 | } |
550 | |
551 | /// Get the index of the object file name. |
552 | #[inline ] |
553 | pub fn object_index(&self) -> usize { |
554 | self.object |
555 | } |
556 | |
557 | /// Get the object file name. |
558 | #[inline ] |
559 | pub fn object(&self, map: &ObjectMap<'data>) -> &'data [u8] { |
560 | map.objects[self.object] |
561 | } |
562 | } |
563 | |
564 | impl<'data> SymbolMapEntry for ObjectMapEntry<'data> { |
565 | #[inline ] |
566 | fn address(&self) -> u64 { |
567 | self.address |
568 | } |
569 | } |
570 | |
571 | /// An imported symbol. |
572 | /// |
573 | /// Returned by [`Object::imports`]. |
574 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
575 | pub struct Import<'data> { |
576 | library: ByteString<'data>, |
577 | // TODO: or ordinal |
578 | name: ByteString<'data>, |
579 | } |
580 | |
581 | impl<'data> Import<'data> { |
582 | /// The symbol name. |
583 | #[inline ] |
584 | pub fn name(&self) -> &'data [u8] { |
585 | self.name.0 |
586 | } |
587 | |
588 | /// The name of the library to import the symbol from. |
589 | #[inline ] |
590 | pub fn library(&self) -> &'data [u8] { |
591 | self.library.0 |
592 | } |
593 | } |
594 | |
595 | /// An exported symbol. |
596 | /// |
597 | /// Returned by [`Object::exports`]. |
598 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
599 | pub struct Export<'data> { |
600 | // TODO: and ordinal? |
601 | name: ByteString<'data>, |
602 | address: u64, |
603 | } |
604 | |
605 | impl<'data> Export<'data> { |
606 | /// The symbol name. |
607 | #[inline ] |
608 | pub fn name(&self) -> &'data [u8] { |
609 | self.name.0 |
610 | } |
611 | |
612 | /// The virtual address of the symbol. |
613 | #[inline ] |
614 | pub fn address(&self) -> u64 { |
615 | self.address |
616 | } |
617 | } |
618 | |
619 | /// PDB information from the debug directory in a PE file. |
620 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
621 | pub struct CodeView<'data> { |
622 | guid: [u8; 16], |
623 | path: ByteString<'data>, |
624 | age: u32, |
625 | } |
626 | |
627 | impl<'data> CodeView<'data> { |
628 | /// The path to the PDB as stored in CodeView. |
629 | #[inline ] |
630 | pub fn path(&self) -> &'data [u8] { |
631 | self.path.0 |
632 | } |
633 | |
634 | /// The age of the PDB. |
635 | #[inline ] |
636 | pub fn age(&self) -> u32 { |
637 | self.age |
638 | } |
639 | |
640 | /// The GUID of the PDB. |
641 | #[inline ] |
642 | pub fn guid(&self) -> [u8; 16] { |
643 | self.guid |
644 | } |
645 | } |
646 | |
647 | /// The target referenced by a [`Relocation`]. |
648 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
649 | #[non_exhaustive ] |
650 | pub enum RelocationTarget { |
651 | /// The target is a symbol. |
652 | Symbol(SymbolIndex), |
653 | /// The target is a section. |
654 | Section(SectionIndex), |
655 | /// The offset is an absolute address. |
656 | Absolute, |
657 | } |
658 | |
659 | /// A relocation entry. |
660 | /// |
661 | /// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`]. |
662 | #[derive (Debug)] |
663 | pub struct Relocation { |
664 | kind: RelocationKind, |
665 | encoding: RelocationEncoding, |
666 | size: u8, |
667 | target: RelocationTarget, |
668 | addend: i64, |
669 | implicit_addend: bool, |
670 | } |
671 | |
672 | impl Relocation { |
673 | /// The operation used to calculate the result of the relocation. |
674 | #[inline ] |
675 | pub fn kind(&self) -> RelocationKind { |
676 | self.kind |
677 | } |
678 | |
679 | /// Information about how the result of the relocation operation is encoded in the place. |
680 | #[inline ] |
681 | pub fn encoding(&self) -> RelocationEncoding { |
682 | self.encoding |
683 | } |
684 | |
685 | /// The size in bits of the place of the relocation. |
686 | /// |
687 | /// If 0, then the size is determined by the relocation kind. |
688 | #[inline ] |
689 | pub fn size(&self) -> u8 { |
690 | self.size |
691 | } |
692 | |
693 | /// The target of the relocation. |
694 | #[inline ] |
695 | pub fn target(&self) -> RelocationTarget { |
696 | self.target |
697 | } |
698 | |
699 | /// The addend to use in the relocation calculation. |
700 | #[inline ] |
701 | pub fn addend(&self) -> i64 { |
702 | self.addend |
703 | } |
704 | |
705 | /// Set the addend to use in the relocation calculation. |
706 | #[inline ] |
707 | pub fn set_addend(&mut self, addend: i64) { |
708 | self.addend = addend |
709 | } |
710 | |
711 | /// Returns true if there is an implicit addend stored in the data at the offset |
712 | /// to be relocated. |
713 | #[inline ] |
714 | pub fn has_implicit_addend(&self) -> bool { |
715 | self.implicit_addend |
716 | } |
717 | } |
718 | |
719 | /// A data compression format. |
720 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
721 | #[non_exhaustive ] |
722 | pub enum CompressionFormat { |
723 | /// The data is uncompressed. |
724 | None, |
725 | /// The data is compressed, but the compression format is unknown. |
726 | Unknown, |
727 | /// ZLIB/DEFLATE. |
728 | /// |
729 | /// Used for ELF compression and GNU compressed debug information. |
730 | Zlib, |
731 | /// Zstandard. |
732 | /// |
733 | /// Used for ELF compression. |
734 | Zstandard, |
735 | } |
736 | |
737 | /// A range in a file that may be compressed. |
738 | /// |
739 | /// Returned by [`ObjectSection::compressed_file_range`]. |
740 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
741 | pub struct CompressedFileRange { |
742 | /// The data compression format. |
743 | pub format: CompressionFormat, |
744 | /// The file offset of the compressed data. |
745 | pub offset: u64, |
746 | /// The compressed data size. |
747 | pub compressed_size: u64, |
748 | /// The uncompressed data size. |
749 | pub uncompressed_size: u64, |
750 | } |
751 | |
752 | impl CompressedFileRange { |
753 | /// Data that is uncompressed. |
754 | #[inline ] |
755 | pub fn none(range: Option<(u64, u64)>) -> Self { |
756 | if let Some((offset, size)) = range { |
757 | CompressedFileRange { |
758 | format: CompressionFormat::None, |
759 | offset, |
760 | compressed_size: size, |
761 | uncompressed_size: size, |
762 | } |
763 | } else { |
764 | CompressedFileRange { |
765 | format: CompressionFormat::None, |
766 | offset: 0, |
767 | compressed_size: 0, |
768 | uncompressed_size: 0, |
769 | } |
770 | } |
771 | } |
772 | |
773 | /// Convert to [`CompressedData`] by reading from the file. |
774 | pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> { |
775 | let data = file |
776 | .read_bytes_at(self.offset, self.compressed_size) |
777 | .read_error("Invalid compressed data size or offset" )?; |
778 | Ok(CompressedData { |
779 | format: self.format, |
780 | data, |
781 | uncompressed_size: self.uncompressed_size, |
782 | }) |
783 | } |
784 | } |
785 | |
786 | /// Data that may be compressed. |
787 | /// |
788 | /// Returned by [`ObjectSection::compressed_data`]. |
789 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
790 | pub struct CompressedData<'data> { |
791 | /// The data compression format. |
792 | pub format: CompressionFormat, |
793 | /// The compressed data. |
794 | pub data: &'data [u8], |
795 | /// The uncompressed data size. |
796 | pub uncompressed_size: u64, |
797 | } |
798 | |
799 | impl<'data> CompressedData<'data> { |
800 | /// Data that is uncompressed. |
801 | #[inline ] |
802 | pub fn none(data: &'data [u8]) -> Self { |
803 | CompressedData { |
804 | format: CompressionFormat::None, |
805 | data, |
806 | uncompressed_size: data.len() as u64, |
807 | } |
808 | } |
809 | |
810 | /// Return the uncompressed data. |
811 | /// |
812 | /// Returns an error for invalid data or unsupported compression. |
813 | /// This includes if the data is compressed but the `compression` feature |
814 | /// for this crate is disabled. |
815 | pub fn decompress(self) -> Result<Cow<'data, [u8]>> { |
816 | match self.format { |
817 | CompressionFormat::None => Ok(Cow::Borrowed(self.data)), |
818 | #[cfg (feature = "compression" )] |
819 | CompressionFormat::Zlib => { |
820 | use core::convert::TryInto; |
821 | let size = self |
822 | .uncompressed_size |
823 | .try_into() |
824 | .ok() |
825 | .read_error("Uncompressed data size is too large." )?; |
826 | let mut decompressed = Vec::with_capacity(size); |
827 | let mut decompress = flate2::Decompress::new(true); |
828 | decompress |
829 | .decompress_vec( |
830 | self.data, |
831 | &mut decompressed, |
832 | flate2::FlushDecompress::Finish, |
833 | ) |
834 | .ok() |
835 | .read_error("Invalid zlib compressed data" )?; |
836 | Ok(Cow::Owned(decompressed)) |
837 | } |
838 | #[cfg (feature = "compression" )] |
839 | CompressionFormat::Zstandard => { |
840 | use core::convert::TryInto; |
841 | use std::io::Read; |
842 | let size = self |
843 | .uncompressed_size |
844 | .try_into() |
845 | .ok() |
846 | .read_error("Uncompressed data size is too large." )?; |
847 | let mut decompressed = Vec::with_capacity(size); |
848 | let mut decoder = ruzstd::StreamingDecoder::new(self.data) |
849 | .ok() |
850 | .read_error("Invalid zstd compressed data" )?; |
851 | decoder |
852 | .read_to_end(&mut decompressed) |
853 | .ok() |
854 | .read_error("Invalid zstd compressed data" )?; |
855 | Ok(Cow::Owned(decompressed)) |
856 | } |
857 | _ => Err(Error("Unsupported compressed data." )), |
858 | } |
859 | } |
860 | } |
861 | |