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