1 //! This crate provides a cross-platform library and binary for translating addresses into
2 //! function names, file names and line numbers. Given an address in an executable or an
3 //! offset in a section of a relocatable object, it uses the debugging information to
4 //! figure out which file name and line number are associated with it.
5 //!
6 //! When used as a library, files must first be loaded using the
7 //! [`object`](https://github.com/gimli-rs/object) crate.
8 //! A context can then be created with [`Context::new`](./struct.Context.html#method.new).
9 //! The context caches some of the parsed information so that multiple lookups are
10 //! efficient.
11 //! Location information is obtained with
12 //! [`Context::find_location`](./struct.Context.html#method.find_location) or
13 //! [`Context::find_location_range`](./struct.Context.html#method.find_location_range).
14 //! Function information is obtained with
15 //! [`Context::find_frames`](./struct.Context.html#method.find_frames), which returns
16 //! a frame for each inline function. Each frame contains both name and location.
17 //!
18 //! The crate has an example CLI wrapper around the library which provides some of
19 //! the functionality of the `addr2line` command line tool distributed with [GNU
20 //! binutils](https://www.gnu.org/software/binutils/).
21 //!
22 //! Currently this library only provides information from the DWARF debugging information,
23 //! which is parsed using [`gimli`](https://github.com/gimli-rs/gimli). The example CLI
24 //! wrapper also uses symbol table information provided by the `object` crate.
25 #![deny(missing_docs)]
26 #![no_std]
27
28 #[allow(unused_imports)]
29 #[macro_use]
30 extern crate alloc;
31
32 #[cfg(feature = "cpp_demangle")]
33 extern crate cpp_demangle;
34 #[cfg(feature = "fallible-iterator")]
35 pub extern crate fallible_iterator;
36 pub extern crate gimli;
37 #[cfg(feature = "object")]
38 pub extern crate object;
39 #[cfg(feature = "rustc-demangle")]
40 extern crate rustc_demangle;
41
42 use alloc::borrow::Cow;
43 use alloc::boxed::Box;
44 #[cfg(feature = "object")]
45 use alloc::rc::Rc;
46 use alloc::string::{String, ToString};
47 use alloc::vec::Vec;
48
49 use core::cmp::{self, Ordering};
50 use core::iter;
51 use core::mem;
52 use core::u64;
53
54 use crate::lazy::LazyCell;
55
56 #[cfg(feature = "smallvec")]
57 mod maybe_small {
58 pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
59 pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
60 }
61 #[cfg(not(feature = "smallvec"))]
62 mod maybe_small {
63 pub type Vec<T> = alloc::vec::Vec<T>;
64 pub type IntoIter<T> = alloc::vec::IntoIter<T>;
65 }
66
67 mod lazy;
68
69 type Error = gimli::Error;
70
71 /// The state necessary to perform address to line translation.
72 ///
73 /// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
74 /// when performing lookups for many addresses in the same executable.
75 pub struct Context<R>
76 where
77 R: gimli::Reader,
78 {
79 unit_ranges: Vec<UnitRange>,
80 units: Vec<ResUnit<R>>,
81 sections: gimli::Dwarf<R>,
82 }
83
84 struct UnitRange {
85 unit_id: usize,
86 max_end: u64,
87 range: gimli::Range,
88 }
89
90 /// The type of `Context` that supports the `new` method.
91 #[cfg(feature = "std-object")]
92 pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>;
93
94 #[cfg(feature = "std-object")]
95 impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> {
96 /// Construct a new `Context`.
97 ///
98 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
99 /// This means it is not thread safe, has no lifetime constraints (since it copies
100 /// the input data), and works for any endianity.
101 ///
102 /// Performance sensitive applications may want to use `Context::from_sections`
103 /// with a more specialised `gimli::Reader` implementation.
new<'data: 'file, 'file, O: object::Object<'data, 'file>>( file: &'file O, ) -> Result<Self, Error>104 pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>(
105 file: &'file O,
106 ) -> Result<Self, Error> {
107 let endian = if file.is_little_endian() {
108 gimli::RunTimeEndian::Little
109 } else {
110 gimli::RunTimeEndian::Big
111 };
112
113 fn load_section<'data: 'file, 'file, O, S, Endian>(file: &'file O, endian: Endian) -> S
114 where
115 O: object::Object<'data, 'file>,
116 S: gimli::Section<gimli::EndianRcSlice<Endian>>,
117 Endian: gimli::Endianity,
118 {
119 use object::ObjectSection;
120
121 let data = file
122 .section_by_name(S::section_name())
123 .and_then(|section| section.uncompressed_data().ok())
124 .unwrap_or(Cow::Borrowed(&[]));
125 S::from(gimli::EndianRcSlice::new(Rc::from(&*data), endian))
126 }
127
128 let debug_abbrev: gimli::DebugAbbrev<_> = load_section(file, endian);
129 let debug_addr: gimli::DebugAddr<_> = load_section(file, endian);
130 let debug_info: gimli::DebugInfo<_> = load_section(file, endian);
131 let debug_line: gimli::DebugLine<_> = load_section(file, endian);
132 let debug_line_str: gimli::DebugLineStr<_> = load_section(file, endian);
133 let debug_ranges: gimli::DebugRanges<_> = load_section(file, endian);
134 let debug_rnglists: gimli::DebugRngLists<_> = load_section(file, endian);
135 let debug_str: gimli::DebugStr<_> = load_section(file, endian);
136 let debug_str_offsets: gimli::DebugStrOffsets<_> = load_section(file, endian);
137 let default_section = gimli::EndianRcSlice::new(Rc::from(&[][..]), endian);
138
139 Context::from_sections(
140 debug_abbrev,
141 debug_addr,
142 debug_info,
143 debug_line,
144 debug_line_str,
145 debug_ranges,
146 debug_rnglists,
147 debug_str,
148 debug_str_offsets,
149 default_section,
150 )
151 }
152 }
153
154 impl<R: gimli::Reader> Context<R> {
155 /// Construct a new `Context` from DWARF sections.
from_sections( debug_abbrev: gimli::DebugAbbrev<R>, debug_addr: gimli::DebugAddr<R>, debug_info: gimli::DebugInfo<R>, debug_line: gimli::DebugLine<R>, debug_line_str: gimli::DebugLineStr<R>, debug_ranges: gimli::DebugRanges<R>, debug_rnglists: gimli::DebugRngLists<R>, debug_str: gimli::DebugStr<R>, debug_str_offsets: gimli::DebugStrOffsets<R>, default_section: R, ) -> Result<Self, Error>156 pub fn from_sections(
157 debug_abbrev: gimli::DebugAbbrev<R>,
158 debug_addr: gimli::DebugAddr<R>,
159 debug_info: gimli::DebugInfo<R>,
160 debug_line: gimli::DebugLine<R>,
161 debug_line_str: gimli::DebugLineStr<R>,
162 debug_ranges: gimli::DebugRanges<R>,
163 debug_rnglists: gimli::DebugRngLists<R>,
164 debug_str: gimli::DebugStr<R>,
165 debug_str_offsets: gimli::DebugStrOffsets<R>,
166 default_section: R,
167 ) -> Result<Self, Error> {
168 Self::from_dwarf(gimli::Dwarf {
169 debug_abbrev,
170 debug_addr,
171 debug_info,
172 debug_line,
173 debug_line_str,
174 debug_str,
175 debug_str_offsets,
176 debug_str_sup: default_section.clone().into(),
177 debug_types: default_section.clone().into(),
178 locations: gimli::LocationLists::new(
179 default_section.clone().into(),
180 default_section.clone().into(),
181 ),
182 ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
183 file_type: gimli::DwarfFileType::Main,
184 })
185 }
186
187 /// Construct a new `Context` from an existing [`gimli::Dwarf`] object.
from_dwarf(sections: gimli::Dwarf<R>) -> Result<Self, Error>188 pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Self, Error> {
189 let mut unit_ranges = Vec::new();
190 let mut res_units = Vec::new();
191 let mut units = sections.units();
192 while let Some(header) = units.next()? {
193 let unit_id = res_units.len();
194 let offset = match header.offset().as_debug_info_offset() {
195 Some(offset) => offset,
196 None => continue,
197 };
198 let dw_unit = match sections.unit(header) {
199 Ok(dw_unit) => dw_unit,
200 Err(_) => continue,
201 };
202
203 let mut lang = None;
204 {
205 let mut entries = dw_unit.entries_raw(None)?;
206
207 let abbrev = match entries.read_abbreviation()? {
208 Some(abbrev) if abbrev.tag() == gimli::DW_TAG_compile_unit => abbrev,
209 _ => continue, // wtf?
210 };
211
212 let mut ranges = RangeAttributes::default();
213 for spec in abbrev.attributes() {
214 let attr = entries.read_attribute(*spec)?;
215 match attr.name() {
216 gimli::DW_AT_low_pc => {
217 if let gimli::AttributeValue::Addr(val) = attr.value() {
218 ranges.low_pc = Some(val);
219 }
220 }
221 gimli::DW_AT_high_pc => match attr.value() {
222 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
223 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
224 _ => {}
225 },
226 gimli::DW_AT_ranges => {
227 ranges.ranges_offset =
228 sections.attr_ranges_offset(&dw_unit, attr.value())?;
229 }
230 gimli::DW_AT_language => {
231 if let gimli::AttributeValue::Language(val) = attr.value() {
232 lang = Some(val);
233 }
234 }
235 _ => {}
236 }
237 }
238
239 ranges.for_each_range(§ions, &dw_unit, |range| {
240 unit_ranges.push(UnitRange {
241 range,
242 unit_id,
243 max_end: 0,
244 });
245 })?;
246 }
247
248 res_units.push(ResUnit {
249 offset,
250 dw_unit,
251 lang,
252 lines: LazyCell::new(),
253 funcs: LazyCell::new(),
254 });
255 }
256
257 // Sort this for faster lookup in `find_unit_and_address` below.
258 unit_ranges.sort_by_key(|i| i.range.begin);
259
260 // Calculate the `max_end` field now that we've determined the order of
261 // CUs.
262 let mut max = 0;
263 for i in unit_ranges.iter_mut() {
264 max = max.max(i.range.end);
265 i.max_end = max;
266 }
267
268 Ok(Context {
269 units: res_units,
270 unit_ranges,
271 sections,
272 })
273 }
274
275 /// The dwarf sections associated with this `Context`.
dwarf(&self) -> &gimli::Dwarf<R>276 pub fn dwarf(&self) -> &gimli::Dwarf<R> {
277 &self.sections
278 }
279
280 /// Finds the CUs for the function address given.
281 ///
282 /// There might be multiple CUs whose range contains this address.
283 /// Weak symbols have shown up in the wild which cause this to happen
284 /// but otherwise this can happen if the CU has non-contiguous functions
285 /// but only reports a single range.
286 ///
287 /// Consequently we return an iterator for all CUs which may contain the
288 /// address, and the caller must check if there is actually a function or
289 /// location in the CU for that address.
find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>>290 fn find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> {
291 self.find_units_range(probe, probe + 1)
292 .map(|(unit, _range)| unit)
293 }
294
295 /// Finds the CUs covering the range of addresses given.
296 ///
297 /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple
298 /// ranges for the same unit.
299 #[inline]
find_units_range( &self, probe_low: u64, probe_high: u64, ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)>300 fn find_units_range(
301 &self,
302 probe_low: u64,
303 probe_high: u64,
304 ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> {
305 // First up find the position in the array which could have our function
306 // address.
307 let pos = match self
308 .unit_ranges
309 .binary_search_by_key(&probe_high, |i| i.range.begin)
310 {
311 // Although unlikely, we could find an exact match.
312 Ok(i) => i + 1,
313 // No exact match was found, but this probe would fit at slot `i`.
314 // This means that slot `i` is bigger than `probe`, along with all
315 // indices greater than `i`, so we need to search all previous
316 // entries.
317 Err(i) => i,
318 };
319
320 // Once we have our index we iterate backwards from that position
321 // looking for a matching CU.
322 self.unit_ranges[..pos]
323 .iter()
324 .rev()
325 .take_while(move |i| {
326 // We know that this CU's start is beneath the probe already because
327 // of our sorted array.
328 debug_assert!(i.range.begin <= probe_high);
329
330 // Each entry keeps track of the maximum end address seen so far,
331 // starting from the beginning of the array of unit ranges. We're
332 // iterating in reverse so if our probe is beyond the maximum range
333 // of this entry, then it's guaranteed to not fit in any prior
334 // entries, so we break out.
335 probe_low < i.max_end
336 })
337 .filter_map(move |i| {
338 // If this CU doesn't actually contain this address, move to the
339 // next CU.
340 if probe_low >= i.range.end || probe_high <= i.range.begin {
341 return None;
342 }
343 Some((&self.units[i.unit_id], &i.range))
344 })
345 }
346
347 /// Find the DWARF unit corresponding to the given virtual memory address.
find_dwarf_unit(&self, probe: u64) -> Option<&gimli::Unit<R>>348 pub fn find_dwarf_unit(&self, probe: u64) -> Option<&gimli::Unit<R>> {
349 for unit in self.find_units(probe) {
350 match unit.find_function_or_location(probe, &self.sections, &self.units) {
351 Ok((Some(_), _)) | Ok((_, Some(_))) => return Some(&unit.dw_unit),
352 _ => {}
353 }
354 }
355 None
356 }
357
358 /// Find the source file and line corresponding to the given virtual memory address.
find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error>359 pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
360 for unit in self.find_units(probe) {
361 if let Some(location) = unit.find_location(probe, &self.sections)? {
362 return Ok(Some(location));
363 }
364 }
365 Ok(None)
366 }
367
368 /// Return source file and lines for a range of addresses. For each location it also
369 /// returns the address and size of the range of the underlying instructions.
find_location_range( &self, probe_low: u64, probe_high: u64, ) -> Result<LocationRangeIter<'_, R>, Error>370 pub fn find_location_range(
371 &self,
372 probe_low: u64,
373 probe_high: u64,
374 ) -> Result<LocationRangeIter<'_, R>, Error> {
375 LocationRangeIter::new(self, probe_low, probe_high)
376 }
377
378 /// Return an iterator for the function frames corresponding to the given virtual
379 /// memory address.
380 ///
381 /// If the probe address is not for an inline function then only one frame is
382 /// returned.
383 ///
384 /// If the probe address is for an inline function then the first frame corresponds
385 /// to the innermost inline function. Subsequent frames contain the caller and call
386 /// location, until an non-inline caller is reached.
find_frames(&self, probe: u64) -> Result<FrameIter<R>, Error>387 pub fn find_frames(&self, probe: u64) -> Result<FrameIter<R>, Error> {
388 for unit in self.find_units(probe) {
389 match unit.find_function_or_location(probe, &self.sections, &self.units)? {
390 (Some(function), location) => {
391 let inlined_functions = function.find_inlined_functions(probe);
392 return Ok(FrameIter(FrameIterState::Frames(FrameIterFrames {
393 unit,
394 sections: &self.sections,
395 function,
396 inlined_functions,
397 next: location,
398 })));
399 }
400 (None, Some(location)) => {
401 return Ok(FrameIter(FrameIterState::Location(Some(location))));
402 }
403 _ => {}
404 }
405 }
406 Ok(FrameIter(FrameIterState::Empty))
407 }
408
409 /// Initialize all line data structures. This is used for benchmarks.
410 #[doc(hidden)]
parse_lines(&self) -> Result<(), Error>411 pub fn parse_lines(&self) -> Result<(), Error> {
412 for unit in &self.units {
413 unit.parse_lines(&self.sections)?;
414 }
415 Ok(())
416 }
417
418 /// Initialize all function data structures. This is used for benchmarks.
419 #[doc(hidden)]
parse_functions(&self) -> Result<(), Error>420 pub fn parse_functions(&self) -> Result<(), Error> {
421 for unit in &self.units {
422 unit.parse_functions(&self.sections)?;
423 }
424 Ok(())
425 }
426
427 /// Initialize all inlined function data structures. This is used for benchmarks.
428 #[doc(hidden)]
parse_inlined_functions(&self) -> Result<(), Error>429 pub fn parse_inlined_functions(&self) -> Result<(), Error> {
430 for unit in &self.units {
431 unit.parse_inlined_functions(&self.sections, &self.units)?;
432 }
433 Ok(())
434 }
435 }
436
437 struct Lines {
438 files: Box<[String]>,
439 sequences: Box<[LineSequence]>,
440 }
441
442 struct LineSequence {
443 start: u64,
444 end: u64,
445 rows: Box<[LineRow]>,
446 }
447
448 struct LineRow {
449 address: u64,
450 file_index: u64,
451 line: u32,
452 column: u32,
453 }
454
455 struct ResUnit<R>
456 where
457 R: gimli::Reader,
458 {
459 offset: gimli::DebugInfoOffset<R::Offset>,
460 dw_unit: gimli::Unit<R>,
461 lang: Option<gimli::DwLang>,
462 lines: LazyCell<Result<Lines, Error>>,
463 funcs: LazyCell<Result<Functions<R>, Error>>,
464 }
465
466 impl<R> ResUnit<R>
467 where
468 R: gimli::Reader,
469 {
parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error>470 fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> {
471 let ilnp = match self.dw_unit.line_program {
472 Some(ref ilnp) => ilnp,
473 None => return Ok(None),
474 };
475 self.lines
476 .borrow_with(|| {
477 let mut sequences = Vec::new();
478 let mut sequence_rows = Vec::<LineRow>::new();
479 let mut rows = ilnp.clone().rows();
480 while let Some((_, row)) = rows.next_row()? {
481 if row.end_sequence() {
482 if let Some(start) = sequence_rows.first().map(|x| x.address) {
483 let end = row.address();
484 let mut rows = Vec::new();
485 mem::swap(&mut rows, &mut sequence_rows);
486 sequences.push(LineSequence {
487 start,
488 end,
489 rows: rows.into_boxed_slice(),
490 });
491 }
492 continue;
493 }
494
495 let address = row.address();
496 let file_index = row.file_index();
497 let line = row.line().unwrap_or(0) as u32;
498 let column = match row.column() {
499 gimli::ColumnType::LeftEdge => 0,
500 gimli::ColumnType::Column(x) => x as u32,
501 };
502
503 if let Some(last_row) = sequence_rows.last_mut() {
504 if last_row.address == address {
505 last_row.file_index = file_index;
506 last_row.line = line;
507 last_row.column = column;
508 continue;
509 }
510 }
511
512 sequence_rows.push(LineRow {
513 address,
514 file_index,
515 line,
516 column,
517 });
518 }
519 sequences.sort_by_key(|x| x.start);
520
521 let mut files = Vec::new();
522 let header = ilnp.header();
523 match header.file(0) {
524 Some(file) => files.push(self.render_file(file, header, sections)?),
525 None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index
526 }
527 let mut index = 1;
528 while let Some(file) = header.file(index) {
529 files.push(self.render_file(file, header, sections)?);
530 index += 1;
531 }
532
533 Ok(Lines {
534 files: files.into_boxed_slice(),
535 sequences: sequences.into_boxed_slice(),
536 })
537 })
538 .as_ref()
539 .map(Some)
540 .map_err(Error::clone)
541 }
542
parse_functions(&self, sections: &gimli::Dwarf<R>) -> Result<&Functions<R>, Error>543 fn parse_functions(&self, sections: &gimli::Dwarf<R>) -> Result<&Functions<R>, Error> {
544 self.funcs
545 .borrow_with(|| Functions::parse(&self.dw_unit, sections))
546 .as_ref()
547 .map_err(Error::clone)
548 }
549
parse_inlined_functions( &self, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], ) -> Result<(), Error>550 fn parse_inlined_functions(
551 &self,
552 sections: &gimli::Dwarf<R>,
553 units: &[ResUnit<R>],
554 ) -> Result<(), Error> {
555 self.funcs
556 .borrow_with(|| Functions::parse(&self.dw_unit, sections))
557 .as_ref()
558 .map_err(Error::clone)?
559 .parse_inlined_functions(&self.dw_unit, sections, units)
560 }
561
find_location( &self, probe: u64, sections: &gimli::Dwarf<R>, ) -> Result<Option<Location<'_>>, Error>562 fn find_location(
563 &self,
564 probe: u64,
565 sections: &gimli::Dwarf<R>,
566 ) -> Result<Option<Location<'_>>, Error> {
567 if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? {
568 match iter.next() {
569 None => Ok(None),
570 Some((_addr, _len, loc)) => Ok(Some(loc)),
571 }
572 } else {
573 Ok(None)
574 }
575 }
576
577 #[inline]
find_location_range( &self, probe_low: u64, probe_high: u64, sections: &gimli::Dwarf<R>, ) -> Result<Option<LocationRangeUnitIter<'_>>, Error>578 fn find_location_range(
579 &self,
580 probe_low: u64,
581 probe_high: u64,
582 sections: &gimli::Dwarf<R>,
583 ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> {
584 LocationRangeUnitIter::new(self, sections, probe_low, probe_high)
585 }
586
find_function_or_location( &self, probe: u64, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], ) -> Result<(Option<&Function<R>>, Option<Location<'_>>), Error>587 fn find_function_or_location(
588 &self,
589 probe: u64,
590 sections: &gimli::Dwarf<R>,
591 units: &[ResUnit<R>],
592 ) -> Result<(Option<&Function<R>>, Option<Location<'_>>), Error> {
593 let functions = self.parse_functions(sections)?;
594 let function = match functions.find_address(probe) {
595 Some(address) => {
596 let function_index = functions.addresses[address].function;
597 let (offset, ref function) = functions.functions[function_index];
598 Some(
599 function
600 .borrow_with(|| Function::parse(offset, &self.dw_unit, sections, units))
601 .as_ref()
602 .map_err(Error::clone)?,
603 )
604 }
605 None => None,
606 };
607 let location = self.find_location(probe, sections)?;
608 Ok((function, location))
609 }
610
render_file( &self, file: &gimli::FileEntry<R, R::Offset>, header: &gimli::LineProgramHeader<R, R::Offset>, sections: &gimli::Dwarf<R>, ) -> Result<String, gimli::Error>611 fn render_file(
612 &self,
613 file: &gimli::FileEntry<R, R::Offset>,
614 header: &gimli::LineProgramHeader<R, R::Offset>,
615 sections: &gimli::Dwarf<R>,
616 ) -> Result<String, gimli::Error> {
617 let mut path = if let Some(ref comp_dir) = self.dw_unit.comp_dir {
618 comp_dir.to_string_lossy()?.into_owned()
619 } else {
620 String::new()
621 };
622
623 if let Some(directory) = file.directory(header) {
624 path_push(
625 &mut path,
626 sections
627 .attr_string(&self.dw_unit, directory)?
628 .to_string_lossy()?
629 .as_ref(),
630 );
631 }
632
633 path_push(
634 &mut path,
635 sections
636 .attr_string(&self.dw_unit, file.path_name())?
637 .to_string_lossy()?
638 .as_ref(),
639 );
640
641 Ok(path)
642 }
643 }
644
645 /// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`.
646 pub struct LocationRangeIter<'ctx, R: gimli::Reader> {
647 unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>,
648 iter: Option<LocationRangeUnitIter<'ctx>>,
649
650 probe_low: u64,
651 probe_high: u64,
652 sections: &'ctx gimli::Dwarf<R>,
653 }
654
655 impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> {
656 #[inline]
new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error>657 fn new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error> {
658 let sections = &ctx.sections;
659 let unit_iter = ctx.find_units_range(probe_low, probe_high);
660 Ok(Self {
661 unit_iter: Box::new(unit_iter),
662 iter: None,
663 probe_low,
664 probe_high,
665 sections,
666 })
667 }
668
next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error>669 fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> {
670 loop {
671 let iter = self.iter.take();
672 match iter {
673 None => match self.unit_iter.next() {
674 Some((unit, range)) => {
675 self.iter = unit.find_location_range(
676 cmp::max(self.probe_low, range.begin),
677 cmp::min(self.probe_high, range.end),
678 self.sections,
679 )?;
680 }
681 None => return Ok(None),
682 },
683 Some(mut iter) => {
684 if let item @ Some(_) = iter.next() {
685 self.iter = Some(iter);
686 return Ok(item);
687 }
688 }
689 }
690 }
691 }
692 }
693
694 impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R>
695 where
696 R: gimli::Reader + 'ctx,
697 {
698 type Item = (u64, u64, Location<'ctx>);
699
700 #[inline]
next(&mut self) -> Option<Self::Item>701 fn next(&mut self) -> Option<Self::Item> {
702 match self.next_loc() {
703 Err(_) => None,
704 Ok(loc) => loc,
705 }
706 }
707 }
708
709 #[cfg(feature = "fallible-iterator")]
710 impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R>
711 where
712 R: gimli::Reader + 'ctx,
713 {
714 type Item = (u64, u64, Location<'ctx>);
715 type Error = Error;
716
717 #[inline]
next(&mut self) -> Result<Option<Self::Item>, Self::Error>718 fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
719 self.next_loc()
720 }
721 }
722
723 struct LocationRangeUnitIter<'ctx> {
724 lines: &'ctx Lines,
725 seqs: &'ctx [LineSequence],
726 seq_idx: usize,
727 row_idx: usize,
728 probe_high: u64,
729 }
730
731 impl<'ctx> LocationRangeUnitIter<'ctx> {
new<R: gimli::Reader>( resunit: &'ctx ResUnit<R>, sections: &gimli::Dwarf<R>, probe_low: u64, probe_high: u64, ) -> Result<Option<Self>, Error>732 fn new<R: gimli::Reader>(
733 resunit: &'ctx ResUnit<R>,
734 sections: &gimli::Dwarf<R>,
735 probe_low: u64,
736 probe_high: u64,
737 ) -> Result<Option<Self>, Error> {
738 let lines = resunit.parse_lines(sections)?;
739
740 if let Some(lines) = lines {
741 // Find index for probe_low.
742 let seq_idx = lines.sequences.binary_search_by(|sequence| {
743 if probe_low < sequence.start {
744 Ordering::Greater
745 } else if probe_low >= sequence.end {
746 Ordering::Less
747 } else {
748 Ordering::Equal
749 }
750 });
751 let seq_idx = match seq_idx {
752 Ok(x) => x,
753 Err(0) => 0, // probe below sequence, but range could overlap
754 Err(_) => lines.sequences.len(),
755 };
756
757 let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) {
758 let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low));
759 let idx = match idx {
760 Ok(x) => x,
761 Err(0) => 0, // probe below sequence, but range could overlap
762 Err(x) => x - 1,
763 };
764 idx
765 } else {
766 0
767 };
768
769 Ok(Some(Self {
770 lines,
771 seqs: &*lines.sequences,
772 seq_idx,
773 row_idx,
774 probe_high,
775 }))
776 } else {
777 Ok(None)
778 }
779 }
780 }
781
782 impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> {
783 type Item = (u64, u64, Location<'ctx>);
784
next(&mut self) -> Option<(u64, u64, Location<'ctx>)>785 fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> {
786 loop {
787 let seq = match self.seqs.get(self.seq_idx) {
788 Some(seq) => seq,
789 None => break,
790 };
791
792 if seq.start >= self.probe_high {
793 break;
794 }
795
796 match seq.rows.get(self.row_idx) {
797 Some(row) => {
798 if row.address >= self.probe_high {
799 break;
800 }
801
802 let file = self
803 .lines
804 .files
805 .get(row.file_index as usize)
806 .map(String::as_str);
807 let nextaddr = seq
808 .rows
809 .get(self.row_idx + 1)
810 .map(|row| row.address)
811 .unwrap_or(seq.end);
812
813 let item = (
814 row.address,
815 nextaddr - row.address,
816 Location {
817 file,
818 line: if row.line != 0 { Some(row.line) } else { None },
819 column: if row.column != 0 {
820 Some(row.column)
821 } else {
822 None
823 },
824 },
825 );
826 self.row_idx += 1;
827
828 return Some(item);
829 }
830 None => {
831 self.seq_idx += 1;
832 self.row_idx = 0;
833 }
834 }
835 }
836 None
837 }
838 }
839
path_push(path: &mut String, p: &str)840 fn path_push(path: &mut String, p: &str) {
841 if p.starts_with('/') {
842 *path = p.to_string();
843 } else {
844 if !path.ends_with('/') {
845 path.push('/');
846 }
847 *path += p;
848 }
849 }
850
name_attr<R>( attr: gimli::AttributeValue<R>, unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], recursion_limit: usize, ) -> Result<Option<R>, Error> where R: gimli::Reader,851 fn name_attr<R>(
852 attr: gimli::AttributeValue<R>,
853 unit: &gimli::Unit<R>,
854 sections: &gimli::Dwarf<R>,
855 units: &[ResUnit<R>],
856 recursion_limit: usize,
857 ) -> Result<Option<R>, Error>
858 where
859 R: gimli::Reader,
860 {
861 if recursion_limit == 0 {
862 return Ok(None);
863 }
864
865 let (unit, offset) = match attr {
866 gimli::AttributeValue::UnitRef(offset) => (unit, offset),
867 gimli::AttributeValue::DebugInfoRef(dr) => {
868 let res_unit = match units.binary_search_by_key(&dr.0, |unit| unit.offset.0) {
869 // There is never a DIE at the unit offset or before the first unit.
870 Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
871 Err(i) => &units[i - 1],
872 };
873 (
874 &res_unit.dw_unit,
875 gimli::UnitOffset(dr.0 - res_unit.offset.0),
876 )
877 }
878 _ => return Ok(None),
879 };
880
881 let mut entries = unit.entries_raw(Some(offset))?;
882 let abbrev = if let Some(abbrev) = entries.read_abbreviation()? {
883 abbrev
884 } else {
885 return Err(gimli::Error::NoEntryAtGivenOffset);
886 };
887
888 let mut name = None;
889 let mut next = None;
890 for spec in abbrev.attributes() {
891 match entries.read_attribute(*spec) {
892 Ok(ref attr) => match attr.name() {
893 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
894 if let Ok(val) = sections.attr_string(unit, attr.value()) {
895 return Ok(Some(val));
896 }
897 }
898 gimli::DW_AT_name => {
899 if let Ok(val) = sections.attr_string(unit, attr.value()) {
900 name = Some(val);
901 }
902 }
903 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
904 next = Some(attr.value());
905 }
906 _ => {}
907 },
908 Err(e) => return Err(e),
909 }
910 }
911
912 if name.is_some() {
913 return Ok(name);
914 }
915
916 if let Some(next) = next {
917 return name_attr(next, unit, sections, units, recursion_limit - 1);
918 }
919
920 Ok(None)
921 }
922
923 struct Functions<R: gimli::Reader> {
924 /// List of all `DW_TAG_subprogram` details in the unit.
925 functions: Box<
926 [(
927 gimli::UnitOffset<R::Offset>,
928 LazyCell<Result<Function<R>, Error>>,
929 )],
930 >,
931 /// List of `DW_TAG_subprogram` address ranges in the unit.
932 addresses: Box<[FunctionAddress]>,
933 }
934
935 /// A single address range for a function.
936 ///
937 /// It is possible for a function to have multiple address ranges; this
938 /// is handled by having multiple `FunctionAddress` entries with the same
939 /// `function` field.
940 struct FunctionAddress {
941 range: gimli::Range,
942 /// An index into `Functions::functions`.
943 function: usize,
944 }
945
946 struct Function<R: gimli::Reader> {
947 dw_die_offset: gimli::UnitOffset<R::Offset>,
948 name: Option<R>,
949 /// List of all `DW_TAG_inlined_subroutine` details in this function.
950 inlined_functions: Box<[InlinedFunction<R>]>,
951 /// List of `DW_TAG_inlined_subroutine` address ranges in this function.
952 inlined_addresses: Box<[InlinedFunctionAddress]>,
953 }
954
955 struct InlinedFunctionAddress {
956 range: gimli::Range,
957 call_depth: usize,
958 /// An index into `Function::inlined_functions`.
959 function: usize,
960 }
961
962 struct InlinedFunction<R: gimli::Reader> {
963 dw_die_offset: gimli::UnitOffset<R::Offset>,
964 name: Option<R>,
965 call_file: u64,
966 call_line: u32,
967 call_column: u32,
968 }
969
970 impl<R: gimli::Reader> Functions<R> {
parse(unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>) -> Result<Functions<R>, Error>971 fn parse(unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>) -> Result<Functions<R>, Error> {
972 let mut functions = Vec::new();
973 let mut addresses = Vec::new();
974 let mut entries = unit.entries_raw(None)?;
975 while !entries.is_empty() {
976 let dw_die_offset = entries.next_offset();
977 if let Some(abbrev) = entries.read_abbreviation()? {
978 if abbrev.tag() == gimli::DW_TAG_subprogram {
979 let mut ranges = RangeAttributes::default();
980 for spec in abbrev.attributes() {
981 match entries.read_attribute(*spec) {
982 Ok(ref attr) => {
983 match attr.name() {
984 gimli::DW_AT_low_pc => {
985 if let gimli::AttributeValue::Addr(val) = attr.value() {
986 ranges.low_pc = Some(val);
987 }
988 }
989 gimli::DW_AT_high_pc => match attr.value() {
990 gimli::AttributeValue::Addr(val) => {
991 ranges.high_pc = Some(val)
992 }
993 gimli::AttributeValue::Udata(val) => {
994 ranges.size = Some(val)
995 }
996 _ => {}
997 },
998 gimli::DW_AT_ranges => {
999 ranges.ranges_offset =
1000 sections.attr_ranges_offset(unit, attr.value())?;
1001 }
1002 _ => {}
1003 };
1004 }
1005 Err(e) => return Err(e),
1006 }
1007 }
1008
1009 let function_index = functions.len();
1010 if ranges.for_each_range(sections, unit, |range| {
1011 addresses.push(FunctionAddress {
1012 range,
1013 function: function_index,
1014 });
1015 })? {
1016 functions.push((dw_die_offset, LazyCell::new()));
1017 }
1018 } else {
1019 for spec in abbrev.attributes() {
1020 match entries.read_attribute(*spec) {
1021 Ok(_) => {}
1022 Err(e) => return Err(e),
1023 }
1024 }
1025 }
1026 }
1027 }
1028
1029 // The binary search requires the addresses to be sorted.
1030 //
1031 // It also requires them to be non-overlapping. In practice, overlapping
1032 // function ranges are unlikely, so we don't try to handle that yet.
1033 //
1034 // It's possible for multiple functions to have the same address range if the
1035 // compiler can detect and remove functions with identical code. In that case
1036 // we'll nondeterministically return one of them.
1037 addresses.sort_by_key(|x| x.range.begin);
1038
1039 Ok(Functions {
1040 functions: functions.into_boxed_slice(),
1041 addresses: addresses.into_boxed_slice(),
1042 })
1043 }
1044
find_address(&self, probe: u64) -> Option<usize>1045 fn find_address(&self, probe: u64) -> Option<usize> {
1046 self.addresses
1047 .binary_search_by(|address| {
1048 if probe < address.range.begin {
1049 Ordering::Greater
1050 } else if probe >= address.range.end {
1051 Ordering::Less
1052 } else {
1053 Ordering::Equal
1054 }
1055 })
1056 .ok()
1057 }
1058
parse_inlined_functions( &self, unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], ) -> Result<(), Error>1059 fn parse_inlined_functions(
1060 &self,
1061 unit: &gimli::Unit<R>,
1062 sections: &gimli::Dwarf<R>,
1063 units: &[ResUnit<R>],
1064 ) -> Result<(), Error> {
1065 for function in &*self.functions {
1066 function
1067 .1
1068 .borrow_with(|| Function::parse(function.0, unit, sections, units))
1069 .as_ref()
1070 .map_err(Error::clone)?;
1071 }
1072 Ok(())
1073 }
1074 }
1075
1076 impl<R: gimli::Reader> Function<R> {
parse( dw_die_offset: gimli::UnitOffset<R::Offset>, unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], ) -> Result<Self, Error>1077 fn parse(
1078 dw_die_offset: gimli::UnitOffset<R::Offset>,
1079 unit: &gimli::Unit<R>,
1080 sections: &gimli::Dwarf<R>,
1081 units: &[ResUnit<R>],
1082 ) -> Result<Self, Error> {
1083 let mut entries = unit.entries_raw(Some(dw_die_offset))?;
1084 let depth = entries.next_depth();
1085 let abbrev = entries.read_abbreviation()?.unwrap();
1086 debug_assert_eq!(abbrev.tag(), gimli::DW_TAG_subprogram);
1087
1088 let mut name = None;
1089 for spec in abbrev.attributes() {
1090 match entries.read_attribute(*spec) {
1091 Ok(ref attr) => {
1092 match attr.name() {
1093 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
1094 if let Ok(val) = sections.attr_string(unit, attr.value()) {
1095 name = Some(val);
1096 }
1097 }
1098 gimli::DW_AT_name => {
1099 if name.is_none() {
1100 name = sections.attr_string(unit, attr.value()).ok();
1101 }
1102 }
1103 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
1104 if name.is_none() {
1105 name = name_attr(attr.value(), unit, sections, units, 16)?;
1106 }
1107 }
1108 _ => {}
1109 };
1110 }
1111 Err(e) => return Err(e),
1112 }
1113 }
1114
1115 let mut inlined_functions = Vec::new();
1116 let mut inlined_addresses = Vec::new();
1117 Function::parse_children(
1118 &mut entries,
1119 depth,
1120 unit,
1121 sections,
1122 units,
1123 &mut inlined_functions,
1124 &mut inlined_addresses,
1125 0,
1126 )?;
1127
1128 // Sort ranges in "breadth-first traversal order", i.e. first by call_depth
1129 // and then by range.begin. This allows finding the range containing an
1130 // address at a certain depth using binary search.
1131 // Note: Using DFS order, i.e. ordering by range.begin first and then by
1132 // call_depth, would not work! Consider the two examples
1133 // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and
1134 // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]".
1135 // In this example, if you want to look up address 7 at depth 0, and you
1136 // encounter [0..2 at depth 1], are you before or after the target range?
1137 // You don't know.
1138 inlined_addresses.sort_by(|r1, r2| {
1139 if r1.call_depth < r2.call_depth {
1140 Ordering::Less
1141 } else if r1.call_depth > r2.call_depth {
1142 Ordering::Greater
1143 } else if r1.range.begin < r2.range.begin {
1144 Ordering::Less
1145 } else if r1.range.begin > r2.range.begin {
1146 Ordering::Greater
1147 } else {
1148 Ordering::Equal
1149 }
1150 });
1151
1152 Ok(Function {
1153 dw_die_offset,
1154 name,
1155 inlined_functions: inlined_functions.into_boxed_slice(),
1156 inlined_addresses: inlined_addresses.into_boxed_slice(),
1157 })
1158 }
1159
parse_children( entries: &mut gimli::EntriesRaw<R>, depth: isize, unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], inlined_functions: &mut Vec<InlinedFunction<R>>, inlined_addresses: &mut Vec<InlinedFunctionAddress>, inlined_depth: usize, ) -> Result<(), Error>1160 fn parse_children(
1161 entries: &mut gimli::EntriesRaw<R>,
1162 depth: isize,
1163 unit: &gimli::Unit<R>,
1164 sections: &gimli::Dwarf<R>,
1165 units: &[ResUnit<R>],
1166 inlined_functions: &mut Vec<InlinedFunction<R>>,
1167 inlined_addresses: &mut Vec<InlinedFunctionAddress>,
1168 inlined_depth: usize,
1169 ) -> Result<(), Error> {
1170 loop {
1171 let dw_die_offset = entries.next_offset();
1172 let next_depth = entries.next_depth();
1173 if next_depth <= depth {
1174 return Ok(());
1175 }
1176 if let Some(abbrev) = entries.read_abbreviation()? {
1177 match abbrev.tag() {
1178 gimli::DW_TAG_subprogram => {
1179 Function::skip(entries, abbrev, next_depth)?;
1180 }
1181 gimli::DW_TAG_inlined_subroutine => {
1182 InlinedFunction::parse(
1183 dw_die_offset,
1184 entries,
1185 abbrev,
1186 next_depth,
1187 unit,
1188 sections,
1189 units,
1190 inlined_functions,
1191 inlined_addresses,
1192 inlined_depth,
1193 )?;
1194 }
1195 _ => {
1196 for spec in abbrev.attributes() {
1197 match entries.read_attribute(*spec) {
1198 Ok(_) => {}
1199 Err(e) => return Err(e),
1200 }
1201 }
1202 }
1203 }
1204 }
1205 }
1206 }
1207
skip( entries: &mut gimli::EntriesRaw<R>, abbrev: &gimli::Abbreviation, depth: isize, ) -> Result<(), Error>1208 fn skip(
1209 entries: &mut gimli::EntriesRaw<R>,
1210 abbrev: &gimli::Abbreviation,
1211 depth: isize,
1212 ) -> Result<(), Error> {
1213 // TODO: use DW_AT_sibling
1214 for spec in abbrev.attributes() {
1215 match entries.read_attribute(*spec) {
1216 Ok(_) => {}
1217 Err(e) => return Err(e),
1218 }
1219 }
1220 while entries.next_depth() > depth {
1221 if let Some(abbrev) = entries.read_abbreviation()? {
1222 for spec in abbrev.attributes() {
1223 match entries.read_attribute(*spec) {
1224 Ok(_) => {}
1225 Err(e) => return Err(e),
1226 }
1227 }
1228 }
1229 }
1230 Ok(())
1231 }
1232
1233 /// Build the list of inlined functions that contain `probe`.
find_inlined_functions( &self, probe: u64, ) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>>1234 fn find_inlined_functions(
1235 &self,
1236 probe: u64,
1237 ) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>> {
1238 // `inlined_functions` is ordered from outside to inside.
1239 let mut inlined_functions = maybe_small::Vec::new();
1240 let mut inlined_addresses = &self.inlined_addresses[..];
1241 loop {
1242 let current_depth = inlined_functions.len();
1243 // Look up (probe, current_depth) in inline_ranges.
1244 // `inlined_addresses` is sorted in "breadth-first traversal order", i.e.
1245 // by `call_depth` first, and then by `range.begin`. See the comment at
1246 // the sort call for more information about why.
1247 let search = inlined_addresses.binary_search_by(|range| {
1248 if range.call_depth > current_depth {
1249 Ordering::Greater
1250 } else if range.call_depth < current_depth {
1251 Ordering::Less
1252 } else if range.range.begin > probe {
1253 Ordering::Greater
1254 } else if range.range.end <= probe {
1255 Ordering::Less
1256 } else {
1257 Ordering::Equal
1258 }
1259 });
1260 if let Ok(index) = search {
1261 let function_index = inlined_addresses[index].function;
1262 inlined_functions.push(&self.inlined_functions[function_index]);
1263 inlined_addresses = &inlined_addresses[index + 1..];
1264 } else {
1265 break;
1266 }
1267 }
1268 inlined_functions.into_iter().rev()
1269 }
1270 }
1271
1272 impl<R: gimli::Reader> InlinedFunction<R> {
parse( dw_die_offset: gimli::UnitOffset<R::Offset>, entries: &mut gimli::EntriesRaw<R>, abbrev: &gimli::Abbreviation, depth: isize, unit: &gimli::Unit<R>, sections: &gimli::Dwarf<R>, units: &[ResUnit<R>], inlined_functions: &mut Vec<InlinedFunction<R>>, inlined_addresses: &mut Vec<InlinedFunctionAddress>, inlined_depth: usize, ) -> Result<(), Error>1273 fn parse(
1274 dw_die_offset: gimli::UnitOffset<R::Offset>,
1275 entries: &mut gimli::EntriesRaw<R>,
1276 abbrev: &gimli::Abbreviation,
1277 depth: isize,
1278 unit: &gimli::Unit<R>,
1279 sections: &gimli::Dwarf<R>,
1280 units: &[ResUnit<R>],
1281 inlined_functions: &mut Vec<InlinedFunction<R>>,
1282 inlined_addresses: &mut Vec<InlinedFunctionAddress>,
1283 inlined_depth: usize,
1284 ) -> Result<(), Error> {
1285 let mut ranges = RangeAttributes::default();
1286 let mut name = None;
1287 let mut call_file = 0;
1288 let mut call_line = 0;
1289 let mut call_column = 0;
1290 for spec in abbrev.attributes() {
1291 match entries.read_attribute(*spec) {
1292 Ok(ref attr) => match attr.name() {
1293 gimli::DW_AT_low_pc => {
1294 if let gimli::AttributeValue::Addr(val) = attr.value() {
1295 ranges.low_pc = Some(val);
1296 }
1297 }
1298 gimli::DW_AT_high_pc => match attr.value() {
1299 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
1300 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
1301 _ => {}
1302 },
1303 gimli::DW_AT_ranges => {
1304 ranges.ranges_offset = sections.attr_ranges_offset(unit, attr.value())?;
1305 }
1306 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
1307 if let Ok(val) = sections.attr_string(unit, attr.value()) {
1308 name = Some(val);
1309 }
1310 }
1311 gimli::DW_AT_name => {
1312 if name.is_none() {
1313 name = sections.attr_string(unit, attr.value()).ok();
1314 }
1315 }
1316 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
1317 if name.is_none() {
1318 name = name_attr(attr.value(), unit, sections, units, 16)?;
1319 }
1320 }
1321 gimli::DW_AT_call_file => {
1322 if let gimli::AttributeValue::FileIndex(fi) = attr.value() {
1323 call_file = fi;
1324 }
1325 }
1326 gimli::DW_AT_call_line => {
1327 call_line = attr.udata_value().unwrap_or(0) as u32;
1328 }
1329 gimli::DW_AT_call_column => {
1330 call_column = attr.udata_value().unwrap_or(0) as u32;
1331 }
1332 _ => {}
1333 },
1334 Err(e) => return Err(e),
1335 }
1336 }
1337
1338 let function_index = inlined_functions.len();
1339 inlined_functions.push(InlinedFunction {
1340 dw_die_offset,
1341 name,
1342 call_file,
1343 call_line,
1344 call_column,
1345 });
1346
1347 ranges.for_each_range(sections, unit, |range| {
1348 inlined_addresses.push(InlinedFunctionAddress {
1349 range,
1350 call_depth: inlined_depth,
1351 function: function_index,
1352 });
1353 })?;
1354
1355 Function::parse_children(
1356 entries,
1357 depth,
1358 unit,
1359 sections,
1360 units,
1361 inlined_functions,
1362 inlined_addresses,
1363 inlined_depth + 1,
1364 )
1365 }
1366 }
1367
1368 struct RangeAttributes<R: gimli::Reader> {
1369 low_pc: Option<u64>,
1370 high_pc: Option<u64>,
1371 size: Option<u64>,
1372 ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
1373 }
1374
1375 impl<R: gimli::Reader> Default for RangeAttributes<R> {
default() -> Self1376 fn default() -> Self {
1377 RangeAttributes {
1378 low_pc: None,
1379 high_pc: None,
1380 size: None,
1381 ranges_offset: None,
1382 }
1383 }
1384 }
1385
1386 impl<R: gimli::Reader> RangeAttributes<R> {
for_each_range<F: FnMut(gimli::Range)>( &self, sections: &gimli::Dwarf<R>, unit: &gimli::Unit<R>, mut f: F, ) -> Result<bool, Error>1387 fn for_each_range<F: FnMut(gimli::Range)>(
1388 &self,
1389 sections: &gimli::Dwarf<R>,
1390 unit: &gimli::Unit<R>,
1391 mut f: F,
1392 ) -> Result<bool, Error> {
1393 let mut added_any = false;
1394 let mut add_range = |range: gimli::Range| {
1395 if range.begin < range.end {
1396 f(range);
1397 added_any = true
1398 }
1399 };
1400 if let Some(ranges_offset) = self.ranges_offset {
1401 let mut range_list = sections.ranges(unit, ranges_offset)?;
1402 while let Some(range) = range_list.next()? {
1403 add_range(range);
1404 }
1405 } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
1406 add_range(gimli::Range { begin, end });
1407 } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
1408 add_range(gimli::Range {
1409 begin,
1410 end: begin + size,
1411 });
1412 }
1413 Ok(added_any)
1414 }
1415 }
1416
1417 /// An iterator over function frames.
1418 pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
1419 where
1420 R: gimli::Reader + 'ctx;
1421
1422 enum FrameIterState<'ctx, R>
1423 where
1424 R: gimli::Reader + 'ctx,
1425 {
1426 Empty,
1427 Location(Option<Location<'ctx>>),
1428 Frames(FrameIterFrames<'ctx, R>),
1429 }
1430
1431 struct FrameIterFrames<'ctx, R>
1432 where
1433 R: gimli::Reader + 'ctx,
1434 {
1435 unit: &'ctx ResUnit<R>,
1436 sections: &'ctx gimli::Dwarf<R>,
1437 function: &'ctx Function<R>,
1438 inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
1439 next: Option<Location<'ctx>>,
1440 }
1441
1442 impl<'ctx, R> FrameIter<'ctx, R>
1443 where
1444 R: gimli::Reader + 'ctx,
1445 {
1446 /// Advances the iterator and returns the next frame.
next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error>1447 pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
1448 let frames = match &mut self.0 {
1449 FrameIterState::Empty => return Ok(None),
1450 FrameIterState::Location(location) => {
1451 // We can't move out of a mutable reference, so use `take` instead.
1452 let location = location.take();
1453 self.0 = FrameIterState::Empty;
1454 return Ok(Some(Frame {
1455 dw_die_offset: None,
1456 function: None,
1457 location,
1458 }));
1459 }
1460 FrameIterState::Frames(frames) => frames,
1461 };
1462
1463 let loc = frames.next.take();
1464 let func = match frames.inlined_functions.next() {
1465 Some(func) => func,
1466 None => {
1467 let frame = Frame {
1468 dw_die_offset: Some(frames.function.dw_die_offset),
1469 function: frames.function.name.clone().map(|name| FunctionName {
1470 name,
1471 language: frames.unit.lang,
1472 }),
1473 location: loc,
1474 };
1475 self.0 = FrameIterState::Empty;
1476 return Ok(Some(frame));
1477 }
1478 };
1479
1480 let mut next = Location {
1481 file: None,
1482 line: if func.call_line != 0 {
1483 Some(func.call_line)
1484 } else {
1485 None
1486 },
1487 column: if func.call_column != 0 {
1488 Some(func.call_column)
1489 } else {
1490 None
1491 },
1492 };
1493 if func.call_file != 0 {
1494 if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
1495 next.file = lines.files.get(func.call_file as usize).map(String::as_str);
1496 }
1497 }
1498 frames.next = Some(next);
1499
1500 Ok(Some(Frame {
1501 dw_die_offset: Some(func.dw_die_offset),
1502 function: func.name.clone().map(|name| FunctionName {
1503 name,
1504 language: frames.unit.lang,
1505 }),
1506 location: loc,
1507 }))
1508 }
1509 }
1510
1511 #[cfg(feature = "fallible-iterator")]
1512 impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
1513 where
1514 R: gimli::Reader + 'ctx,
1515 {
1516 type Item = Frame<'ctx, R>;
1517 type Error = Error;
1518
1519 #[inline]
next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error>1520 fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
1521 self.next()
1522 }
1523 }
1524
1525 /// A function frame.
1526 pub struct Frame<'ctx, R: gimli::Reader> {
1527 /// The DWARF unit offset corresponding to the DIE of the function.
1528 pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
1529 /// The name of the function.
1530 pub function: Option<FunctionName<R>>,
1531 /// The source location corresponding to this frame.
1532 pub location: Option<Location<'ctx>>,
1533 }
1534
1535 /// A function name.
1536 pub struct FunctionName<R: gimli::Reader> {
1537 /// The name of the function.
1538 pub name: R,
1539 /// The language of the compilation unit containing this function.
1540 pub language: Option<gimli::DwLang>,
1541 }
1542
1543 impl<R: gimli::Reader> FunctionName<R> {
1544 /// The raw name of this function before demangling.
raw_name(&self) -> Result<Cow<str>, Error>1545 pub fn raw_name(&self) -> Result<Cow<str>, Error> {
1546 self.name.to_string_lossy()
1547 }
1548
1549 /// The name of this function after demangling (if applicable).
demangle(&self) -> Result<Cow<str>, Error>1550 pub fn demangle(&self) -> Result<Cow<str>, Error> {
1551 self.raw_name().map(|x| demangle_auto(x, self.language))
1552 }
1553 }
1554
1555 /// Demangle a symbol name using the demangling scheme for the given language.
1556 ///
1557 /// Returns `None` if demangling failed or is not required.
1558 #[allow(unused_variables)]
demangle(name: &str, language: gimli::DwLang) -> Option<String>1559 pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
1560 match language {
1561 #[cfg(feature = "rustc-demangle")]
1562 gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name)
1563 .ok()
1564 .as_ref()
1565 .map(|x| format!("{:#}", x)),
1566 #[cfg(feature = "cpp_demangle")]
1567 gimli::DW_LANG_C_plus_plus
1568 | gimli::DW_LANG_C_plus_plus_03
1569 | gimli::DW_LANG_C_plus_plus_11
1570 | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name)
1571 .ok()
1572 .and_then(|x| x.demangle(&Default::default()).ok()),
1573 _ => None,
1574 }
1575 }
1576
1577 /// Apply 'best effort' demangling of a symbol name.
1578 ///
1579 /// If `language` is given, then only the demangling scheme for that language
1580 /// is used.
1581 ///
1582 /// If `language` is `None`, then heuristics are used to determine how to
1583 /// demangle the name. Currently, these heuristics are very basic.
1584 ///
1585 /// If demangling fails or is not required, then `name` is returned unchanged.
demangle_auto(name: Cow<str>, language: Option<gimli::DwLang>) -> Cow<str>1586 pub fn demangle_auto(name: Cow<str>, language: Option<gimli::DwLang>) -> Cow<str> {
1587 match language {
1588 Some(language) => demangle(name.as_ref(), language),
1589 None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
1590 .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
1591 }
1592 .map(Cow::from)
1593 .unwrap_or(name)
1594 }
1595
1596 /// A source location.
1597 pub struct Location<'a> {
1598 /// The file name.
1599 pub file: Option<&'a str>,
1600 /// The line number.
1601 pub line: Option<u32>,
1602 /// The column number.
1603 pub column: Option<u32>,
1604 }
1605