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