1use 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#[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
130pub 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#[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#[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#[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#[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#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
185pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>;
186
187#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
189pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>;
190
191#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
193pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>;
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
197#[non_exhaustive]
198pub enum FileKind {
199 #[cfg(feature = "archive")]
203 Archive,
204 #[cfg(feature = "coff")]
208 Coff,
209 #[cfg(feature = "coff")]
215 CoffBig,
216 #[cfg(feature = "coff")]
220 CoffImport,
221 #[cfg(feature = "macho")]
225 DyldCache,
226 #[cfg(feature = "elf")]
230 Elf32,
231 #[cfg(feature = "elf")]
235 Elf64,
236 #[cfg(feature = "macho")]
240 MachO32,
241 #[cfg(feature = "macho")]
245 MachO64,
246 #[cfg(feature = "macho")]
250 MachOFat32,
251 #[cfg(feature = "macho")]
255 MachOFat64,
256 #[cfg(feature = "pe")]
260 Pe32,
261 #[cfg(feature = "pe")]
265 Pe64,
266 #[cfg(feature = "wasm")]
270 Wasm,
271 #[cfg(feature = "xcoff")]
275 Xcoff32,
276 #[cfg(feature = "xcoff")]
280 Xcoff64,
281}
282
283impl FileKind {
284 pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
286 Self::parse_at(data, 0)
287 }
288
289 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 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 #[cfg(feature = "coff")]
335 [0xc4, 0x01, ..]
337 | [0x64, 0xaa, ..]
339 | [0x41, 0xa6, ..]
341 | [0x4c, 0x01, ..]
343 | [0x64, 0x86, ..] => FileKind::Coff,
345 #[cfg(feature = "coff")]
346 [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
347 #[cfg(feature = "coff")]
348 [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
349 match coff::anon_object_class_id(data) {
351 Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
352 _ => return Err(Error("Unknown anon object file")),
353 }
354 }
355 #[cfg(feature = "xcoff")]
356 [0x01, 0xdf, ..] => FileKind::Xcoff32,
357 #[cfg(feature = "xcoff")]
358 [0x01, 0xf7, ..] => FileKind::Xcoff64,
359 _ => return Err(Error("Unknown file magic")),
360 };
361 Ok(kind)
362 }
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
369#[non_exhaustive]
370pub enum ObjectKind {
371 Unknown,
373 Relocatable,
375 Executable,
377 Dynamic,
379 Core,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
385pub struct SectionIndex(pub usize);
386
387impl fmt::Display for SectionIndex {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 self.0.fmt(f)
390 }
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
395pub struct SymbolIndex(pub usize);
396
397impl fmt::Display for SymbolIndex {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 self.0.fmt(f)
400 }
401}
402
403#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
405#[non_exhaustive]
406pub enum SymbolSection {
407 Unknown,
409 None,
411 Undefined,
413 Absolute,
415 Common,
417 Section(SectionIndex),
419}
420
421impl SymbolSection {
422 #[inline]
426 pub fn index(self) -> Option<SectionIndex> {
427 if let SymbolSection::Section(index) = self {
428 Some(index)
429 } else {
430 None
431 }
432 }
433}
434
435pub trait SymbolMapEntry {
437 fn address(&self) -> u64;
439}
440
441#[derive(Debug, Default, Clone)]
447pub struct SymbolMap<T: SymbolMapEntry> {
448 symbols: Vec<T>,
449}
450
451impl<T: SymbolMapEntry> SymbolMap<T> {
452 pub fn new(mut symbols: Vec<T>) -> Self {
456 symbols.sort_by_key(|s| s.address());
457 SymbolMap { symbols }
458 }
459
460 pub fn get(&self, address: u64) -> Option<&T> {
462 let index = match self
463 .symbols
464 .binary_search_by_key(&address, |symbol| symbol.address())
465 {
466 Ok(index) => index,
467 Err(index) => index.checked_sub(1)?,
468 };
469 self.symbols.get(index)
470 }
471
472 #[inline]
474 pub fn symbols(&self) -> &[T] {
475 &self.symbols
476 }
477}
478
479#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
481pub struct SymbolMapName<'data> {
482 address: u64,
483 name: &'data str,
484}
485
486impl<'data> SymbolMapName<'data> {
487 pub fn new(address: u64, name: &'data str) -> Self {
489 SymbolMapName { address, name }
490 }
491
492 #[inline]
494 pub fn address(&self) -> u64 {
495 self.address
496 }
497
498 #[inline]
500 pub fn name(&self) -> &'data str {
501 self.name
502 }
503}
504
505impl<'data> SymbolMapEntry for SymbolMapName<'data> {
506 #[inline]
507 fn address(&self) -> u64 {
508 self.address
509 }
510}
511
512#[derive(Debug, Default, Clone)]
518pub struct ObjectMap<'data> {
519 symbols: SymbolMap<ObjectMapEntry<'data>>,
520 objects: Vec<ObjectMapFile<'data>>,
521}
522
523impl<'data> ObjectMap<'data> {
524 pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
526 self.symbols
527 .get(address)
528 .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
529 }
530
531 #[inline]
533 pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
534 self.symbols.symbols()
535 }
536
537 #[inline]
539 pub fn objects(&self) -> &[ObjectMapFile<'data>] {
540 &self.objects
541 }
542}
543
544#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
546pub struct ObjectMapEntry<'data> {
547 address: u64,
548 size: u64,
549 name: &'data [u8],
550 object: usize,
551}
552
553impl<'data> ObjectMapEntry<'data> {
554 #[inline]
556 pub fn address(&self) -> u64 {
557 self.address
558 }
559
560 #[inline]
564 pub fn size(&self) -> u64 {
565 self.size
566 }
567
568 #[inline]
570 pub fn name(&self) -> &'data [u8] {
571 self.name
572 }
573
574 #[inline]
576 pub fn object_index(&self) -> usize {
577 self.object
578 }
579
580 #[inline]
582 pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
583 &map.objects[self.object]
584 }
585}
586
587impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
588 #[inline]
589 fn address(&self) -> u64 {
590 self.address
591 }
592}
593
594#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
596pub struct ObjectMapFile<'data> {
597 path: &'data [u8],
598 member: Option<&'data [u8]>,
599}
600
601impl<'data> ObjectMapFile<'data> {
602 #[cfg(feature = "macho")]
603 fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
604 ObjectMapFile { path, member }
605 }
606
607 #[inline]
609 pub fn path(&self) -> &'data [u8] {
610 self.path
611 }
612
613 #[inline]
615 pub fn member(&self) -> Option<&'data [u8]> {
616 self.member
617 }
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq)]
624pub struct Import<'data> {
625 library: ByteString<'data>,
626 name: ByteString<'data>,
628}
629
630impl<'data> Import<'data> {
631 #[inline]
633 pub fn name(&self) -> &'data [u8] {
634 self.name.0
635 }
636
637 #[inline]
639 pub fn library(&self) -> &'data [u8] {
640 self.library.0
641 }
642}
643
644#[derive(Debug, Clone, Copy, PartialEq, Eq)]
648pub struct Export<'data> {
649 name: ByteString<'data>,
651 address: u64,
652}
653
654impl<'data> Export<'data> {
655 #[inline]
657 pub fn name(&self) -> &'data [u8] {
658 self.name.0
659 }
660
661 #[inline]
663 pub fn address(&self) -> u64 {
664 self.address
665 }
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq)]
670pub struct CodeView<'data> {
671 guid: [u8; 16],
672 path: ByteString<'data>,
673 age: u32,
674}
675
676impl<'data> CodeView<'data> {
677 #[inline]
679 pub fn path(&self) -> &'data [u8] {
680 self.path.0
681 }
682
683 #[inline]
685 pub fn age(&self) -> u32 {
686 self.age
687 }
688
689 #[inline]
691 pub fn guid(&self) -> [u8; 16] {
692 self.guid
693 }
694}
695
696#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
698#[non_exhaustive]
699pub enum RelocationTarget {
700 Symbol(SymbolIndex),
702 Section(SectionIndex),
704 Absolute,
706}
707
708#[derive(Debug)]
712pub 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
722impl Relocation {
723 #[inline]
725 pub fn kind(&self) -> RelocationKind {
726 self.kind
727 }
728
729 #[inline]
731 pub fn encoding(&self) -> RelocationEncoding {
732 self.encoding
733 }
734
735 #[inline]
739 pub fn size(&self) -> u8 {
740 self.size
741 }
742
743 #[inline]
745 pub fn target(&self) -> RelocationTarget {
746 self.target
747 }
748
749 #[inline]
751 pub fn addend(&self) -> i64 {
752 self.addend
753 }
754
755 #[inline]
757 pub fn set_addend(&mut self, addend: i64) {
758 self.addend = addend;
759 }
760
761 #[inline]
764 pub fn has_implicit_addend(&self) -> bool {
765 self.implicit_addend
766 }
767
768 #[inline]
773 pub fn flags(&self) -> RelocationFlags {
774 self.flags
775 }
776}
777
778#[derive(Debug, Default)]
786pub struct RelocationMap(Map<u64, RelocationMapEntry>);
787
788impl RelocationMap {
789 pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
795 where
796 T: Object<'data>,
797 {
798 let mut map = RelocationMap(Map::new());
799 for (offset, relocation) in section.relocations() {
800 map.add(file, offset, relocation)?;
801 }
802 Ok(map)
803 }
804
805 pub fn add<'data: 'file, 'file, T>(
807 &mut self,
808 file: &'file T,
809 offset: u64,
810 relocation: Relocation,
811 ) -> Result<()>
812 where
813 T: Object<'data>,
814 {
815 let mut entry = RelocationMapEntry {
816 implicit_addend: relocation.has_implicit_addend(),
817 addend: relocation.addend() as u64,
818 };
819 match relocation.kind() {
820 RelocationKind::Absolute => match relocation.target() {
821 RelocationTarget::Symbol(symbol_idx) => {
822 let symbol = file
823 .symbol_by_index(symbol_idx)
824 .read_error("Relocation with invalid symbol")?;
825 entry.addend = symbol.address().wrapping_add(entry.addend);
826 }
827 RelocationTarget::Section(section_idx) => {
828 let section = file
829 .section_by_index(section_idx)
830 .read_error("Relocation with invalid section")?;
831 if section.kind() != SectionKind::Debug {
834 entry.addend = section.address().wrapping_add(entry.addend);
835 }
836 }
837 _ => {
838 return Err(Error("Unsupported relocation target"));
839 }
840 },
841 _ => {
842 return Err(Error("Unsupported relocation type"));
843 }
844 }
845 if self.0.insert(offset, entry).is_some() {
846 return Err(Error("Multiple relocations for offset"));
847 }
848 Ok(())
849 }
850
851 pub fn relocate(&self, offset: u64, value: u64) -> u64 {
853 if let Some(relocation) = self.0.get(&offset) {
854 if relocation.implicit_addend {
855 value.wrapping_add(relocation.addend)
857 } else {
858 relocation.addend
859 }
860 } else {
861 value
862 }
863 }
864}
865
866#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
867struct RelocationMapEntry {
868 implicit_addend: bool,
869 addend: u64,
870}
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
874#[non_exhaustive]
875pub enum CompressionFormat {
876 None,
878 Unknown,
880 Zlib,
884 Zstandard,
888}
889
890#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
894pub struct CompressedFileRange {
895 pub format: CompressionFormat,
897 pub offset: u64,
899 pub compressed_size: u64,
901 pub uncompressed_size: u64,
903}
904
905impl CompressedFileRange {
906 #[inline]
908 pub fn none(range: Option<(u64, u64)>) -> Self {
909 if let Some((offset, size)) = range {
910 CompressedFileRange {
911 format: CompressionFormat::None,
912 offset,
913 compressed_size: size,
914 uncompressed_size: size,
915 }
916 } else {
917 CompressedFileRange {
918 format: CompressionFormat::None,
919 offset: 0,
920 compressed_size: 0,
921 uncompressed_size: 0,
922 }
923 }
924 }
925
926 pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
928 let data = file
929 .read_bytes_at(self.offset, self.compressed_size)
930 .read_error("Invalid compressed data size or offset")?;
931 Ok(CompressedData {
932 format: self.format,
933 data,
934 uncompressed_size: self.uncompressed_size,
935 })
936 }
937}
938
939#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
943pub struct CompressedData<'data> {
944 pub format: CompressionFormat,
946 pub data: &'data [u8],
948 pub uncompressed_size: u64,
950}
951
952impl<'data> CompressedData<'data> {
953 #[inline]
955 pub fn none(data: &'data [u8]) -> Self {
956 CompressedData {
957 format: CompressionFormat::None,
958 data,
959 uncompressed_size: data.len() as u64,
960 }
961 }
962
963 pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
969 match self.format {
970 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 _ => Err(Error("Unsupported compressed data.")),
1035 }
1036 }
1037}