1#![deny(missing_docs)]
28#![no_std]
29
30#[cfg(feature = "cargo-all")]
31compile_error!("'--all-features' is not supported; use '--features all' instead");
32
33#[cfg(feature = "std")]
34extern crate std;
35
36#[allow(unused_imports)]
37#[macro_use]
38extern crate alloc;
39
40#[cfg(feature = "fallible-iterator")]
41pub extern crate fallible_iterator;
42pub extern crate gimli;
43
44use alloc::sync::Arc;
45use core::cell::OnceCell;
46use core::ops::ControlFlow;
47
48use crate::function::{Function, Functions, InlinedFunction, LazyFunctions};
49use crate::line::{LazyLines, LineLocationRangeIter, Lines};
50use crate::lookup::{LoopingLookup, SimpleLookup};
51use crate::unit::{ResUnit, ResUnits, SupUnits};
52
53#[cfg(feature = "smallvec")]
54mod maybe_small {
55 pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
56 pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
57}
58#[cfg(not(feature = "smallvec"))]
59mod maybe_small {
60 pub type Vec<T> = alloc::vec::Vec<T>;
61 pub type IntoIter<T> = alloc::vec::IntoIter<T>;
62}
63
64mod frame;
65pub use frame::{demangle, demangle_auto, Frame, FrameIter, FunctionName, Location};
66
67mod function;
68mod line;
69
70#[cfg(feature = "loader")]
71mod loader;
72#[cfg(feature = "loader")]
73pub use loader::{Loader, LoaderReader, Symbol};
74
75mod lookup;
76pub use lookup::{LookupContinuation, LookupResult, SplitDwarfLoad};
77
78mod unit;
79pub use unit::LocationRangeIter;
80
81type Error = gimli::Error;
82type LazyResult<T> = OnceCell<Result<T, Error>>;
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85enum DebugFile {
86 Primary,
87 Supplementary,
88 Dwo,
89}
90
91pub struct Context<R: gimli::Reader> {
96 sections: Arc<gimli::Dwarf<R>>,
97 units: ResUnits<R>,
98 sup_units: SupUnits<R>,
99}
100
101impl<R: gimli::Reader> Context<R> {
102 #[allow(clippy::too_many_arguments)]
106 pub fn from_sections(
107 debug_abbrev: gimli::DebugAbbrev<R>,
108 debug_addr: gimli::DebugAddr<R>,
109 debug_aranges: gimli::DebugAranges<R>,
110 debug_info: gimli::DebugInfo<R>,
111 debug_line: gimli::DebugLine<R>,
112 debug_line_str: gimli::DebugLineStr<R>,
113 debug_ranges: gimli::DebugRanges<R>,
114 debug_rnglists: gimli::DebugRngLists<R>,
115 debug_str: gimli::DebugStr<R>,
116 debug_str_offsets: gimli::DebugStrOffsets<R>,
117 default_section: R,
118 ) -> Result<Self, Error> {
119 Self::from_dwarf(gimli::Dwarf {
120 debug_abbrev,
121 debug_addr,
122 debug_aranges,
123 debug_info,
124 debug_line,
125 debug_line_str,
126 debug_macinfo: default_section.clone().into(),
127 debug_macro: default_section.clone().into(),
128 debug_str,
129 debug_str_offsets,
130 debug_types: default_section.clone().into(),
131 locations: gimli::LocationLists::new(
132 default_section.clone().into(),
133 default_section.into(),
134 ),
135 ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
136 file_type: gimli::DwarfFileType::Main,
137 sup: None,
138 abbreviations_cache: gimli::AbbreviationsCache::new(),
139 })
140 }
141
142 #[inline]
144 pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
145 Self::from_arc_dwarf(Arc::new(sections))
146 }
147
148 #[inline]
150 pub fn from_arc_dwarf(sections: Arc<gimli::Dwarf<R>>) -> Result<Context<R>, Error> {
151 let units = ResUnits::parse(§ions)?;
152 let sup_units = if let Some(sup) = sections.sup.as_ref() {
153 SupUnits::parse(sup)?
154 } else {
155 SupUnits::default()
156 };
157 Ok(Context {
158 sections,
159 units,
160 sup_units,
161 })
162 }
163}
164
165impl<R: gimli::Reader> Context<R> {
166 pub fn find_dwarf_and_unit(
168 &self,
169 probe: u64,
170 ) -> LookupResult<impl LookupContinuation<Output = Option<gimli::UnitRef<'_, R>>, Buf = R>>
171 {
172 let mut units_iter = self.units.find(probe);
173 if let Some(unit) = units_iter.next() {
174 return LoopingLookup::new_lookup(
175 unit.find_function_or_location(probe, self),
176 move |r| {
177 ControlFlow::Break(match r {
178 Ok((Some(_), _)) | Ok((_, Some(_))) => {
179 let (_file, unit) = unit
180 .dwarf_and_unit(self)
181 .unwrap()
183 .unwrap();
184 Some(unit)
185 }
186 _ => match units_iter.next() {
187 Some(next_unit) => {
188 return ControlFlow::Continue(
189 next_unit.find_function_or_location(probe, self),
190 );
191 }
192 None => None,
193 },
194 })
195 },
196 );
197 }
198
199 LoopingLookup::new_complete(None)
200 }
201
202 pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
204 for unit in self.units.find(probe) {
205 if let Some(location) = unit.find_location(probe, &self.sections)? {
206 return Ok(Some(location));
207 }
208 }
209 Ok(None)
210 }
211
212 pub fn find_location_range(
215 &self,
216 probe_low: u64,
217 probe_high: u64,
218 ) -> Result<LocationRangeIter<'_, R>, Error> {
219 self.units
220 .find_location_range(probe_low, probe_high, &self.sections)
221 }
222
223 pub fn find_frames(
233 &self,
234 probe: u64,
235 ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>>
236 {
237 let mut units_iter = self.units.find(probe);
238 if let Some(unit) = units_iter.next() {
239 LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| {
240 ControlFlow::Break(match r {
241 Err(e) => Err(e),
242 Ok((Some(function), location)) => {
243 let inlined_functions = function.find_inlined_functions(probe);
244 Ok(FrameIter::new_frames(
245 unit,
246 &self.sections,
247 function,
248 inlined_functions,
249 location,
250 ))
251 }
252 Ok((None, Some(location))) => Ok(FrameIter::new_location(location)),
253 Ok((None, None)) => match units_iter.next() {
254 Some(next_unit) => {
255 return ControlFlow::Continue(
256 next_unit.find_function_or_location(probe, self),
257 );
258 }
259 None => Ok(FrameIter::new_empty()),
260 },
261 })
262 })
263 } else {
264 LoopingLookup::new_complete(Ok(FrameIter::new_empty()))
265 }
266 }
267
268 pub fn preload_units(
297 &'_ self,
298 probe: u64,
299 ) -> impl Iterator<
300 Item = (
301 SplitDwarfLoad<R>,
302 impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_,
303 ),
304 > {
305 self.units
306 .find(probe)
307 .filter_map(move |unit| match unit.dwarf_and_unit(self) {
308 LookupResult::Output(_) => None,
309 LookupResult::Load { load, continuation } => Some((load, |result| {
310 continuation.resume(result).unwrap().map(|_| ())
311 })),
312 })
313 }
314
315 #[doc(hidden)]
317 pub fn parse_lines(&self) -> Result<(), Error> {
318 for unit in self.units.iter() {
319 unit.parse_lines(&self.sections)?;
320 }
321 Ok(())
322 }
323
324 #[doc(hidden)]
326 pub fn parse_functions(&self) -> Result<(), Error> {
327 for unit in self.units.iter() {
328 unit.parse_functions(self).skip_all_loads()?;
329 }
330 Ok(())
331 }
332
333 #[doc(hidden)]
335 pub fn parse_inlined_functions(&self) -> Result<(), Error> {
336 for unit in self.units.iter() {
337 unit.parse_inlined_functions(self).skip_all_loads()?;
338 }
339 Ok(())
340 }
341}
342
343impl<R: gimli::Reader> Context<R> {
344 fn find_unit(
346 &self,
347 offset: gimli::DebugInfoOffset<R::Offset>,
348 file: DebugFile,
349 ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> {
350 let unit = match file {
351 DebugFile::Primary => self.units.find_offset(offset)?,
352 DebugFile::Supplementary => self.sup_units.find_offset(offset)?,
353 DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset),
354 };
355
356 let unit_offset = offset
357 .to_unit_offset(&unit.header)
358 .ok_or(gimli::Error::NoEntryAtGivenOffset)?;
359 Ok((unit, unit_offset))
360 }
361}
362
363struct RangeAttributes<R: gimli::Reader> {
364 low_pc: Option<u64>,
365 high_pc: Option<u64>,
366 size: Option<u64>,
367 ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
368}
369
370impl<R: gimli::Reader> Default for RangeAttributes<R> {
371 fn default() -> Self {
372 RangeAttributes {
373 low_pc: None,
374 high_pc: None,
375 size: None,
376 ranges_offset: None,
377 }
378 }
379}
380
381impl<R: gimli::Reader> RangeAttributes<R> {
382 fn for_each_range<F: FnMut(gimli::Range)>(
383 &self,
384 unit: gimli::UnitRef<R>,
385 mut f: F,
386 ) -> Result<bool, Error> {
387 let mut added_any = false;
388 let mut add_range = |range: gimli::Range| {
389 if range.begin < range.end {
390 f(range);
391 added_any = true
392 }
393 };
394 if let Some(ranges_offset) = self.ranges_offset {
395 let mut range_list = unit.ranges(ranges_offset)?;
396 while let Some(range) = range_list.next()? {
397 add_range(range);
398 }
399 } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
400 add_range(gimli::Range { begin, end });
401 } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
402 let end = begin.wrapping_add(size);
405 add_range(gimli::Range { begin, end });
406 }
407 Ok(added_any)
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 #[test]
414 fn context_is_send() {
415 fn assert_is_send<T: Send>() {}
416 assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>();
417 }
418}