1use crate::binary::BinaryProvider;
2use crate::error::Result;
3use gimli::{EndianSlice, RunTimeEndian};
4use serde::Serialize;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
8pub struct SourceLocation {
9 pub file: String,
10 pub line: Option<u32>,
11 pub column: Option<u32>,
12}
13
14type Slice = EndianSlice<'static, RunTimeEndian>;
15
16pub struct DwarfIndex {
18 _sections: Arc<DwarfSections>,
19 dwarf: gimli::Dwarf<Slice>,
20}
21
22struct DwarfSections {
23 debug_info: Vec<u8>,
24 debug_abbrev: Vec<u8>,
25 debug_str: Vec<u8>,
26 debug_str_offsets: Vec<u8>,
27 debug_line: Vec<u8>,
28 debug_line_str: Vec<u8>,
29 debug_ranges: Vec<u8>,
30 debug_rnglists: Vec<u8>,
31 debug_addr: Vec<u8>,
32}
33
34impl DwarfIndex {
35 pub fn load(provider: &dyn BinaryProvider) -> Result<Self> {
36 let endian = if is_big_endian(provider.architecture()) {
37 RunTimeEndian::Big
38 } else {
39 RunTimeEndian::Little
40 };
41
42 let load = |name: &str| -> Vec<u8> {
43 provider
44 .section_data(name)
45 .map(|(data, _, _)| data.to_vec())
46 .unwrap_or_default()
47 };
48
49 let sections = Arc::new(DwarfSections {
50 debug_info: load(".debug_info"),
51 debug_abbrev: load(".debug_abbrev"),
52 debug_str: load(".debug_str"),
53 debug_str_offsets: load(".debug_str_offsets"),
54 debug_line: load(".debug_line"),
55 debug_line_str: load(".debug_line_str"),
56 debug_ranges: load(".debug_ranges"),
57 debug_rnglists: load(".debug_rnglists"),
58 debug_addr: load(".debug_addr"),
59 });
60
61 let dwarf = unsafe {
66 let s = &*Arc::as_ptr(§ions);
67 let mk = |v: &Vec<u8>| -> Slice {
68 let raw: &'static [u8] = std::slice::from_raw_parts(v.as_ptr(), v.len());
69 EndianSlice::new(raw, endian)
70 };
71
72 let loader = |section: gimli::SectionId| -> std::result::Result<Slice, gimli::Error> {
73 let slice = match section {
74 gimli::SectionId::DebugInfo => mk(&s.debug_info),
75 gimli::SectionId::DebugAbbrev => mk(&s.debug_abbrev),
76 gimli::SectionId::DebugStr => mk(&s.debug_str),
77 gimli::SectionId::DebugStrOffsets => mk(&s.debug_str_offsets),
78 gimli::SectionId::DebugLine => mk(&s.debug_line),
79 gimli::SectionId::DebugLineStr => mk(&s.debug_line_str),
80 gimli::SectionId::DebugRanges => mk(&s.debug_ranges),
81 gimli::SectionId::DebugRngLists => mk(&s.debug_rnglists),
82 gimli::SectionId::DebugAddr => mk(&s.debug_addr),
83 _ => EndianSlice::new(&[], endian),
84 };
85 Ok(slice)
86 };
87
88 gimli::Dwarf::load(loader)?
89 };
90
91 Ok(Self { _sections: sections, dwarf })
92 }
93
94 pub fn resolve(&self, address: u64) -> Result<Option<SourceLocation>> {
96 let mut units = self.dwarf.units();
97 while let Some(header) = units.next()? {
98 let unit = self.dwarf.unit(header)?;
99
100 if !unit_may_contain(&self.dwarf, &unit, address)? {
101 continue;
102 }
103
104 if let Some(program) = unit.line_program.clone() {
105 let comp_dir = unit.comp_dir.clone();
106 let (program, sequences) = program.sequences()?;
107 for seq in &sequences {
108 if address < seq.start || address >= seq.end {
109 continue;
110 }
111 let mut rows = program.resume_from(seq);
112 let mut best: Option<SourceLocation> = None;
113 while let Some((header, row)) = rows.next_row()? {
114 if row.address() > address {
115 break;
116 }
117 if let Some(file) = row.file(header) {
118 if let Ok(filename) = file_to_string(&self.dwarf, &unit, file, comp_dir.as_ref()) {
119 best = Some(SourceLocation {
120 file: filename,
121 line: row.line().map(|n| n.get() as u32),
122 column: match row.column() {
123 gimli::ColumnType::LeftEdge => Some(0),
124 gimli::ColumnType::Column(c) => Some(c.get() as u32),
125 },
126 });
127 }
128 }
129 }
130 if let Some(loc) = best {
131 return Ok(Some(loc));
132 }
133 }
134 }
135 }
136 Ok(None)
137 }
138}
139
140fn unit_may_contain(
141 dwarf: &gimli::Dwarf<Slice>,
142 unit: &gimli::Unit<Slice>,
143 address: u64,
144) -> Result<bool> {
145 let mut cursor = unit.entries();
146 if let Some((_, entry)) = cursor.next_dfs()? {
147 if let Some(val) = entry.attr_value(gimli::DW_AT_ranges)? {
149 let _offset = match val {
150 gimli::AttributeValue::RangeListsRef(o) => {
151 let mut ranges = dwarf.ranges(unit, gimli::RangeListsOffset(o.0))?;
152 while let Some(range) = ranges.next()? {
153 if address >= range.begin && address < range.end {
154 return Ok(true);
155 }
156 }
157 return Ok(false);
158 }
159 gimli::AttributeValue::SecOffset(o) => {
160 let offset = gimli::RangeListsOffset(o as usize);
161 let mut ranges = dwarf.ranges(unit, offset)?;
162 while let Some(range) = ranges.next()? {
163 if address >= range.begin && address < range.end {
164 return Ok(true);
165 }
166 }
167 return Ok(false);
168 }
169 _ => return Ok(true),
170 };
171 }
172
173 let low = entry.attr_value(gimli::DW_AT_low_pc)?;
175 let high = entry.attr_value(gimli::DW_AT_high_pc)?;
176 if let Some(gimli::AttributeValue::Addr(lo)) = low {
177 let hi = match high {
178 Some(gimli::AttributeValue::Addr(a)) => a,
179 Some(gimli::AttributeValue::Udata(offset)) => lo + offset,
180 _ => return Ok(true),
181 };
182 return Ok(address >= lo && address < hi);
183 }
184 }
185 Ok(true)
186}
187
188fn file_to_string(
189 dwarf: &gimli::Dwarf<Slice>,
190 unit: &gimli::Unit<Slice>,
191 file: &gimli::FileEntry<Slice, usize>,
192 comp_dir: Option<&Slice>,
193) -> std::result::Result<String, gimli::Error> {
194 let mut path = String::new();
195
196 if let Some(dir) = comp_dir {
197 let s = dir.to_string_lossy();
198 if !s.is_empty() {
199 path.push_str(&s);
200 if !path.ends_with('/') {
201 path.push('/');
202 }
203 }
204 }
205
206 if let Some(lp) = unit.line_program.as_ref() {
207 if let Some(dir) = file.directory(lp.header()) {
208 let dir_str = dwarf.attr_string(unit, dir)?;
209 let s = dir_str.to_string_lossy();
210 let s = s.trim_end_matches('/');
211 if !s.is_empty() && !path.trim_end_matches('/').ends_with(s) {
212 path.push_str(s);
213 path.push('/');
214 }
215 }
216 }
217
218 let name = dwarf.attr_string(unit, file.path_name())?;
219 path.push_str(&name.to_string_lossy());
220 Ok(path)
221}
222
223fn is_big_endian(arch: object::Architecture) -> bool {
224 matches!(
225 arch,
226 object::Architecture::PowerPc
227 | object::Architecture::PowerPc64
228 | object::Architecture::Mips
229 | object::Architecture::Sparc64
230 )
231}