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#[cfg(feature = "std")]
29extern crate std;
30
31#[allow(unused_imports)]
32#[macro_use]
33extern crate alloc;
34
35#[cfg(feature = "fallible-iterator")]
36pub extern crate fallible_iterator;
37pub extern crate gimli;
38#[cfg(feature = "object")]
39pub extern crate object;
40
41use alloc::borrow::Cow;
42use alloc::boxed::Box;
43#[cfg(feature = "object")]
44use alloc::rc::Rc;
45use alloc::string::{String, ToString};
46use alloc::sync::Arc;
47use alloc::vec::Vec;
48
49use core::cmp::{self, Ordering};
50use core::iter;
51use core::marker::PhantomData;
52use core::mem;
53use core::num::NonZeroU64;
54use core::ops::ControlFlow;
55use core::u64;
56
57use crate::function::{Function, Functions, InlinedFunction};
58use crate::lazy::LazyCell;
59
60#[cfg(feature = "smallvec")]
61mod maybe_small {
62 pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
63 pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
64}
65#[cfg(not(feature = "smallvec"))]
66mod maybe_small {
67 pub type Vec<T> = alloc::vec::Vec<T>;
68 pub type IntoIter<T> = alloc::vec::IntoIter<T>;
69}
70
71#[cfg(all(feature = "std", feature = "object", feature = "memmap2"))]
72/// A simple builtin split DWARF loader.
73pub mod builtin_split_dwarf_loader;
74mod function;
75mod lazy;
76
77type Error = gimli::Error;
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80enum DebugFile {
81 Primary,
82 Supplementary,
83 Dwo,
84}
85
86/// Operations that consult debug information may require additional files
87/// to be loaded if split DWARF is being used. This enum returns the result
88/// of the operation in the `Break` variant, or information about the split
89/// DWARF that is required and a continuation to invoke once it is available
90/// in the `Continue` variant.
91///
92/// This enum is intended to be used in a loop like so:
93/// ```no_run
94/// # use addr2line::*;
95/// # use std::sync::Arc;
96/// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
97/// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
98/// const ADDRESS: u64 = 0xdeadbeef;
99/// let mut r = ctx.find_frames(ADDRESS);
100/// let result = loop {
101/// match r {
102/// LookupResult::Output(result) => break result,
103/// LookupResult::Load { load, continuation } => {
104/// let dwo = do_split_dwarf_load(load);
105/// r = continuation.resume(dwo);
106/// }
107/// }
108/// };
109/// ```
110pub enum LookupResult<L: LookupContinuation> {
111 /// The lookup requires split DWARF data to be loaded.
112 Load {
113 /// The information needed to find the split DWARF data.
114 load: SplitDwarfLoad<<L as LookupContinuation>::Buf>,
115 /// The continuation to resume with the loaded split DWARF data.
116 continuation: L,
117 },
118 /// The lookup has completed and produced an output.
119 Output(<L as LookupContinuation>::Output),
120}
121
122/// This trait represents a partially complete operation that can be resumed
123/// once a load of needed split DWARF data is completed or abandoned by the
124/// API consumer.
125pub trait LookupContinuation: Sized {
126 /// The final output of this operation.
127 type Output;
128 /// The type of reader used.
129 type Buf: gimli::Reader;
130
131 /// Resumes the operation with the provided data.
132 ///
133 /// After the caller loads the split DWARF data required, call this
134 /// method to resume the operation. The return value of this method
135 /// indicates if the computation has completed or if further data is
136 /// required.
137 ///
138 /// If the additional data cannot be located, or the caller does not
139 /// support split DWARF, `resume(None)` can be used to continue the
140 /// operation with the data that is available.
141 fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>;
142}
143
144impl<L: LookupContinuation> LookupResult<L> {
145 /// Callers that do not handle split DWARF can call `skip_all_loads`
146 /// to fast-forward to the end result. This result is produced with
147 /// the data that is available and may be less accurate than the
148 /// the results that would be produced if the caller did properly
149 /// support split DWARF.
150 pub fn skip_all_loads(mut self) -> L::Output {
151 loop {
152 self = match self {
153 LookupResult::Output(t) => return t,
154 LookupResult::Load { continuation, .. } => continuation.resume(None),
155 };
156 }
157 }
158
159 fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>> {
160 match self {
161 LookupResult::Output(t) => LookupResult::Output(f(t)),
162 LookupResult::Load { load, continuation } => LookupResult::Load {
163 load,
164 continuation: MappedLookup {
165 original: continuation,
166 mutator: f,
167 },
168 },
169 }
170 }
171
172 fn unwrap(self) -> L::Output {
173 match self {
174 LookupResult::Output(t) => t,
175 LookupResult::Load { .. } => unreachable!("Internal API misuse"),
176 }
177 }
178}
179
180/// The state necessary to perform address to line translation.
181///
182/// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
183/// when performing lookups for many addresses in the same executable.
184pub struct Context<R: gimli::Reader> {
185 sections: Arc<gimli::Dwarf<R>>,
186 unit_ranges: Box<[UnitRange]>,
187 units: Box<[ResUnit<R>]>,
188 sup_units: Box<[SupUnit<R>]>,
189}
190
191/// The type of `Context` that supports the `new` method.
192#[cfg(feature = "std-object")]
193pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>;
194
195#[cfg(feature = "std-object")]
196impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> {
197 /// Construct a new `Context`.
198 ///
199 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
200 /// This means it is not thread safe, has no lifetime constraints (since it copies
201 /// the input data), and works for any endianity.
202 ///
203 /// Performance sensitive applications may want to use `Context::from_dwarf`
204 /// with a more specialised `gimli::Reader` implementation.
205 #[inline]
206 pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>(
207 file: &'file O,
208 ) -> Result<Self, Error> {
209 Self::new_with_sup(file, None)
210 }
211
212 /// Construct a new `Context`.
213 ///
214 /// Optionally also use a supplementary object file.
215 ///
216 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
217 /// This means it is not thread safe, has no lifetime constraints (since it copies
218 /// the input data), and works for any endianity.
219 ///
220 /// Performance sensitive applications may want to use `Context::from_dwarf`
221 /// with a more specialised `gimli::Reader` implementation.
222 pub fn new_with_sup<'data: 'file, 'file, O: object::Object<'data, 'file>>(
223 file: &'file O,
224 sup_file: Option<&'file O>,
225 ) -> Result<Self, Error> {
226 let endian = if file.is_little_endian() {
227 gimli::RunTimeEndian::Little
228 } else {
229 gimli::RunTimeEndian::Big
230 };
231
232 fn load_section<'data: 'file, 'file, O, Endian>(
233 id: gimli::SectionId,
234 file: &'file O,
235 endian: Endian,
236 ) -> Result<gimli::EndianRcSlice<Endian>, Error>
237 where
238 O: object::Object<'data, 'file>,
239 Endian: gimli::Endianity,
240 {
241 use object::ObjectSection;
242
243 let data = file
244 .section_by_name(id.name())
245 .and_then(|section| section.uncompressed_data().ok())
246 .unwrap_or(Cow::Borrowed(&[]));
247 Ok(gimli::EndianRcSlice::new(Rc::from(&*data), endian))
248 }
249
250 let mut dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian))?;
251 if let Some(sup_file) = sup_file {
252 dwarf.load_sup(|id| load_section(id, sup_file, endian))?;
253 }
254 Context::from_dwarf(dwarf)
255 }
256}
257
258impl<R: gimli::Reader> Context<R> {
259 /// Construct a new `Context` from DWARF sections.
260 ///
261 /// This method does not support using a supplementary object file.
262 pub fn from_sections(
263 debug_abbrev: gimli::DebugAbbrev<R>,
264 debug_addr: gimli::DebugAddr<R>,
265 debug_aranges: gimli::DebugAranges<R>,
266 debug_info: gimli::DebugInfo<R>,
267 debug_line: gimli::DebugLine<R>,
268 debug_line_str: gimli::DebugLineStr<R>,
269 debug_ranges: gimli::DebugRanges<R>,
270 debug_rnglists: gimli::DebugRngLists<R>,
271 debug_str: gimli::DebugStr<R>,
272 debug_str_offsets: gimli::DebugStrOffsets<R>,
273 default_section: R,
274 ) -> Result<Self, Error> {
275 Self::from_dwarf(gimli::Dwarf {
276 debug_abbrev,
277 debug_addr,
278 debug_aranges,
279 debug_info,
280 debug_line,
281 debug_line_str,
282 debug_str,
283 debug_str_offsets,
284 debug_types: default_section.clone().into(),
285 locations: gimli::LocationLists::new(
286 default_section.clone().into(),
287 default_section.into(),
288 ),
289 ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
290 file_type: gimli::DwarfFileType::Main,
291 sup: None,
292 abbreviations_cache: gimli::AbbreviationsCache::new(),
293 })
294 }
295
296 /// Construct a new `Context` from an existing [`gimli::Dwarf`] object.
297 #[inline]
298 pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
299 let sections = Arc::new(sections);
300 let (unit_ranges, units) = Context::parse_units(&sections)?;
301 let sup_units = if let Some(sup) = sections.sup.as_ref() {
302 Context::parse_sup(sup)?
303 } else {
304 Vec::new()
305 };
306 Ok(Context {
307 sections,
308 unit_ranges: unit_ranges.into_boxed_slice(),
309 units: units.into_boxed_slice(),
310 sup_units: sup_units.into_boxed_slice(),
311 })
312 }
313
314 /// Finds the CUs for the function address given.
315 ///
316 /// There might be multiple CUs whose range contains this address.
317 /// Weak symbols have shown up in the wild which cause this to happen
318 /// but otherwise this can happen if the CU has non-contiguous functions
319 /// but only reports a single range.
320 ///
321 /// Consequently we return an iterator for all CUs which may contain the
322 /// address, and the caller must check if there is actually a function or
323 /// location in the CU for that address.
324 fn find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> {
325 self.find_units_range(probe, probe + 1)
326 .map(|(unit, _range)| unit)
327 }
328
329 /// Finds the CUs covering the range of addresses given.
330 ///
331 /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple
332 /// ranges for the same unit.
333 #[inline]
334 fn find_units_range(
335 &self,
336 probe_low: u64,
337 probe_high: u64,
338 ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> {
339 // First up find the position in the array which could have our function
340 // address.
341 let pos = match self
342 .unit_ranges
343 .binary_search_by_key(&probe_high, |i| i.range.begin)
344 {
345 // Although unlikely, we could find an exact match.
346 Ok(i) => i + 1,
347 // No exact match was found, but this probe would fit at slot `i`.
348 // This means that slot `i` is bigger than `probe`, along with all
349 // indices greater than `i`, so we need to search all previous
350 // entries.
351 Err(i) => i,
352 };
353
354 // Once we have our index we iterate backwards from that position
355 // looking for a matching CU.
356 self.unit_ranges[..pos]
357 .iter()
358 .rev()
359 .take_while(move |i| {
360 // We know that this CU's start is beneath the probe already because
361 // of our sorted array.
362 debug_assert!(i.range.begin <= probe_high);
363
364 // Each entry keeps track of the maximum end address seen so far,
365 // starting from the beginning of the array of unit ranges. We're
366 // iterating in reverse so if our probe is beyond the maximum range
367 // of this entry, then it's guaranteed to not fit in any prior
368 // entries, so we break out.
369 probe_low < i.max_end
370 })
371 .filter_map(move |i| {
372 // If this CU doesn't actually contain this address, move to the
373 // next CU.
374 if probe_low >= i.range.end || probe_high <= i.range.begin {
375 return None;
376 }
377 Some((&self.units[i.unit_id], &i.range))
378 })
379 }
380
381 /// Find the DWARF unit corresponding to the given virtual memory address.
382 pub fn find_dwarf_and_unit(
383 &self,
384 probe: u64,
385 ) -> LookupResult<
386 impl LookupContinuation<Output = Option<(&gimli::Dwarf<R>, &gimli::Unit<R>)>, Buf = R>,
387 > {
388 let mut units_iter = self.find_units(probe);
389 if let Some(unit) = units_iter.next() {
390 return LoopingLookup::new_lookup(
391 unit.find_function_or_location(probe, self),
392 move |r| {
393 ControlFlow::Break(match r {
394 Ok((Some(_), _)) | Ok((_, Some(_))) => {
395 let (_file, sections, unit) = unit
396 .dwarf_and_unit_dwo(self)
397 // We've already been through both error cases here to get to this point.
398 .unwrap()
399 .unwrap();
400 Some((sections, unit))
401 }
402 _ => match units_iter.next() {
403 Some(next_unit) => {
404 return ControlFlow::Continue(
405 next_unit.find_function_or_location(probe, self),
406 );
407 }
408 None => None,
409 },
410 })
411 },
412 );
413 }
414
415 LoopingLookup::new_complete(None)
416 }
417
418 /// Find the source file and line corresponding to the given virtual memory address.
419 pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
420 for unit in self.find_units(probe) {
421 if let Some(location) = unit.find_location(probe, &self.sections)? {
422 return Ok(Some(location));
423 }
424 }
425 Ok(None)
426 }
427
428 /// Return source file and lines for a range of addresses. For each location it also
429 /// returns the address and size of the range of the underlying instructions.
430 pub fn find_location_range(
431 &self,
432 probe_low: u64,
433 probe_high: u64,
434 ) -> Result<LocationRangeIter<'_, R>, Error> {
435 LocationRangeIter::new(self, probe_low, probe_high)
436 }
437
438 /// Return an iterator for the function frames corresponding to the given virtual
439 /// memory address.
440 ///
441 /// If the probe address is not for an inline function then only one frame is
442 /// returned.
443 ///
444 /// If the probe address is for an inline function then the first frame corresponds
445 /// to the innermost inline function. Subsequent frames contain the caller and call
446 /// location, until an non-inline caller is reached.
447 pub fn find_frames(
448 &self,
449 probe: u64,
450 ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>>
451 {
452 let mut units_iter = self.find_units(probe);
453 if let Some(unit) = units_iter.next() {
454 LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| {
455 ControlFlow::Break(match r {
456 Err(e) => Err(e),
457 Ok((Some(function), location)) => {
458 let inlined_functions = function.find_inlined_functions(probe);
459 Ok(FrameIter(FrameIterState::Frames(FrameIterFrames {
460 unit,
461 sections: &self.sections,
462 function,
463 inlined_functions,
464 next: location,
465 })))
466 }
467 Ok((None, Some(location))) => {
468 Ok(FrameIter(FrameIterState::Location(Some(location))))
469 }
470 Ok((None, None)) => match units_iter.next() {
471 Some(next_unit) => {
472 return ControlFlow::Continue(
473 next_unit.find_function_or_location(probe, self),
474 );
475 }
476 None => Ok(FrameIter(FrameIterState::Empty)),
477 },
478 })
479 })
480 } else {
481 LoopingLookup::new_complete(Ok(FrameIter(FrameIterState::Empty)))
482 }
483 }
484
485 /// Preload units for `probe`.
486 ///
487 /// The iterator returns pairs of `SplitDwarfLoad`s containing the
488 /// information needed to locate and load split DWARF for `probe` and
489 /// a matching callback to invoke once that data is available.
490 ///
491 /// If this method is called, and all of the returned closures are invoked,
492 /// addr2line guarantees that any future API call for the address `probe`
493 /// will not require the loading of any split DWARF.
494 ///
495 /// ```no_run
496 /// # use addr2line::*;
497 /// # use std::sync::Arc;
498 /// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
499 /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
500 /// const ADDRESS: u64 = 0xdeadbeef;
501 /// ctx.preload_units(ADDRESS).for_each(|(load, callback)| {
502 /// let dwo = do_split_dwarf_load(load);
503 /// callback(dwo);
504 /// });
505 ///
506 /// let frames_iter = match ctx.find_frames(ADDRESS) {
507 /// LookupResult::Output(result) => result,
508 /// LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"),
509 /// };
510 ///
511 /// // ...
512 /// ```
513 pub fn preload_units(
514 &'_ self,
515 probe: u64,
516 ) -> impl Iterator<
517 Item = (
518 SplitDwarfLoad<R>,
519 impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_,
520 ),
521 > {
522 self.find_units(probe)
523 .filter_map(move |unit| match unit.dwarf_and_unit_dwo(self) {
524 LookupResult::Output(_) => None,
525 LookupResult::Load { load, continuation } => Some((load, |result| {
526 continuation.resume(result).unwrap().map(|_| ())
527 })),
528 })
529 }
530
531 /// Initialize all line data structures. This is used for benchmarks.
532 #[doc(hidden)]
533 pub fn parse_lines(&self) -> Result<(), Error> {
534 for unit in self.units.iter() {
535 unit.parse_lines(&self.sections)?;
536 }
537 Ok(())
538 }
539
540 /// Initialize all function data structures. This is used for benchmarks.
541 #[doc(hidden)]
542 pub fn parse_functions(&self) -> Result<(), Error> {
543 for unit in self.units.iter() {
544 unit.parse_functions(self).skip_all_loads()?;
545 }
546 Ok(())
547 }
548
549 /// Initialize all inlined function data structures. This is used for benchmarks.
550 #[doc(hidden)]
551 pub fn parse_inlined_functions(&self) -> Result<(), Error> {
552 for unit in self.units.iter() {
553 unit.parse_inlined_functions(self).skip_all_loads()?;
554 }
555 Ok(())
556 }
557}
558
559struct UnitRange {
560 unit_id: usize,
561 max_end: u64,
562 range: gimli::Range,
563}
564
565struct ResUnit<R: gimli::Reader> {
566 offset: gimli::DebugInfoOffset<R::Offset>,
567 dw_unit: gimli::Unit<R>,
568 lang: Option<gimli::DwLang>,
569 lines: LazyCell<Result<Lines, Error>>,
570 funcs: LazyCell<Result<Functions<R>, Error>>,
571 dwo: LazyCell<Result<Option<Box<(Arc<gimli::Dwarf<R>>, gimli::Unit<R>)>>, Error>>,
572}
573
574struct SupUnit<R: gimli::Reader> {
575 offset: gimli::DebugInfoOffset<R::Offset>,
576 dw_unit: gimli::Unit<R>,
577}
578
579impl<R: gimli::Reader> Context<R> {
580 fn parse_units(sections: &gimli::Dwarf<R>) -> Result<(Vec<UnitRange>, Vec<ResUnit<R>>), Error> {
581 // Find all the references to compilation units in .debug_aranges.
582 // Note that we always also iterate through all of .debug_info to
583 // find compilation units, because .debug_aranges may be missing some.
584 let mut aranges = Vec::new();
585 let mut headers = sections.debug_aranges.headers();
586 while let Some(header) = headers.next()? {
587 aranges.push((header.debug_info_offset(), header.offset()));
588 }
589 aranges.sort_by_key(|i| i.0);
590
591 let mut unit_ranges = Vec::new();
592 let mut res_units = Vec::new();
593 let mut units = sections.units();
594 while let Some(header) = units.next()? {
595 let unit_id = res_units.len();
596 let offset = match header.offset().as_debug_info_offset() {
597 Some(offset) => offset,
598 None => continue,
599 };
600 // We mainly want compile units, but we may need to follow references to entries
601 // within other units for function names. We don't need anything from type units.
602 match header.type_() {
603 gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue,
604 _ => {}
605 }
606 let dw_unit = match sections.unit(header) {
607 Ok(dw_unit) => dw_unit,
608 Err(_) => continue,
609 };
610
611 let mut lang = None;
612 let mut have_unit_range = false;
613 {
614 let mut entries = dw_unit.entries_raw(None)?;
615
616 let abbrev = match entries.read_abbreviation()? {
617 Some(abbrev) => abbrev,
618 None => continue,
619 };
620
621 let mut ranges = RangeAttributes::default();
622 for spec in abbrev.attributes() {
623 let attr = entries.read_attribute(*spec)?;
624 match attr.name() {
625 gimli::DW_AT_low_pc => match attr.value() {
626 gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val),
627 gimli::AttributeValue::DebugAddrIndex(index) => {
628 ranges.low_pc = Some(sections.address(&dw_unit, index)?);
629 }
630 _ => {}
631 },
632 gimli::DW_AT_high_pc => match attr.value() {
633 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
634 gimli::AttributeValue::DebugAddrIndex(index) => {
635 ranges.high_pc = Some(sections.address(&dw_unit, index)?);
636 }
637 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
638 _ => {}
639 },
640 gimli::DW_AT_ranges => {
641 ranges.ranges_offset =
642 sections.attr_ranges_offset(&dw_unit, attr.value())?;
643 }
644 gimli::DW_AT_language => {
645 if let gimli::AttributeValue::Language(val) = attr.value() {
646 lang = Some(val);
647 }
648 }
649 _ => {}
650 }
651 }
652
653 // Find the address ranges for the CU, using in order of preference:
654 // - DW_AT_ranges
655 // - .debug_aranges
656 // - DW_AT_low_pc/DW_AT_high_pc
657 //
658 // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice,
659 // but the feeling is that DW_AT_ranges is more likely to be reliable or complete
660 // if it is present.
661 //
662 // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because
663 // it has been observed on macOS that DW_AT_ranges was not emitted even for
664 // discontiguous CUs.
665 let i = match ranges.ranges_offset {
666 Some(_) => None,
667 None => aranges.binary_search_by_key(&offset, |x| x.0).ok(),
668 };
669 if let Some(mut i) = i {
670 // There should be only one set per CU, but in practice multiple
671 // sets have been observed. This is probably a compiler bug, but
672 // either way we need to handle it.
673 while i > 0 && aranges[i - 1].0 == offset {
674 i -= 1;
675 }
676 for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) {
677 let aranges_header = sections.debug_aranges.header(*aranges_offset)?;
678 let mut aranges = aranges_header.entries();
679 while let Some(arange) = aranges.next()? {
680 if arange.length() != 0 {
681 unit_ranges.push(UnitRange {
682 range: arange.range(),
683 unit_id,
684 max_end: 0,
685 });
686 have_unit_range = true;
687 }
688 }
689 }
690 } else {
691 have_unit_range |= ranges.for_each_range(sections, &dw_unit, |range| {
692 unit_ranges.push(UnitRange {
693 range,
694 unit_id,
695 max_end: 0,
696 });
697 })?;
698 }
699 }
700
701 let lines = LazyCell::new();
702 if !have_unit_range {
703 // The unit did not declare any ranges.
704 // Try to get some ranges from the line program sequences.
705 if let Some(ref ilnp) = dw_unit.line_program {
706 if let Ok(lines) = lines
707 .borrow_with(|| Lines::parse(&dw_unit, ilnp.clone(), sections))
708 .as_ref()
709 {
710 for sequence in lines.sequences.iter() {
711 unit_ranges.push(UnitRange {
712 range: gimli::Range {
713 begin: sequence.start,
714 end: sequence.end,
715 },
716 unit_id,
717 max_end: 0,
718 })
719 }
720 }
721 }
722 }
723
724 res_units.push(ResUnit {
725 offset,
726 dw_unit,
727 lang,
728 lines,
729 funcs: LazyCell::new(),
730 dwo: LazyCell::new(),
731 });
732 }
733
734 // Sort this for faster lookup in `find_unit_and_address` below.
735 unit_ranges.sort_by_key(|i| i.range.begin);
736
737 // Calculate the `max_end` field now that we've determined the order of
738 // CUs.
739 let mut max = 0;
740 for i in unit_ranges.iter_mut() {
741 max = max.max(i.range.end);
742 i.max_end = max;
743 }
744
745 Ok((unit_ranges, res_units))
746 }
747
748 fn parse_sup(sections: &gimli::Dwarf<R>) -> Result<Vec<SupUnit<R>>, Error> {
749 let mut sup_units = Vec::new();
750 let mut units = sections.units();
751 while let Some(header) = units.next()? {
752 let offset = match header.offset().as_debug_info_offset() {
753 Some(offset) => offset,
754 None => continue,
755 };
756 let dw_unit = match sections.unit(header) {
757 Ok(dw_unit) => dw_unit,
758 Err(_) => continue,
759 };
760 sup_units.push(SupUnit { dw_unit, offset });
761 }
762 Ok(sup_units)
763 }
764
765 // Find the unit containing the given offset, and convert the offset into a unit offset.
766 fn find_unit(
767 &self,
768 offset: gimli::DebugInfoOffset<R::Offset>,
769 file: DebugFile,
770 ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> {
771 let unit = match file {
772 DebugFile::Primary => {
773 match self
774 .units
775 .binary_search_by_key(&offset.0, |unit| unit.offset.0)
776 {
777 // There is never a DIE at the unit offset or before the first unit.
778 Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
779 Err(i) => &self.units[i - 1].dw_unit,
780 }
781 }
782 DebugFile::Supplementary => {
783 match self
784 .sup_units
785 .binary_search_by_key(&offset.0, |unit| unit.offset.0)
786 {
787 // There is never a DIE at the unit offset or before the first unit.
788 Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
789 Err(i) => &self.sup_units[i - 1].dw_unit,
790 }
791 }
792 DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset),
793 };
794
795 let unit_offset = offset
796 .to_unit_offset(&unit.header)
797 .ok_or(gimli::Error::NoEntryAtGivenOffset)?;
798 Ok((unit, unit_offset))
799 }
800}
801
802struct Lines {
803 files: Box<[String]>,
804 sequences: Box<[LineSequence]>,
805}
806
807impl Lines {
808 fn parse<R: gimli::Reader>(
809 dw_unit: &gimli::Unit<R>,
810 ilnp: gimli::IncompleteLineProgram<R, R::Offset>,
811 sections: &gimli::Dwarf<R>,
812 ) -> Result<Self, Error> {
813 let mut sequences = Vec::new();
814 let mut sequence_rows = Vec::<LineRow>::new();
815 let mut rows = ilnp.rows();
816 while let Some((_, row)) = rows.next_row()? {
817 if row.end_sequence() {
818 if let Some(start) = sequence_rows.first().map(|x| x.address) {
819 let end = row.address();
820 let mut rows = Vec::new();
821 mem::swap(&mut rows, &mut sequence_rows);
822 sequences.push(LineSequence {
823 start,
824 end,
825 rows: rows.into_boxed_slice(),
826 });
827 }
828 continue;
829 }
830
831 let address = row.address();
832 let file_index = row.file_index();
833 let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32;
834 let column = match row.column() {
835 gimli::ColumnType::LeftEdge => 0,
836 gimli::ColumnType::Column(x) => x.get() as u32,
837 };
838
839 if let Some(last_row) = sequence_rows.last_mut() {
840 if last_row.address == address {
841 last_row.file_index = file_index;
842 last_row.line = line;
843 last_row.column = column;
844 continue;
845 }
846 }
847
848 sequence_rows.push(LineRow {
849 address,
850 file_index,
851 line,
852 column,
853 });
854 }
855 sequences.sort_by_key(|x| x.start);
856
857 let mut files = Vec::new();
858 let header = rows.header();
859 match header.file(0) {
860 Some(file) => files.push(render_file(dw_unit, file, header, sections)?),
861 None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index
862 }
863 let mut index = 1;
864 while let Some(file) = header.file(index) {
865 files.push(render_file(dw_unit, file, header, sections)?);
866 index += 1;
867 }
868
869 Ok(Self {
870 files: files.into_boxed_slice(),
871 sequences: sequences.into_boxed_slice(),
872 })
873 }
874}
875
876fn render_file<R: gimli::Reader>(
877 dw_unit: &gimli::Unit<R>,
878 file: &gimli::FileEntry<R, R::Offset>,
879 header: &gimli::LineProgramHeader<R, R::Offset>,
880 sections: &gimli::Dwarf<R>,
881) -> Result<String, gimli::Error> {
882 let mut path = if let Some(ref comp_dir) = dw_unit.comp_dir {
883 comp_dir.to_string_lossy()?.into_owned()
884 } else {
885 String::new()
886 };
887
888 // The directory index 0 is defined to correspond to the compilation unit directory.
889 if file.directory_index() != 0 {
890 if let Some(directory) = file.directory(header) {
891 path_push(
892 &mut path,
893 sections
894 .attr_string(dw_unit, directory)?
895 .to_string_lossy()?
896 .as_ref(),
897 );
898 }
899 }
900
901 path_push(
902 &mut path,
903 sections
904 .attr_string(dw_unit, file.path_name())?
905 .to_string_lossy()?
906 .as_ref(),
907 );
908
909 Ok(path)
910}
911
912struct LineSequence {
913 start: u64,
914 end: u64,
915 rows: Box<[LineRow]>,
916}
917
918struct LineRow {
919 address: u64,
920 file_index: u64,
921 line: u32,
922 column: u32,
923}
924
925/// This struct contains the information needed to find split DWARF data
926/// and to produce a `gimli::Dwarf<R>` for it.
927pub struct SplitDwarfLoad<R> {
928 /// The dwo id, for looking up in a DWARF package, or for
929 /// verifying an unpacked dwo found on the file system
930 pub dwo_id: gimli::DwoId,
931 /// The compilation directory `path` is relative to.
932 pub comp_dir: Option<R>,
933 /// A path on the filesystem, relative to `comp_dir` to find this dwo.
934 pub path: Option<R>,
935 /// Once the split DWARF data is loaded, the loader is expected
936 /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before
937 /// returning the data.
938 pub parent: Arc<gimli::Dwarf<R>>,
939}
940
941struct SimpleLookup<T, R, F>
942where
943 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
944 R: gimli::Reader,
945{
946 f: F,
947 phantom: PhantomData<(T, R)>,
948}
949
950impl<T, R, F> SimpleLookup<T, R, F>
951where
952 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
953 R: gimli::Reader,
954{
955 fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> {
956 LookupResult::Output(t)
957 }
958
959 fn new_needs_load(load: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> {
960 LookupResult::Load {
961 load,
962 continuation: SimpleLookup {
963 f,
964 phantom: PhantomData,
965 },
966 }
967 }
968}
969
970impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F>
971where
972 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
973 R: gimli::Reader,
974{
975 type Output = T;
976 type Buf = R;
977
978 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
979 LookupResult::Output((self.f)(v))
980 }
981}
982
983struct MappedLookup<T, L, F>
984where
985 L: LookupContinuation,
986 F: FnOnce(L::Output) -> T,
987{
988 original: L,
989 mutator: F,
990}
991
992impl<T, L, F> LookupContinuation for MappedLookup<T, L, F>
993where
994 L: LookupContinuation,
995 F: FnOnce(L::Output) -> T,
996{
997 type Output = T;
998 type Buf = L::Buf;
999
1000 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
1001 match self.original.resume(input:v) {
1002 LookupResult::Output(t: ::Output) => LookupResult::Output((self.mutator)(t)),
1003 LookupResult::Load { load: SplitDwarfLoad<::Buf>, continuation: L } => LookupResult::Load {
1004 load,
1005 continuation: MappedLookup {
1006 original: continuation,
1007 mutator: self.mutator,
1008 },
1009 },
1010 }
1011 }
1012}
1013
1014/// Some functions (e.g. `find_frames`) require considering multiple
1015/// compilation units, each of which might require their own split DWARF
1016/// lookup (and thus produce a continuation).
1017///
1018/// We store the underlying continuation here as well as a mutator function
1019/// that will either a) decide that the result of this continuation is
1020/// what is needed and mutate it to the final result or b) produce another
1021/// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation
1022/// `LookupResult` with successive invocations of the mutator, until a new
1023/// continuation or a final result is produced. And finally, the impl of
1024/// `LookupContinuation::resume` will call `new_lookup` each time the
1025/// computation is resumed.
1026struct LoopingLookup<T, L, F>
1027where
1028 L: LookupContinuation,
1029 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
1030{
1031 continuation: L,
1032 mutator: F,
1033}
1034
1035impl<T, L, F> LoopingLookup<T, L, F>
1036where
1037 L: LookupContinuation,
1038 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
1039{
1040 fn new_complete(t: T) -> LookupResult<Self> {
1041 LookupResult::Output(t)
1042 }
1043
1044 fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> {
1045 // Drive the loop eagerly so that we only ever have to represent one state
1046 // (the r == ControlFlow::Continue state) in LoopingLookup.
1047 loop {
1048 match r {
1049 LookupResult::Output(l) => match mutator(l) {
1050 ControlFlow::Break(t) => return LookupResult::Output(t),
1051 ControlFlow::Continue(r2) => {
1052 r = r2;
1053 }
1054 },
1055 LookupResult::Load { load, continuation } => {
1056 return LookupResult::Load {
1057 load,
1058 continuation: LoopingLookup {
1059 continuation,
1060 mutator,
1061 },
1062 };
1063 }
1064 }
1065 }
1066 }
1067}
1068
1069impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F>
1070where
1071 L: LookupContinuation,
1072 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
1073{
1074 type Output = T;
1075 type Buf = L::Buf;
1076
1077 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
1078 let r: LookupResult = self.continuation.resume(input:v);
1079 LoopingLookup::new_lookup(r, self.mutator)
1080 }
1081}
1082
1083impl<R: gimli::Reader> ResUnit<R> {
1084 fn dwarf_and_unit_dwo<'unit, 'ctx: 'unit>(
1085 &'unit self,
1086 ctx: &'ctx Context<R>,
1087 ) -> LookupResult<
1088 SimpleLookup<
1089 Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
1090 R,
1091 impl FnOnce(
1092 Option<Arc<gimli::Dwarf<R>>>,
1093 )
1094 -> Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
1095 >,
1096 > {
1097 loop {
1098 break SimpleLookup::new_complete(match self.dwo.borrow() {
1099 Some(Ok(Some(v))) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
1100 Some(Ok(None)) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
1101 Some(Err(e)) => Err(*e),
1102 None => {
1103 let dwo_id = match self.dw_unit.dwo_id {
1104 None => {
1105 self.dwo.borrow_with(|| Ok(None));
1106 continue;
1107 }
1108 Some(dwo_id) => dwo_id,
1109 };
1110
1111 let comp_dir = self.dw_unit.comp_dir.clone();
1112
1113 let dwo_name = self.dw_unit.dwo_name().and_then(|s| {
1114 if let Some(s) = s {
1115 Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?))
1116 } else {
1117 Ok(None)
1118 }
1119 });
1120
1121 let path = match dwo_name {
1122 Ok(v) => v,
1123 Err(e) => {
1124 self.dwo.borrow_with(|| Err(e));
1125 continue;
1126 }
1127 };
1128
1129 let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| {
1130 let dwo_dwarf = match dwo_dwarf {
1131 None => return Ok(None),
1132 Some(dwo_dwarf) => dwo_dwarf,
1133 };
1134 let mut dwo_units = dwo_dwarf.units();
1135 let dwo_header = match dwo_units.next()? {
1136 Some(dwo_header) => dwo_header,
1137 None => return Ok(None),
1138 };
1139
1140 let mut dwo_unit = dwo_dwarf.unit(dwo_header)?;
1141 dwo_unit.copy_relocated_attributes(&self.dw_unit);
1142 Ok(Some(Box::new((dwo_dwarf, dwo_unit))))
1143 };
1144
1145 return SimpleLookup::new_needs_load(
1146 SplitDwarfLoad {
1147 dwo_id,
1148 comp_dir,
1149 path,
1150 parent: ctx.sections.clone(),
1151 },
1152 move |dwo_dwarf| match self.dwo.borrow_with(|| process_dwo(dwo_dwarf)) {
1153 Ok(Some(v)) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
1154 Ok(None) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
1155 Err(e) => Err(*e),
1156 },
1157 );
1158 }
1159 });
1160 }
1161 }
1162
1163 fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> {
1164 // NB: line information is always stored in the main debug file so this does not need
1165 // to handle DWOs.
1166 let ilnp = match self.dw_unit.line_program {
1167 Some(ref ilnp) => ilnp,
1168 None => return Ok(None),
1169 };
1170 self.lines
1171 .borrow_with(|| Lines::parse(&self.dw_unit, ilnp.clone(), sections))
1172 .as_ref()
1173 .map(Some)
1174 .map_err(Error::clone)
1175 }
1176
1177 fn parse_functions_dwarf_and_unit(
1178 &self,
1179 unit: &gimli::Unit<R>,
1180 sections: &gimli::Dwarf<R>,
1181 ) -> Result<&Functions<R>, Error> {
1182 self.funcs
1183 .borrow_with(|| Functions::parse(unit, sections))
1184 .as_ref()
1185 .map_err(Error::clone)
1186 }
1187
1188 fn parse_functions<'unit, 'ctx: 'unit>(
1189 &'unit self,
1190 ctx: &'ctx Context<R>,
1191 ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>>
1192 {
1193 self.dwarf_and_unit_dwo(ctx).map(move |r| {
1194 let (_file, sections, unit) = r?;
1195 self.parse_functions_dwarf_and_unit(unit, sections)
1196 })
1197 }
1198 fn parse_inlined_functions<'unit, 'ctx: 'unit>(
1199 &'unit self,
1200 ctx: &'ctx Context<R>,
1201 ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> {
1202 self.dwarf_and_unit_dwo(ctx).map(move |r| {
1203 let (file, sections, unit) = r?;
1204 self.funcs
1205 .borrow_with(|| Functions::parse(unit, sections))
1206 .as_ref()
1207 .map_err(Error::clone)?
1208 .parse_inlined_functions(file, unit, ctx, sections)
1209 })
1210 }
1211
1212 fn find_location(
1213 &self,
1214 probe: u64,
1215 sections: &gimli::Dwarf<R>,
1216 ) -> Result<Option<Location<'_>>, Error> {
1217 if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? {
1218 match iter.next() {
1219 None => Ok(None),
1220 Some((_addr, _len, loc)) => Ok(Some(loc)),
1221 }
1222 } else {
1223 Ok(None)
1224 }
1225 }
1226
1227 #[inline]
1228 fn find_location_range(
1229 &self,
1230 probe_low: u64,
1231 probe_high: u64,
1232 sections: &gimli::Dwarf<R>,
1233 ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> {
1234 LocationRangeUnitIter::new(self, sections, probe_low, probe_high)
1235 }
1236
1237 fn find_function_or_location<'unit, 'ctx: 'unit>(
1238 &'unit self,
1239 probe: u64,
1240 ctx: &'ctx Context<R>,
1241 ) -> LookupResult<
1242 impl LookupContinuation<
1243 Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>,
1244 Buf = R,
1245 >,
1246 > {
1247 self.dwarf_and_unit_dwo(ctx).map(move |r| {
1248 let (file, sections, unit) = r?;
1249 let functions = self.parse_functions_dwarf_and_unit(unit, sections)?;
1250 let function = match functions.find_address(probe) {
1251 Some(address) => {
1252 let function_index = functions.addresses[address].function;
1253 let (offset, ref function) = functions.functions[function_index];
1254 Some(
1255 function
1256 .borrow_with(|| Function::parse(offset, file, unit, ctx, sections))
1257 .as_ref()
1258 .map_err(Error::clone)?,
1259 )
1260 }
1261 None => None,
1262 };
1263 let location = self.find_location(probe, sections)?;
1264 Ok((function, location))
1265 })
1266 }
1267}
1268
1269/// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`.
1270pub struct LocationRangeIter<'ctx, R: gimli::Reader> {
1271 unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>,
1272 iter: Option<LocationRangeUnitIter<'ctx>>,
1273
1274 probe_low: u64,
1275 probe_high: u64,
1276 sections: &'ctx gimli::Dwarf<R>,
1277}
1278
1279impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> {
1280 #[inline]
1281 fn new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error> {
1282 let sections = &ctx.sections;
1283 let unit_iter = ctx.find_units_range(probe_low, probe_high);
1284 Ok(Self {
1285 unit_iter: Box::new(unit_iter),
1286 iter: None,
1287 probe_low,
1288 probe_high,
1289 sections,
1290 })
1291 }
1292
1293 fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> {
1294 loop {
1295 let iter = self.iter.take();
1296 match iter {
1297 None => match self.unit_iter.next() {
1298 Some((unit, range)) => {
1299 self.iter = unit.find_location_range(
1300 cmp::max(self.probe_low, range.begin),
1301 cmp::min(self.probe_high, range.end),
1302 self.sections,
1303 )?;
1304 }
1305 None => return Ok(None),
1306 },
1307 Some(mut iter) => {
1308 if let item @ Some(_) = iter.next() {
1309 self.iter = Some(iter);
1310 return Ok(item);
1311 }
1312 }
1313 }
1314 }
1315 }
1316}
1317
1318impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R>
1319where
1320 R: gimli::Reader + 'ctx,
1321{
1322 type Item = (u64, u64, Location<'ctx>);
1323
1324 #[inline]
1325 fn next(&mut self) -> Option<Self::Item> {
1326 match self.next_loc() {
1327 Err(_) => None,
1328 Ok(loc: Option<(u64, u64, Location<'_>)>) => loc,
1329 }
1330 }
1331}
1332
1333#[cfg(feature = "fallible-iterator")]
1334impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R>
1335where
1336 R: gimli::Reader + 'ctx,
1337{
1338 type Item = (u64, u64, Location<'ctx>);
1339 type Error = Error;
1340
1341 #[inline]
1342 fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
1343 self.next_loc()
1344 }
1345}
1346
1347struct LocationRangeUnitIter<'ctx> {
1348 lines: &'ctx Lines,
1349 seqs: &'ctx [LineSequence],
1350 seq_idx: usize,
1351 row_idx: usize,
1352 probe_high: u64,
1353}
1354
1355impl<'ctx> LocationRangeUnitIter<'ctx> {
1356 fn new<R: gimli::Reader>(
1357 resunit: &'ctx ResUnit<R>,
1358 sections: &gimli::Dwarf<R>,
1359 probe_low: u64,
1360 probe_high: u64,
1361 ) -> Result<Option<Self>, Error> {
1362 let lines = resunit.parse_lines(sections)?;
1363
1364 if let Some(lines) = lines {
1365 // Find index for probe_low.
1366 let seq_idx = lines.sequences.binary_search_by(|sequence| {
1367 if probe_low < sequence.start {
1368 Ordering::Greater
1369 } else if probe_low >= sequence.end {
1370 Ordering::Less
1371 } else {
1372 Ordering::Equal
1373 }
1374 });
1375 let seq_idx = match seq_idx {
1376 Ok(x) => x,
1377 Err(0) => 0, // probe below sequence, but range could overlap
1378 Err(_) => lines.sequences.len(),
1379 };
1380
1381 let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) {
1382 let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low));
1383 match idx {
1384 Ok(x) => x,
1385 Err(0) => 0, // probe below sequence, but range could overlap
1386 Err(x) => x - 1,
1387 }
1388 } else {
1389 0
1390 };
1391
1392 Ok(Some(Self {
1393 lines,
1394 seqs: &*lines.sequences,
1395 seq_idx,
1396 row_idx,
1397 probe_high,
1398 }))
1399 } else {
1400 Ok(None)
1401 }
1402 }
1403}
1404
1405impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> {
1406 type Item = (u64, u64, Location<'ctx>);
1407
1408 fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> {
1409 while let Some(seq) = self.seqs.get(self.seq_idx) {
1410 if seq.start >= self.probe_high {
1411 break;
1412 }
1413
1414 match seq.rows.get(self.row_idx) {
1415 Some(row) => {
1416 if row.address >= self.probe_high {
1417 break;
1418 }
1419
1420 let file = self
1421 .lines
1422 .files
1423 .get(row.file_index as usize)
1424 .map(String::as_str);
1425 let nextaddr = seq
1426 .rows
1427 .get(self.row_idx + 1)
1428 .map(|row| row.address)
1429 .unwrap_or(seq.end);
1430
1431 let item = (
1432 row.address,
1433 nextaddr - row.address,
1434 Location {
1435 file,
1436 line: if row.line != 0 { Some(row.line) } else { None },
1437 column: if row.column != 0 {
1438 Some(row.column)
1439 } else {
1440 None
1441 },
1442 },
1443 );
1444 self.row_idx += 1;
1445
1446 return Some(item);
1447 }
1448 None => {
1449 self.seq_idx += 1;
1450 self.row_idx = 0;
1451 }
1452 }
1453 }
1454 None
1455 }
1456}
1457
1458fn path_push(path: &mut String, p: &str) {
1459 if has_unix_root(p) || has_windows_root(p) {
1460 *path = p.to_string();
1461 } else {
1462 let dir_separator: char = if has_windows_root(path.as_str()) {
1463 '\\'
1464 } else {
1465 '/'
1466 };
1467
1468 if !path.is_empty() && !path.ends_with(dir_separator) {
1469 path.push(ch:dir_separator);
1470 }
1471 *path += p;
1472 }
1473}
1474
1475/// Check if the path in the given string has a unix style root
1476fn has_unix_root(p: &str) -> bool {
1477 p.starts_with('/')
1478}
1479
1480/// Check if the path in the given string has a windows style root
1481fn has_windows_root(p: &str) -> bool {
1482 p.starts_with('\\') || p.get(1..3) == Some(":\\")
1483}
1484struct RangeAttributes<R: gimli::Reader> {
1485 low_pc: Option<u64>,
1486 high_pc: Option<u64>,
1487 size: Option<u64>,
1488 ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
1489}
1490
1491impl<R: gimli::Reader> Default for RangeAttributes<R> {
1492 fn default() -> Self {
1493 RangeAttributes {
1494 low_pc: None,
1495 high_pc: None,
1496 size: None,
1497 ranges_offset: None,
1498 }
1499 }
1500}
1501
1502impl<R: gimli::Reader> RangeAttributes<R> {
1503 fn for_each_range<F: FnMut(gimli::Range)>(
1504 &self,
1505 sections: &gimli::Dwarf<R>,
1506 unit: &gimli::Unit<R>,
1507 mut f: F,
1508 ) -> Result<bool, Error> {
1509 let mut added_any = false;
1510 let mut add_range = |range: gimli::Range| {
1511 if range.begin < range.end {
1512 f(range);
1513 added_any = true
1514 }
1515 };
1516 if let Some(ranges_offset) = self.ranges_offset {
1517 let mut range_list = sections.ranges(unit, ranges_offset)?;
1518 while let Some(range) = range_list.next()? {
1519 add_range(range);
1520 }
1521 } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
1522 add_range(gimli::Range { begin, end });
1523 } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
1524 add_range(gimli::Range {
1525 begin,
1526 end: begin + size,
1527 });
1528 }
1529 Ok(added_any)
1530 }
1531}
1532
1533/// An iterator over function frames.
1534pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
1535where
1536 R: gimli::Reader;
1537
1538enum FrameIterState<'ctx, R>
1539where
1540 R: gimli::Reader,
1541{
1542 Empty,
1543 Location(Option<Location<'ctx>>),
1544 Frames(FrameIterFrames<'ctx, R>),
1545}
1546
1547struct FrameIterFrames<'ctx, R>
1548where
1549 R: gimli::Reader,
1550{
1551 unit: &'ctx ResUnit<R>,
1552 sections: &'ctx gimli::Dwarf<R>,
1553 function: &'ctx Function<R>,
1554 inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
1555 next: Option<Location<'ctx>>,
1556}
1557
1558impl<'ctx, R> FrameIter<'ctx, R>
1559where
1560 R: gimli::Reader + 'ctx,
1561{
1562 /// Advances the iterator and returns the next frame.
1563 pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
1564 let frames = match &mut self.0 {
1565 FrameIterState::Empty => return Ok(None),
1566 FrameIterState::Location(location) => {
1567 // We can't move out of a mutable reference, so use `take` instead.
1568 let location = location.take();
1569 self.0 = FrameIterState::Empty;
1570 return Ok(Some(Frame {
1571 dw_die_offset: None,
1572 function: None,
1573 location,
1574 }));
1575 }
1576 FrameIterState::Frames(frames) => frames,
1577 };
1578
1579 let loc = frames.next.take();
1580 let func = match frames.inlined_functions.next() {
1581 Some(func) => func,
1582 None => {
1583 let frame = Frame {
1584 dw_die_offset: Some(frames.function.dw_die_offset),
1585 function: frames.function.name.clone().map(|name| FunctionName {
1586 name,
1587 language: frames.unit.lang,
1588 }),
1589 location: loc,
1590 };
1591 self.0 = FrameIterState::Empty;
1592 return Ok(Some(frame));
1593 }
1594 };
1595
1596 let mut next = Location {
1597 file: None,
1598 line: if func.call_line != 0 {
1599 Some(func.call_line)
1600 } else {
1601 None
1602 },
1603 column: if func.call_column != 0 {
1604 Some(func.call_column)
1605 } else {
1606 None
1607 },
1608 };
1609 if let Some(call_file) = func.call_file {
1610 if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
1611 next.file = lines.files.get(call_file as usize).map(String::as_str);
1612 }
1613 }
1614 frames.next = Some(next);
1615
1616 Ok(Some(Frame {
1617 dw_die_offset: Some(func.dw_die_offset),
1618 function: func.name.clone().map(|name| FunctionName {
1619 name,
1620 language: frames.unit.lang,
1621 }),
1622 location: loc,
1623 }))
1624 }
1625}
1626
1627#[cfg(feature = "fallible-iterator")]
1628impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
1629where
1630 R: gimli::Reader + 'ctx,
1631{
1632 type Item = Frame<'ctx, R>;
1633 type Error = Error;
1634
1635 #[inline]
1636 fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
1637 self.next()
1638 }
1639}
1640
1641/// A function frame.
1642pub struct Frame<'ctx, R: gimli::Reader> {
1643 /// The DWARF unit offset corresponding to the DIE of the function.
1644 pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
1645 /// The name of the function.
1646 pub function: Option<FunctionName<R>>,
1647 /// The source location corresponding to this frame.
1648 pub location: Option<Location<'ctx>>,
1649}
1650
1651/// A function name.
1652pub struct FunctionName<R: gimli::Reader> {
1653 /// The name of the function.
1654 pub name: R,
1655 /// The language of the compilation unit containing this function.
1656 pub language: Option<gimli::DwLang>,
1657}
1658
1659impl<R: gimli::Reader> FunctionName<R> {
1660 /// The raw name of this function before demangling.
1661 pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> {
1662 self.name.to_string_lossy()
1663 }
1664
1665 /// The name of this function after demangling (if applicable).
1666 pub fn demangle(&self) -> Result<Cow<'_, str>, Error> {
1667 self.raw_name().map(|x: Cow<'_, str>| demangle_auto(name:x, self.language))
1668 }
1669}
1670
1671/// Demangle a symbol name using the demangling scheme for the given language.
1672///
1673/// Returns `None` if demangling failed or is not required.
1674#[allow(unused_variables)]
1675pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
1676 match language {
1677 #[cfg(feature = "rustc-demangle")]
1678 gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name)
1679 .ok()
1680 .as_ref()
1681 .map(|x| format!("{:#}", x)),
1682 #[cfg(feature = "cpp_demangle")]
1683 gimli::DW_LANG_C_plus_plus
1684 | gimli::DW_LANG_C_plus_plus_03
1685 | gimli::DW_LANG_C_plus_plus_11
1686 | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name)
1687 .ok()
1688 .and_then(|x| x.demangle(&Default::default()).ok()),
1689 _ => None,
1690 }
1691}
1692
1693/// Apply 'best effort' demangling of a symbol name.
1694///
1695/// If `language` is given, then only the demangling scheme for that language
1696/// is used.
1697///
1698/// If `language` is `None`, then heuristics are used to determine how to
1699/// demangle the name. Currently, these heuristics are very basic.
1700///
1701/// If demangling fails or is not required, then `name` is returned unchanged.
1702pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> {
1703 match language {
1704 Some(language) => demangle(name.as_ref(), language),
1705 None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
1706 .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
1707 }
1708 .map(Cow::from)
1709 .unwrap_or(default:name)
1710}
1711
1712/// A source location.
1713pub struct Location<'a> {
1714 /// The file name.
1715 pub file: Option<&'a str>,
1716 /// The line number.
1717 pub line: Option<u32>,
1718 /// The column number.
1719 pub column: Option<u32>,
1720}
1721
1722#[cfg(test)]
1723mod tests {
1724 #[test]
1725 fn context_is_send() {
1726 fn assert_is_send<T: Send>() {}
1727 assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>();
1728 }
1729}
1730