1use alloc::borrow::Cow;
45use core::{fmt, result};
46
47#[cfg(not(feature = "std"))]
48use alloc::collections::btree_map::BTreeMap as Map;
49#[cfg(feature = "std")]
50use std::collections::hash_map::HashMap as Map;
51
52pub use crate::common::*;
53
54mod read_ref;
55pub use read_ref::*;
56
57mod read_cache;
58pub use read_cache::*;
59
60mod symbol_map;
61pub use symbol_map::*;
62
63mod util;
64pub use util::*;
65
66#[cfg(any(feature = "elf", feature = "macho"))]
67mod gnu_compression;
68
69#[cfg(any(
70 feature = "coff",
71 feature = "elf",
72 feature = "macho",
73 feature = "pe",
74 feature = "wasm",
75 feature = "xcoff"
76))]
77mod any;
78#[cfg(any(
79 feature = "coff",
80 feature = "elf",
81 feature = "macho",
82 feature = "pe",
83 feature = "wasm",
84 feature = "xcoff"
85))]
86pub use any::*;
87
88#[cfg(feature = "archive")]
89pub mod archive;
90
91#[cfg(feature = "coff")]
92pub mod coff;
93
94#[cfg(feature = "elf")]
95pub mod elf;
96
97#[cfg(feature = "macho")]
98pub mod macho;
99
100#[cfg(feature = "pe")]
101pub mod pe;
102
103#[cfg(feature = "wasm")]
104pub mod wasm;
105
106#[cfg(feature = "xcoff")]
107pub mod xcoff;
108
109mod traits;
110pub use traits::*;
111
112mod private {
113 pub trait Sealed {}
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct Error(pub(crate) &'static str);
119
120impl fmt::Display for Error {
121 #[inline]
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.write_str(self.0)
124 }
125}
126
127#[cfg(feature = "std")]
128impl std::error::Error for Error {}
129#[cfg(all(not(feature = "std"), core_error))]
130impl core::error::Error for Error {}
131
132pub type Result<T> = result::Result<T, Error>;
134
135trait ReadError<T> {
136 fn read_error(self, error: &'static str) -> Result<T>;
137}
138
139impl<T> ReadError<T> for result::Result<T, ()> {
140 fn read_error(self, error: &'static str) -> Result<T> {
141 self.map_err(|()| Error(error))
142 }
143}
144
145impl<T> ReadError<T> for result::Result<T, Error> {
146 fn read_error(self, error: &'static str) -> Result<T> {
147 self.map_err(|_| Error(error))
148 }
149}
150
151impl<T> ReadError<T> for Option<T> {
152 fn read_error(self, error: &'static str) -> Result<T> {
153 self.ok_or(Error(error))
154 }
155}
156
157#[cfg(all(
159 unix,
160 not(target_vendor = "apple"),
161 not(target_os = "aix"),
162 feature = "elf"
163))]
164pub type NativeFile<'data, R = &'data [u8]> = elf::NativeElfFile<'data, R>;
165
166#[cfg(all(target_vendor = "apple", feature = "macho"))]
168pub type NativeFile<'data, R = &'data [u8]> = macho::NativeMachOFile<'data, R>;
169
170#[cfg(all(target_os = "windows", feature = "pe"))]
172pub type NativeFile<'data, R = &'data [u8]> = pe::NativePeFile<'data, R>;
173
174#[cfg(all(target_family = "wasm", feature = "wasm"))]
176pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>;
177
178#[cfg(all(target_os = "aix", feature = "xcoff"))]
180pub type NativeFile<'data, R = &'data [u8]> = xcoff::NativeXcoffFile<'data, R>;
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184#[non_exhaustive]
185pub enum FileKind {
186 #[cfg(feature = "archive")]
190 Archive,
191 #[cfg(feature = "coff")]
195 Coff,
196 #[cfg(feature = "coff")]
202 CoffBig,
203 #[cfg(feature = "coff")]
207 CoffImport,
208 #[cfg(feature = "macho")]
212 DyldCache,
213 #[cfg(feature = "elf")]
217 Elf32,
218 #[cfg(feature = "elf")]
222 Elf64,
223 #[cfg(feature = "macho")]
227 MachO32,
228 #[cfg(feature = "macho")]
232 MachO64,
233 #[cfg(feature = "macho")]
237 MachOFat32,
238 #[cfg(feature = "macho")]
242 MachOFat64,
243 #[cfg(feature = "pe")]
247 Pe32,
248 #[cfg(feature = "pe")]
252 Pe64,
253 #[cfg(feature = "wasm")]
257 Wasm,
258 #[cfg(feature = "xcoff")]
262 Xcoff32,
263 #[cfg(feature = "xcoff")]
267 Xcoff64,
268}
269
270impl FileKind {
271 pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
273 Self::parse_at(data, 0)
274 }
275
276 pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> {
278 let magic = data
279 .read_bytes_at(offset, 16)
280 .read_error("Could not read file magic")?;
281 if magic.len() < 16 {
282 return Err(Error("File too short"));
283 }
284
285 let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] {
286 #[cfg(feature = "archive")]
287 [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n']
288 | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive,
289 #[cfg(feature = "macho")]
290 [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache,
291 #[cfg(feature = "elf")]
292 [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
293 #[cfg(feature = "elf")]
294 [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
295 #[cfg(feature = "macho")]
296 [0xfe, 0xed, 0xfa, 0xce, ..]
297 | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
298 #[cfg(feature = "macho")]
299 | [0xfe, 0xed, 0xfa, 0xcf, ..]
300 | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
301 #[cfg(feature = "macho")]
302 [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
303 #[cfg(feature = "macho")]
304 [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64,
305 #[cfg(feature = "wasm")]
306 [0x00, b'a', b's', b'm', _, _, 0x00, 0x00] => FileKind::Wasm,
307 #[cfg(feature = "pe")]
308 [b'M', b'Z', ..] if offset == 0 => {
309 match pe::optional_header_magic(data) {
311 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
312 FileKind::Pe32
313 }
314 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
315 FileKind::Pe64
316 }
317 _ => return Err(Error("Unknown MS-DOS file")),
318 }
319 }
320 #[cfg(feature = "coff")]
322 [0xc4, 0x01, ..]
324 | [0x64, 0xaa, ..]
326 | [0x41, 0xa6, ..]
328 | [0xf0, 0x01, ..]
330 | [0xf1, 0x01, ..]
331 | [0xf2, 0x01, ..]
332 | [0x4c, 0x01, ..]
334 | [0x64, 0x86, ..] => FileKind::Coff,
336 #[cfg(feature = "coff")]
337 [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
338 #[cfg(feature = "coff")]
339 [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
340 match coff::anon_object_class_id(data) {
342 Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
343 _ => return Err(Error("Unknown anon object file")),
344 }
345 }
346 #[cfg(feature = "xcoff")]
347 [0x01, 0xdf, ..] => FileKind::Xcoff32,
348 #[cfg(feature = "xcoff")]
349 [0x01, 0xf7, ..] => FileKind::Xcoff64,
350 _ => return Err(Error("Unknown file magic")),
351 };
352 Ok(kind)
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
360#[non_exhaustive]
361pub enum ObjectKind {
362 Unknown,
364 Relocatable,
366 Executable,
368 Dynamic,
370 Core,
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
376pub struct SectionIndex(pub usize);
377
378impl fmt::Display for SectionIndex {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 self.0.fmt(f)
381 }
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
386pub struct SymbolIndex(pub usize);
387
388impl fmt::Display for SymbolIndex {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 self.0.fmt(f)
391 }
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
396#[non_exhaustive]
397pub enum SymbolSection {
398 Unknown,
400 None,
402 Undefined,
404 Absolute,
406 Common,
408 Section(SectionIndex),
410}
411
412impl SymbolSection {
413 #[inline]
417 pub fn index(self) -> Option<SectionIndex> {
418 if let SymbolSection::Section(index) = self {
419 Some(index)
420 } else {
421 None
422 }
423 }
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq)]
430pub struct Import<'data> {
431 library: ByteString<'data>,
432 name: ByteString<'data>,
434}
435
436impl<'data> Import<'data> {
437 #[inline]
439 pub fn name(&self) -> &'data [u8] {
440 self.name.0
441 }
442
443 #[inline]
445 pub fn library(&self) -> &'data [u8] {
446 self.library.0
447 }
448}
449
450#[derive(Debug, Clone, Copy, PartialEq, Eq)]
454pub struct Export<'data> {
455 name: ByteString<'data>,
457 address: u64,
458}
459
460impl<'data> Export<'data> {
461 #[inline]
463 pub fn name(&self) -> &'data [u8] {
464 self.name.0
465 }
466
467 #[inline]
469 pub fn address(&self) -> u64 {
470 self.address
471 }
472}
473
474#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub struct CodeView<'data> {
477 guid: [u8; 16],
478 path: ByteString<'data>,
479 age: u32,
480}
481
482impl<'data> CodeView<'data> {
483 #[inline]
485 pub fn path(&self) -> &'data [u8] {
486 self.path.0
487 }
488
489 #[inline]
491 pub fn age(&self) -> u32 {
492 self.age
493 }
494
495 #[inline]
497 pub fn guid(&self) -> [u8; 16] {
498 self.guid
499 }
500}
501
502#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
504#[non_exhaustive]
505pub enum RelocationTarget {
506 Symbol(SymbolIndex),
508 Section(SectionIndex),
510 Absolute,
512}
513
514pub struct Relocation {
518 kind: RelocationKind,
519 encoding: RelocationEncoding,
520 size: u8,
521 target: RelocationTarget,
522 subtractor: Option<SymbolIndex>,
523 addend: i64,
524 implicit_addend: bool,
525 flags: RelocationFlags,
526}
527
528impl fmt::Debug for Relocation {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 let mut s = f.debug_struct("Relocation");
531 s.field("kind", &self.kind)
532 .field("encoding", &self.encoding)
533 .field("size", &self.size)
534 .field("target", &self.target);
535 if let Some(subtractor) = self.subtractor {
536 s.field("subtractor", &subtractor);
537 }
538 s.field("addend", &self.addend)
539 .field("implicit_addend", &self.implicit_addend)
540 .field("flags", &self.flags)
541 .finish()
542 }
543}
544
545impl Relocation {
546 #[inline]
548 pub fn kind(&self) -> RelocationKind {
549 self.kind
550 }
551
552 #[inline]
554 pub fn encoding(&self) -> RelocationEncoding {
555 self.encoding
556 }
557
558 #[inline]
562 pub fn size(&self) -> u8 {
563 self.size
564 }
565
566 #[inline]
568 pub fn target(&self) -> RelocationTarget {
569 self.target
570 }
571
572 #[inline]
576 pub fn subtractor(&self) -> Option<SymbolIndex> {
577 self.subtractor
578 }
579
580 #[inline]
582 pub fn addend(&self) -> i64 {
583 self.addend
584 }
585
586 #[inline]
588 pub fn set_addend(&mut self, addend: i64) {
589 self.addend = addend;
590 }
591
592 #[inline]
595 pub fn has_implicit_addend(&self) -> bool {
596 self.implicit_addend
597 }
598
599 #[inline]
604 pub fn flags(&self) -> RelocationFlags {
605 self.flags
606 }
607}
608
609#[derive(Debug, Default)]
617pub struct RelocationMap(Map<u64, RelocationMapEntry>);
618
619impl RelocationMap {
620 pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
626 where
627 T: Object<'data>,
628 {
629 let mut map = RelocationMap(Map::new());
630 for (offset, relocation) in section.relocations() {
631 map.add(file, offset, relocation)?;
632 }
633 Ok(map)
634 }
635
636 pub fn add<'data: 'file, 'file, T>(
638 &mut self,
639 file: &'file T,
640 offset: u64,
641 relocation: Relocation,
642 ) -> Result<()>
643 where
644 T: Object<'data>,
645 {
646 let mut entry = RelocationMapEntry {
647 implicit_addend: relocation.has_implicit_addend(),
648 addend: relocation.addend() as u64,
649 };
650 match relocation.kind() {
651 RelocationKind::None => return Ok(()),
652 RelocationKind::Absolute => match relocation.target() {
653 RelocationTarget::Symbol(symbol_idx) => {
654 let symbol = file
655 .symbol_by_index(symbol_idx)
656 .read_error("Relocation with invalid symbol")?;
657 entry.addend = symbol.address().wrapping_add(entry.addend);
658 }
659 RelocationTarget::Section(section_idx) => {
660 let section = file
661 .section_by_index(section_idx)
662 .read_error("Relocation with invalid section")?;
663 if section.kind() != SectionKind::Debug {
666 entry.addend = section.address().wrapping_add(entry.addend);
667 }
668 }
669 _ => {
670 return Err(Error("Unsupported relocation target"));
671 }
672 },
673 _ => {
674 return Err(Error("Unsupported relocation type"));
675 }
676 }
677 if relocation.encoding() != RelocationEncoding::Generic {
678 return Err(Error("Unsupported relocation encoding"));
679 }
680 if self.0.insert(offset, entry).is_some() {
681 return Err(Error("Multiple relocations for offset"));
682 }
683 Ok(())
684 }
685
686 pub fn relocate(&self, offset: u64, value: u64) -> u64 {
688 if let Some(relocation) = self.0.get(&offset) {
689 if relocation.implicit_addend {
690 value.wrapping_add(relocation.addend)
692 } else {
693 relocation.addend
694 }
695 } else {
696 value
697 }
698 }
699}
700
701#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
702struct RelocationMapEntry {
703 implicit_addend: bool,
704 addend: u64,
705}
706
707#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
709#[non_exhaustive]
710pub enum CompressionFormat {
711 None,
713 Unknown,
715 Zlib,
719 Zstandard,
723}
724
725#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
729pub struct CompressedFileRange {
730 pub format: CompressionFormat,
732 pub offset: u64,
734 pub compressed_size: u64,
736 pub uncompressed_size: u64,
738}
739
740impl CompressedFileRange {
741 #[inline]
743 pub fn none(range: Option<(u64, u64)>) -> Self {
744 if let Some((offset, size)) = range {
745 CompressedFileRange {
746 format: CompressionFormat::None,
747 offset,
748 compressed_size: size,
749 uncompressed_size: size,
750 }
751 } else {
752 CompressedFileRange {
753 format: CompressionFormat::None,
754 offset: 0,
755 compressed_size: 0,
756 uncompressed_size: 0,
757 }
758 }
759 }
760
761 pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
763 let data = file
764 .read_bytes_at(self.offset, self.compressed_size)
765 .read_error("Invalid compressed data size or offset")?;
766 Ok(CompressedData {
767 format: self.format,
768 data,
769 uncompressed_size: self.uncompressed_size,
770 })
771 }
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
778pub struct CompressedData<'data> {
779 pub format: CompressionFormat,
781 pub data: &'data [u8],
783 pub uncompressed_size: u64,
785}
786
787impl<'data> CompressedData<'data> {
788 #[inline]
790 pub fn none(data: &'data [u8]) -> Self {
791 CompressedData {
792 format: CompressionFormat::None,
793 data,
794 uncompressed_size: data.len() as u64,
795 }
796 }
797
798 pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
804 match self.format {
805 CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
806 #[cfg(feature = "compression")]
807 CompressionFormat::Zlib | CompressionFormat::Zstandard => {
808 use alloc::vec::Vec;
809 use core::convert::TryInto;
810 use std::io::Read;
811 let size = self
812 .uncompressed_size
813 .try_into()
814 .ok()
815 .read_error("Uncompressed data size is too large.")?;
816 let mut decompressed = Vec::new();
817 decompressed
818 .try_reserve_exact(size)
819 .ok()
820 .read_error("Uncompressed data allocation failed")?;
821
822 match self.format {
823 CompressionFormat::Zlib => {
824 let mut decompress = flate2::Decompress::new(true);
825 decompress
826 .decompress_vec(
827 self.data,
828 &mut decompressed,
829 flate2::FlushDecompress::Finish,
830 )
831 .ok()
832 .read_error("Invalid zlib compressed data")?;
833 }
834 CompressionFormat::Zstandard => {
835 let mut input = self.data;
836 while !input.is_empty() {
837 let mut decoder = match ruzstd::decoding::StreamingDecoder::new(&mut input) {
838 Ok(decoder) => decoder,
839 Err(
840 ruzstd::decoding::errors::FrameDecoderError::ReadFrameHeaderError(
841 ruzstd::decoding::errors::ReadFrameHeaderError::SkipFrame {
842 length,
843 ..
844 },
845 ),
846 ) => {
847 input = input
848 .get(length as usize..)
849 .read_error("Invalid zstd compressed data")?;
850 continue;
851 }
852 x => x.ok().read_error("Invalid zstd compressed data")?,
853 };
854 decoder
855 .read_to_end(&mut decompressed)
856 .ok()
857 .read_error("Invalid zstd compressed data")?;
858 }
859 }
860 _ => unreachable!(),
861 }
862 if size != decompressed.len() {
863 return Err(Error(
864 "Uncompressed data size does not match compression header",
865 ));
866 }
867
868 Ok(Cow::Owned(decompressed))
869 }
870 _ => Err(Error("Unsupported compressed data.")),
871 }
872 }
873}