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