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 | [0xf0, 0x01, ..]
343 | [0xf1, 0x01, ..]
344 | [0xf2, 0x01, ..]
345 | [0x4c, 0x01, ..]
347 | [0x64, 0x86, ..] => FileKind::Coff,
349 #[cfg(feature = "coff")]
350 [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
351 #[cfg(feature = "coff")]
352 [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
353 match coff::anon_object_class_id(data) {
355 Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
356 _ => return Err(Error("Unknown anon object file")),
357 }
358 }
359 #[cfg(feature = "xcoff")]
360 [0x01, 0xdf, ..] => FileKind::Xcoff32,
361 #[cfg(feature = "xcoff")]
362 [0x01, 0xf7, ..] => FileKind::Xcoff64,
363 _ => return Err(Error("Unknown file magic")),
364 };
365 Ok(kind)
366 }
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
373#[non_exhaustive]
374pub enum ObjectKind {
375 Unknown,
377 Relocatable,
379 Executable,
381 Dynamic,
383 Core,
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
389pub struct SectionIndex(pub usize);
390
391impl fmt::Display for SectionIndex {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 self.0.fmt(f)
394 }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
399pub struct SymbolIndex(pub usize);
400
401impl fmt::Display for SymbolIndex {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 self.0.fmt(f)
404 }
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
409#[non_exhaustive]
410pub enum SymbolSection {
411 Unknown,
413 None,
415 Undefined,
417 Absolute,
419 Common,
421 Section(SectionIndex),
423}
424
425impl SymbolSection {
426 #[inline]
430 pub fn index(self) -> Option<SectionIndex> {
431 if let SymbolSection::Section(index) = self {
432 Some(index)
433 } else {
434 None
435 }
436 }
437}
438
439pub trait SymbolMapEntry {
441 fn address(&self) -> u64;
443}
444
445#[derive(Debug, Default, Clone)]
451pub struct SymbolMap<T: SymbolMapEntry> {
452 symbols: Vec<T>,
453}
454
455impl<T: SymbolMapEntry> SymbolMap<T> {
456 pub fn new(mut symbols: Vec<T>) -> Self {
460 symbols.sort_by_key(|s| s.address());
461 SymbolMap { symbols }
462 }
463
464 pub fn get(&self, address: u64) -> Option<&T> {
466 let index = match self
467 .symbols
468 .binary_search_by_key(&address, |symbol| symbol.address())
469 {
470 Ok(index) => index,
471 Err(index) => index.checked_sub(1)?,
472 };
473 self.symbols.get(index)
474 }
475
476 #[inline]
478 pub fn symbols(&self) -> &[T] {
479 &self.symbols
480 }
481}
482
483#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
485pub struct SymbolMapName<'data> {
486 address: u64,
487 name: &'data str,
488}
489
490impl<'data> SymbolMapName<'data> {
491 pub fn new(address: u64, name: &'data str) -> Self {
493 SymbolMapName { address, name }
494 }
495
496 #[inline]
498 pub fn address(&self) -> u64 {
499 self.address
500 }
501
502 #[inline]
504 pub fn name(&self) -> &'data str {
505 self.name
506 }
507}
508
509impl<'data> SymbolMapEntry for SymbolMapName<'data> {
510 #[inline]
511 fn address(&self) -> u64 {
512 self.address
513 }
514}
515
516#[derive(Debug, Default, Clone)]
522pub struct ObjectMap<'data> {
523 symbols: SymbolMap<ObjectMapEntry<'data>>,
524 objects: Vec<ObjectMapFile<'data>>,
525}
526
527impl<'data> ObjectMap<'data> {
528 pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
530 self.symbols
531 .get(address)
532 .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
533 }
534
535 #[inline]
537 pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
538 self.symbols.symbols()
539 }
540
541 #[inline]
543 pub fn objects(&self) -> &[ObjectMapFile<'data>] {
544 &self.objects
545 }
546}
547
548#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
550pub struct ObjectMapEntry<'data> {
551 address: u64,
552 size: u64,
553 name: &'data [u8],
554 object: usize,
555}
556
557impl<'data> ObjectMapEntry<'data> {
558 #[inline]
560 pub fn address(&self) -> u64 {
561 self.address
562 }
563
564 #[inline]
568 pub fn size(&self) -> u64 {
569 self.size
570 }
571
572 #[inline]
574 pub fn name(&self) -> &'data [u8] {
575 self.name
576 }
577
578 #[inline]
580 pub fn object_index(&self) -> usize {
581 self.object
582 }
583
584 #[inline]
586 pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
587 &map.objects[self.object]
588 }
589}
590
591impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
592 #[inline]
593 fn address(&self) -> u64 {
594 self.address
595 }
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
600pub struct ObjectMapFile<'data> {
601 path: &'data [u8],
602 member: Option<&'data [u8]>,
603}
604
605impl<'data> ObjectMapFile<'data> {
606 #[cfg(feature = "macho")]
607 fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
608 ObjectMapFile { path, member }
609 }
610
611 #[inline]
613 pub fn path(&self) -> &'data [u8] {
614 self.path
615 }
616
617 #[inline]
619 pub fn member(&self) -> Option<&'data [u8]> {
620 self.member
621 }
622}
623
624#[derive(Debug, Clone, Copy, PartialEq, Eq)]
628pub struct Import<'data> {
629 library: ByteString<'data>,
630 name: ByteString<'data>,
632}
633
634impl<'data> Import<'data> {
635 #[inline]
637 pub fn name(&self) -> &'data [u8] {
638 self.name.0
639 }
640
641 #[inline]
643 pub fn library(&self) -> &'data [u8] {
644 self.library.0
645 }
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652pub struct Export<'data> {
653 name: ByteString<'data>,
655 address: u64,
656}
657
658impl<'data> Export<'data> {
659 #[inline]
661 pub fn name(&self) -> &'data [u8] {
662 self.name.0
663 }
664
665 #[inline]
667 pub fn address(&self) -> u64 {
668 self.address
669 }
670}
671
672#[derive(Debug, Clone, Copy, PartialEq, Eq)]
674pub struct CodeView<'data> {
675 guid: [u8; 16],
676 path: ByteString<'data>,
677 age: u32,
678}
679
680impl<'data> CodeView<'data> {
681 #[inline]
683 pub fn path(&self) -> &'data [u8] {
684 self.path.0
685 }
686
687 #[inline]
689 pub fn age(&self) -> u32 {
690 self.age
691 }
692
693 #[inline]
695 pub fn guid(&self) -> [u8; 16] {
696 self.guid
697 }
698}
699
700#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
702#[non_exhaustive]
703pub enum RelocationTarget {
704 Symbol(SymbolIndex),
706 Section(SectionIndex),
708 Absolute,
710}
711
712#[derive(Debug)]
716pub struct Relocation {
717 kind: RelocationKind,
718 encoding: RelocationEncoding,
719 size: u8,
720 target: RelocationTarget,
721 addend: i64,
722 implicit_addend: bool,
723 flags: RelocationFlags,
724}
725
726impl Relocation {
727 #[inline]
729 pub fn kind(&self) -> RelocationKind {
730 self.kind
731 }
732
733 #[inline]
735 pub fn encoding(&self) -> RelocationEncoding {
736 self.encoding
737 }
738
739 #[inline]
743 pub fn size(&self) -> u8 {
744 self.size
745 }
746
747 #[inline]
749 pub fn target(&self) -> RelocationTarget {
750 self.target
751 }
752
753 #[inline]
755 pub fn addend(&self) -> i64 {
756 self.addend
757 }
758
759 #[inline]
761 pub fn set_addend(&mut self, addend: i64) {
762 self.addend = addend;
763 }
764
765 #[inline]
768 pub fn has_implicit_addend(&self) -> bool {
769 self.implicit_addend
770 }
771
772 #[inline]
777 pub fn flags(&self) -> RelocationFlags {
778 self.flags
779 }
780}
781
782#[derive(Debug, Default)]
790pub struct RelocationMap(Map<u64, RelocationMapEntry>);
791
792impl RelocationMap {
793 pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
799 where
800 T: Object<'data>,
801 {
802 let mut map = RelocationMap(Map::new());
803 for (offset, relocation) in section.relocations() {
804 map.add(file, offset, relocation)?;
805 }
806 Ok(map)
807 }
808
809 pub fn add<'data: 'file, 'file, T>(
811 &mut self,
812 file: &'file T,
813 offset: u64,
814 relocation: Relocation,
815 ) -> Result<()>
816 where
817 T: Object<'data>,
818 {
819 let mut entry = RelocationMapEntry {
820 implicit_addend: relocation.has_implicit_addend(),
821 addend: relocation.addend() as u64,
822 };
823 match relocation.kind() {
824 RelocationKind::Absolute => match relocation.target() {
825 RelocationTarget::Symbol(symbol_idx) => {
826 let symbol = file
827 .symbol_by_index(symbol_idx)
828 .read_error("Relocation with invalid symbol")?;
829 entry.addend = symbol.address().wrapping_add(entry.addend);
830 }
831 RelocationTarget::Section(section_idx) => {
832 let section = file
833 .section_by_index(section_idx)
834 .read_error("Relocation with invalid section")?;
835 if section.kind() != SectionKind::Debug {
838 entry.addend = section.address().wrapping_add(entry.addend);
839 }
840 }
841 _ => {
842 return Err(Error("Unsupported relocation target"));
843 }
844 },
845 _ => {
846 return Err(Error("Unsupported relocation type"));
847 }
848 }
849 if self.0.insert(offset, entry).is_some() {
850 return Err(Error("Multiple relocations for offset"));
851 }
852 Ok(())
853 }
854
855 pub fn relocate(&self, offset: u64, value: u64) -> u64 {
857 if let Some(relocation) = self.0.get(&offset) {
858 if relocation.implicit_addend {
859 value.wrapping_add(relocation.addend)
861 } else {
862 relocation.addend
863 }
864 } else {
865 value
866 }
867 }
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
871struct RelocationMapEntry {
872 implicit_addend: bool,
873 addend: u64,
874}
875
876#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
878#[non_exhaustive]
879pub enum CompressionFormat {
880 None,
882 Unknown,
884 Zlib,
888 Zstandard,
892}
893
894#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
898pub struct CompressedFileRange {
899 pub format: CompressionFormat,
901 pub offset: u64,
903 pub compressed_size: u64,
905 pub uncompressed_size: u64,
907}
908
909impl CompressedFileRange {
910 #[inline]
912 pub fn none(range: Option<(u64, u64)>) -> Self {
913 if let Some((offset, size)) = range {
914 CompressedFileRange {
915 format: CompressionFormat::None,
916 offset,
917 compressed_size: size,
918 uncompressed_size: size,
919 }
920 } else {
921 CompressedFileRange {
922 format: CompressionFormat::None,
923 offset: 0,
924 compressed_size: 0,
925 uncompressed_size: 0,
926 }
927 }
928 }
929
930 pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
932 let data = file
933 .read_bytes_at(self.offset, self.compressed_size)
934 .read_error("Invalid compressed data size or offset")?;
935 Ok(CompressedData {
936 format: self.format,
937 data,
938 uncompressed_size: self.uncompressed_size,
939 })
940 }
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
947pub struct CompressedData<'data> {
948 pub format: CompressionFormat,
950 pub data: &'data [u8],
952 pub uncompressed_size: u64,
954}
955
956impl<'data> CompressedData<'data> {
957 #[inline]
959 pub fn none(data: &'data [u8]) -> Self {
960 CompressedData {
961 format: CompressionFormat::None,
962 data,
963 uncompressed_size: data.len() as u64,
964 }
965 }
966
967 pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
973 match self.format {
974 CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
975 #[cfg(feature = "compression")]
976 CompressionFormat::Zlib | CompressionFormat::Zstandard => {
977 use core::convert::TryInto;
978 use std::io::Read;
979 let size = self
980 .uncompressed_size
981 .try_into()
982 .ok()
983 .read_error("Uncompressed data size is too large.")?;
984 let mut decompressed = Vec::new();
985 decompressed
986 .try_reserve_exact(size)
987 .ok()
988 .read_error("Uncompressed data allocation failed")?;
989
990 match self.format {
991 CompressionFormat::Zlib => {
992 let mut decompress = flate2::Decompress::new(true);
993 decompress
994 .decompress_vec(
995 self.data,
996 &mut decompressed,
997 flate2::FlushDecompress::Finish,
998 )
999 .ok()
1000 .read_error("Invalid zlib compressed data")?;
1001 }
1002 CompressionFormat::Zstandard => {
1003 let mut input = self.data;
1004 while !input.is_empty() {
1005 let mut decoder = match ruzstd::decoding::StreamingDecoder::new(&mut input) {
1006 Ok(decoder) => decoder,
1007 Err(
1008 ruzstd::decoding::errors::FrameDecoderError::ReadFrameHeaderError(
1009 ruzstd::decoding::errors::ReadFrameHeaderError::SkipFrame {
1010 length,
1011 ..
1012 },
1013 ),
1014 ) => {
1015 input = input
1016 .get(length as usize..)
1017 .read_error("Invalid zstd compressed data")?;
1018 continue;
1019 }
1020 x => x.ok().read_error("Invalid zstd compressed data")?,
1021 };
1022 decoder
1023 .read_to_end(&mut decompressed)
1024 .ok()
1025 .read_error("Invalid zstd compressed data")?;
1026 }
1027 }
1028 _ => unreachable!(),
1029 }
1030 if size != decompressed.len() {
1031 return Err(Error(
1032 "Uncompressed data size does not match compression header",
1033 ));
1034 }
1035
1036 Ok(Cow::Owned(decompressed))
1037 }
1038 _ => Err(Error("Unsupported compressed data.")),
1039 }
1040 }
1041}