1use crate::error::{Error, Result};
2use gimli::{Dwarf, EndianSlice, RunTimeEndian, SectionId};
3use memmap2::Mmap;
4use object::{Object, ObjectSection};
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::fs::File;
8use std::path::Path;
9
10pub struct BinaryData {
11 pub mmap: Mmap,
12}
13
14pub type DwarfSlice<'a> = EndianSlice<'a, RunTimeEndian>;
15
16pub struct LoadedDwarf<'a> {
17 pub dwarf: Dwarf<DwarfSlice<'a>>,
18 pub address_size: u8,
19 pub endian: RunTimeEndian,
20 #[allow(dead_code)]
21 decompressed_sections: HashMap<&'static str, Vec<u8>>,
22}
23
24impl BinaryData {
25 pub fn load(path: &Path) -> Result<Self> {
26 let file = File::open(path)?;
27 let mmap = unsafe { Mmap::map(&file)? };
28 Ok(Self { mmap })
29 }
30
31 pub fn load_dwarf(&self) -> Result<LoadedDwarf<'_>> {
32 let object = object::File::parse(&*self.mmap)?;
33
34 if !matches!(
35 object.format(),
36 object::BinaryFormat::Elf | object::BinaryFormat::MachO | object::BinaryFormat::Pe
37 ) {
38 return Err(Error::UnsupportedFormat);
39 }
40
41 let endian =
42 if object.is_little_endian() { RunTimeEndian::Little } else { RunTimeEndian::Big };
43
44 let mut decompressed_sections: HashMap<&'static str, Vec<u8>> = HashMap::new();
45
46 let section_names: &[&'static str] = &[
47 ".debug_abbrev",
48 ".debug_addr",
49 ".debug_aranges",
50 ".debug_info",
51 ".debug_line",
52 ".debug_line_str",
53 ".debug_loc",
54 ".debug_loclists",
55 ".debug_ranges",
56 ".debug_rnglists",
57 ".debug_str",
58 ".debug_str_offsets",
59 ".debug_types",
60 ".zdebug_abbrev",
61 ".zdebug_addr",
62 ".zdebug_aranges",
63 ".zdebug_info",
64 ".zdebug_line",
65 ".zdebug_line_str",
66 ".zdebug_loc",
67 ".zdebug_loclists",
68 ".zdebug_ranges",
69 ".zdebug_rnglists",
70 ".zdebug_str",
71 ".zdebug_str_offsets",
72 ".zdebug_types",
73 ];
74
75 for &name in section_names {
76 if let Some(section) = object.section_by_name(name) {
77 if let Ok(Cow::Owned(vec)) = section.uncompressed_data() {
78 decompressed_sections.insert(name, vec);
79 }
80 }
81 }
82
83 let decompressed_ptr = &decompressed_sections as *const HashMap<&'static str, Vec<u8>>;
90
91 let load_section = |id: SectionId| -> std::result::Result<DwarfSlice<'_>, gimli::Error> {
92 let section_name = id.name();
93 let zdebug_name = section_name.replace(".debug_", ".zdebug_");
94
95 let try_load = |name: &str| -> Option<&[u8]> {
96 let decompressed = unsafe { &*decompressed_ptr };
97 if let Some(vec) = decompressed.get(name) {
98 return Some(vec.as_slice());
99 }
100
101 object.section_by_name(name).and_then(|s| s.uncompressed_data().ok()).and_then(
102 |data| match data {
103 Cow::Borrowed(b) => Some(b),
104 Cow::Owned(_) => None,
105 },
106 )
107 };
108
109 let slice = try_load(section_name).or_else(|| try_load(&zdebug_name)).unwrap_or(&[]);
110
111 Ok(EndianSlice::new(slice, endian))
112 };
113
114 let dwarf = Dwarf::load(load_section).map_err(|e| Error::Dwarf(e.to_string()))?;
115
116 let mut units = dwarf.units();
117 if units.next().map_err(|e| Error::Dwarf(e.to_string()))?.is_none() {
118 return Err(Error::NoDebugInfo);
119 }
120
121 Ok(LoadedDwarf {
122 dwarf,
123 address_size: if object.is_64() { 8 } else { 4 },
124 endian,
125 decompressed_sections,
126 })
127 }
128}