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
128pub type Result<T> = result::Result<T, Error>;
130
131trait ReadError<T> {
132 fn read_error(self, error: &'static str) -> Result<T>;
133}
134
135impl<T> ReadError<T> for result::Result<T, ()> {
136 fn read_error(self, error: &'static str) -> Result<T> {
137 self.map_err(|()| Error(error))
138 }
139}
140
141impl<T> ReadError<T> for result::Result<T, Error> {
142 fn read_error(self, error: &'static str) -> Result<T> {
143 self.map_err(|_| Error(error))
144 }
145}
146
147impl<T> ReadError<T> for Option<T> {
148 fn read_error(self, error: &'static str) -> Result<T> {
149 self.ok_or(Error(error))
150 }
151}
152
153#[cfg(all(
155 unix,
156 not(target_os = "macos"),
157 target_pointer_width = "32",
158 feature = "elf"
159))]
160pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::endian::Endianness, R>;
161
162#[cfg(all(
164 unix,
165 not(target_os = "macos"),
166 target_pointer_width = "64",
167 feature = "elf"
168))]
169pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::endian::Endianness, R>;
170
171#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
173pub type NativeFile<'data, R = &'data [u8]> =
174 macho::MachOFile32<'data, crate::endian::Endianness, R>;
175
176#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
178pub type NativeFile<'data, R = &'data [u8]> =
179 macho::MachOFile64<'data, crate::endian::Endianness, R>;
180
181#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
183pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>;
184
185#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
187pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>;
188
189#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
191pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>;
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
195#[non_exhaustive]
196pub enum FileKind {
197 #[cfg(feature = "archive")]
201 Archive,
202 #[cfg(feature = "coff")]
206 Coff,
207 #[cfg(feature = "coff")]
213 CoffBig,
214 #[cfg(feature = "coff")]
218 CoffImport,
219 #[cfg(feature = "macho")]
223 DyldCache,
224 #[cfg(feature = "elf")]
228 Elf32,
229 #[cfg(feature = "elf")]
233 Elf64,
234 #[cfg(feature = "macho")]
238 MachO32,
239 #[cfg(feature = "macho")]
243 MachO64,
244 #[cfg(feature = "macho")]
248 MachOFat32,
249 #[cfg(feature = "macho")]
253 MachOFat64,
254 #[cfg(feature = "pe")]
258 Pe32,
259 #[cfg(feature = "pe")]
263 Pe64,
264 #[cfg(feature = "wasm")]
268 Wasm,
269 #[cfg(feature = "xcoff")]
273 Xcoff32,
274 #[cfg(feature = "xcoff")]
278 Xcoff64,
279}
280
281impl FileKind {
282 pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
284 Self::parse_at(data, 0)
285 }
286
287 pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> {
289 let magic = data
290 .read_bytes_at(offset, 16)
291 .read_error("Could not read file magic")?;
292 if magic.len() < 16 {
293 return Err(Error("File too short"));
294 }
295
296 let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] {
297 #[cfg(feature = "archive")]
298 [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n']
299 | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive,
300 #[cfg(feature = "macho")]
301 [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache,
302 #[cfg(feature = "elf")]
303 [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
304 #[cfg(feature = "elf")]
305 [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
306 #[cfg(feature = "macho")]
307 [0xfe, 0xed, 0xfa, 0xce, ..]
308 | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
309 #[cfg(feature = "macho")]
310 | [0xfe, 0xed, 0xfa, 0xcf, ..]
311 | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
312 #[cfg(feature = "macho")]
313 [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
314 #[cfg(feature = "macho")]
315 [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64,
316 #[cfg(feature = "wasm")]
317 [0x00, b'a', b's', b'm', _, _, 0x00, 0x00] => FileKind::Wasm,
318 #[cfg(feature = "pe")]
319 [b'M', b'Z', ..] if offset == 0 => {
320 match pe::optional_header_magic(data) {
322 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
323 FileKind::Pe32
324 }
325 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
326 FileKind::Pe64
327 }
328 _ => return Err(Error("Unknown MS-DOS file")),
329 }
330 }
331 #[cfg(feature = "coff")]
333 [0xc4, 0x01, ..]
335 | [0x64, 0xaa, ..]
337 | [0x41, 0xa6, ..]
339 | [0x4c, 0x01, ..]
341 | [0x64, 0x86, ..] => FileKind::Coff,
343 #[cfg(feature = "coff")]
344 [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
345 #[cfg(feature = "coff")]
346 [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
347 match coff::anon_object_class_id(data) {
349 Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
350 _ => return Err(Error("Unknown anon object file")),
351 }
352 }
353 #[cfg(feature = "xcoff")]
354 [0x01, 0xdf, ..] => FileKind::Xcoff32,
355 #[cfg(feature = "xcoff")]
356 [0x01, 0xf7, ..] => FileKind::Xcoff64,
357 _ => return Err(Error("Unknown file magic")),
358 };
359 Ok(kind)
360 }
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
367#[non_exhaustive]
368pub enum ObjectKind {
369 Unknown,
371 Relocatable,
373 Executable,
375 Dynamic,
377 Core,
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
383pub struct SectionIndex(pub usize);
384
385impl fmt::Display for SectionIndex {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 self.0.fmt(f)
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
393pub struct SymbolIndex(pub usize);
394
395impl fmt::Display for SymbolIndex {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 self.0.fmt(f)
398 }
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
403#[non_exhaustive]
404pub enum SymbolSection {
405 Unknown,
407 None,
409 Undefined,
411 Absolute,
413 Common,
415 Section(SectionIndex),
417}
418
419impl SymbolSection {
420 #[inline]
424 pub fn index(self) -> Option<SectionIndex> {
425 if let SymbolSection::Section(index) = self {
426 Some(index)
427 } else {
428 None
429 }
430 }
431}
432
433pub trait SymbolMapEntry {
435 fn address(&self) -> u64;
437}
438
439#[derive(Debug, Default, Clone)]
445pub struct SymbolMap<T: SymbolMapEntry> {
446 symbols: Vec<T>,
447}
448
449impl<T: SymbolMapEntry> SymbolMap<T> {
450 pub fn new(mut symbols: Vec<T>) -> Self {
454 symbols.sort_by_key(|s| s.address());
455 SymbolMap { symbols }
456 }
457
458 pub fn get(&self, address: u64) -> Option<&T> {
460 let index = match self
461 .symbols
462 .binary_search_by_key(&address, |symbol| symbol.address())
463 {
464 Ok(index) => index,
465 Err(index) => index.checked_sub(1)?,
466 };
467 self.symbols.get(index)
468 }
469
470 #[inline]
472 pub fn symbols(&self) -> &[T] {
473 &self.symbols
474 }
475}
476
477#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
479pub struct SymbolMapName<'data> {
480 address: u64,
481 name: &'data str,
482}
483
484impl<'data> SymbolMapName<'data> {
485 pub fn new(address: u64, name: &'data str) -> Self {
487 SymbolMapName { address, name }
488 }
489
490 #[inline]
492 pub fn address(&self) -> u64 {
493 self.address
494 }
495
496 #[inline]
498 pub fn name(&self) -> &'data str {
499 self.name
500 }
501}
502
503impl<'data> SymbolMapEntry for SymbolMapName<'data> {
504 #[inline]
505 fn address(&self) -> u64 {
506 self.address
507 }
508}
509
510#[derive(Debug, Default, Clone)]
516pub struct ObjectMap<'data> {
517 symbols: SymbolMap<ObjectMapEntry<'data>>,
518 objects: Vec<ObjectMapFile<'data>>,
519}
520
521impl<'data> ObjectMap<'data> {
522 pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
524 self.symbols
525 .get(address)
526 .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
527 }
528
529 #[inline]
531 pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
532 self.symbols.symbols()
533 }
534
535 #[inline]
537 pub fn objects(&self) -> &[ObjectMapFile<'data>] {
538 &self.objects
539 }
540}
541
542#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
544pub struct ObjectMapEntry<'data> {
545 address: u64,
546 size: u64,
547 name: &'data [u8],
548 object: usize,
549}
550
551impl<'data> ObjectMapEntry<'data> {
552 #[inline]
554 pub fn address(&self) -> u64 {
555 self.address
556 }
557
558 #[inline]
562 pub fn size(&self) -> u64 {
563 self.size
564 }
565
566 #[inline]
568 pub fn name(&self) -> &'data [u8] {
569 self.name
570 }
571
572 #[inline]
574 pub fn object_index(&self) -> usize {
575 self.object
576 }
577
578 #[inline]
580 pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> {
581 &map.objects[self.object]
582 }
583}
584
585impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
586 #[inline]
587 fn address(&self) -> u64 {
588 self.address
589 }
590}
591
592#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
594pub struct ObjectMapFile<'data> {
595 path: &'data [u8],
596 member: Option<&'data [u8]>,
597}
598
599impl<'data> ObjectMapFile<'data> {
600 #[cfg(feature = "macho")]
601 fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self {
602 ObjectMapFile { path, member }
603 }
604
605 #[inline]
607 pub fn path(&self) -> &'data [u8] {
608 self.path
609 }
610
611 #[inline]
613 pub fn member(&self) -> Option<&'data [u8]> {
614 self.member
615 }
616}
617
618#[derive(Debug, Clone, Copy, PartialEq, Eq)]
622pub struct Import<'data> {
623 library: ByteString<'data>,
624 name: ByteString<'data>,
626}
627
628impl<'data> Import<'data> {
629 #[inline]
631 pub fn name(&self) -> &'data [u8] {
632 self.name.0
633 }
634
635 #[inline]
637 pub fn library(&self) -> &'data [u8] {
638 self.library.0
639 }
640}
641
642#[derive(Debug, Clone, Copy, PartialEq, Eq)]
646pub struct Export<'data> {
647 name: ByteString<'data>,
649 address: u64,
650}
651
652impl<'data> Export<'data> {
653 #[inline]
655 pub fn name(&self) -> &'data [u8] {
656 self.name.0
657 }
658
659 #[inline]
661 pub fn address(&self) -> u64 {
662 self.address
663 }
664}
665
666#[derive(Debug, Clone, Copy, PartialEq, Eq)]
668pub struct CodeView<'data> {
669 guid: [u8; 16],
670 path: ByteString<'data>,
671 age: u32,
672}
673
674impl<'data> CodeView<'data> {
675 #[inline]
677 pub fn path(&self) -> &'data [u8] {
678 self.path.0
679 }
680
681 #[inline]
683 pub fn age(&self) -> u32 {
684 self.age
685 }
686
687 #[inline]
689 pub fn guid(&self) -> [u8; 16] {
690 self.guid
691 }
692}
693
694#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
696#[non_exhaustive]
697pub enum RelocationTarget {
698 Symbol(SymbolIndex),
700 Section(SectionIndex),
702 Absolute,
704}
705
706#[derive(Debug)]
710pub struct Relocation {
711 kind: RelocationKind,
712 encoding: RelocationEncoding,
713 size: u8,
714 target: RelocationTarget,
715 addend: i64,
716 implicit_addend: bool,
717 flags: RelocationFlags,
718}
719
720impl Relocation {
721 #[inline]
723 pub fn kind(&self) -> RelocationKind {
724 self.kind
725 }
726
727 #[inline]
729 pub fn encoding(&self) -> RelocationEncoding {
730 self.encoding
731 }
732
733 #[inline]
737 pub fn size(&self) -> u8 {
738 self.size
739 }
740
741 #[inline]
743 pub fn target(&self) -> RelocationTarget {
744 self.target
745 }
746
747 #[inline]
749 pub fn addend(&self) -> i64 {
750 self.addend
751 }
752
753 #[inline]
755 pub fn set_addend(&mut self, addend: i64) {
756 self.addend = addend;
757 }
758
759 #[inline]
762 pub fn has_implicit_addend(&self) -> bool {
763 self.implicit_addend
764 }
765
766 #[inline]
771 pub fn flags(&self) -> RelocationFlags {
772 self.flags
773 }
774}
775
776#[derive(Debug, Default)]
784pub struct RelocationMap(Map<u64, RelocationMapEntry>);
785
786impl RelocationMap {
787 pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result<Self>
793 where
794 T: Object<'data>,
795 {
796 let mut map = RelocationMap(Map::new());
797 for (offset, relocation) in section.relocations() {
798 map.add(file, offset, relocation)?;
799 }
800 Ok(map)
801 }
802
803 pub fn add<'data: 'file, 'file, T>(
805 &mut self,
806 file: &'file T,
807 offset: u64,
808 relocation: Relocation,
809 ) -> Result<()>
810 where
811 T: Object<'data>,
812 {
813 let mut entry = RelocationMapEntry {
814 implicit_addend: relocation.has_implicit_addend(),
815 addend: relocation.addend() as u64,
816 };
817 match relocation.kind() {
818 RelocationKind::Absolute => match relocation.target() {
819 RelocationTarget::Symbol(symbol_idx) => {
820 let symbol = file
821 .symbol_by_index(symbol_idx)
822 .read_error("Relocation with invalid symbol")?;
823 entry.addend = symbol.address().wrapping_add(entry.addend);
824 }
825 RelocationTarget::Section(section_idx) => {
826 let section = file
827 .section_by_index(section_idx)
828 .read_error("Relocation with invalid section")?;
829 if section.kind() != SectionKind::Debug {
832 entry.addend = section.address().wrapping_add(entry.addend);
833 }
834 }
835 _ => {
836 return Err(Error("Unsupported relocation target"));
837 }
838 },
839 _ => {
840 return Err(Error("Unsupported relocation type"));
841 }
842 }
843 if self.0.insert(offset, entry).is_some() {
844 return Err(Error("Multiple relocations for offset"));
845 }
846 Ok(())
847 }
848
849 pub fn relocate(&self, offset: u64, value: u64) -> u64 {
851 if let Some(relocation) = self.0.get(&offset) {
852 if relocation.implicit_addend {
853 value.wrapping_add(relocation.addend)
855 } else {
856 relocation.addend
857 }
858 } else {
859 value
860 }
861 }
862}
863
864#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
865struct RelocationMapEntry {
866 implicit_addend: bool,
867 addend: u64,
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
872#[non_exhaustive]
873pub enum CompressionFormat {
874 None,
876 Unknown,
878 Zlib,
882 Zstandard,
886}
887
888#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
892pub struct CompressedFileRange {
893 pub format: CompressionFormat,
895 pub offset: u64,
897 pub compressed_size: u64,
899 pub uncompressed_size: u64,
901}
902
903impl CompressedFileRange {
904 #[inline]
906 pub fn none(range: Option<(u64, u64)>) -> Self {
907 if let Some((offset, size)) = range {
908 CompressedFileRange {
909 format: CompressionFormat::None,
910 offset,
911 compressed_size: size,
912 uncompressed_size: size,
913 }
914 } else {
915 CompressedFileRange {
916 format: CompressionFormat::None,
917 offset: 0,
918 compressed_size: 0,
919 uncompressed_size: 0,
920 }
921 }
922 }
923
924 pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
926 let data = file
927 .read_bytes_at(self.offset, self.compressed_size)
928 .read_error("Invalid compressed data size or offset")?;
929 Ok(CompressedData {
930 format: self.format,
931 data,
932 uncompressed_size: self.uncompressed_size,
933 })
934 }
935}
936
937#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
941pub struct CompressedData<'data> {
942 pub format: CompressionFormat,
944 pub data: &'data [u8],
946 pub uncompressed_size: u64,
948}
949
950impl<'data> CompressedData<'data> {
951 #[inline]
953 pub fn none(data: &'data [u8]) -> Self {
954 CompressedData {
955 format: CompressionFormat::None,
956 data,
957 uncompressed_size: data.len() as u64,
958 }
959 }
960
961 pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
967 match self.format {
968 CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
969 #[cfg(feature = "compression")]
970 CompressionFormat::Zlib | CompressionFormat::Zstandard => {
971 use core::convert::TryInto;
972 use std::io::Read;
973 let size = self
974 .uncompressed_size
975 .try_into()
976 .ok()
977 .read_error("Uncompressed data size is too large.")?;
978 let mut decompressed = Vec::new();
979 decompressed
980 .try_reserve_exact(size)
981 .ok()
982 .read_error("Uncompressed data allocation failed")?;
983
984 match self.format {
985 CompressionFormat::Zlib => {
986 let mut decompress = flate2::Decompress::new(true);
987 decompress
988 .decompress_vec(
989 self.data,
990 &mut decompressed,
991 flate2::FlushDecompress::Finish,
992 )
993 .ok()
994 .read_error("Invalid zlib compressed data")?;
995 }
996 CompressionFormat::Zstandard => {
997 let mut input = self.data;
998 while !input.is_empty() {
999 let mut decoder = match ruzstd::StreamingDecoder::new(&mut input) {
1000 Ok(decoder) => decoder,
1001 Err(
1002 ruzstd::frame_decoder::FrameDecoderError::ReadFrameHeaderError(
1003 ruzstd::frame::ReadFrameHeaderError::SkipFrame {
1004 length,
1005 ..
1006 },
1007 ),
1008 ) => {
1009 input = input
1010 .get(length as usize..)
1011 .read_error("Invalid zstd compressed data")?;
1012 continue;
1013 }
1014 x => x.ok().read_error("Invalid zstd compressed data")?,
1015 };
1016 decoder
1017 .read_to_end(&mut decompressed)
1018 .ok()
1019 .read_error("Invalid zstd compressed data")?;
1020 }
1021 }
1022 _ => unreachable!(),
1023 }
1024 if size != decompressed.len() {
1025 return Err(Error(
1026 "Uncompressed data size does not match compression header",
1027 ));
1028 }
1029
1030 Ok(Cow::Owned(decompressed))
1031 }
1032 _ => Err(Error("Unsupported compressed data.")),
1033 }
1034 }
1035}