1use alloc::boxed::Box;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4use core::cmp;
5
6use crate::lazy::LazyResult;
7use crate::{
8 Context, DebugFile, Error, Function, Functions, LazyFunctions, LazyLines,
9 LineLocationRangeIter, Lines, Location, LookupContinuation, LookupResult, RangeAttributes,
10 SimpleLookup, SplitDwarfLoad,
11};
12
13pub(crate) struct UnitRange {
14 unit_id: usize,
15 min_begin: u64,
16 range: gimli::Range,
17}
18
19pub(crate) struct ResUnit<R: gimli::Reader> {
20 offset: gimli::DebugInfoOffset<R::Offset>,
21 dw_unit: gimli::Unit<R>,
22 pub(crate) lang: Option<gimli::DwLang>,
23 lines: LazyLines,
24 functions: LazyFunctions<R>,
25 dwo: LazyResult<Option<Box<DwoUnit<R>>>>,
26}
27
28type UnitRef<'unit, R> = (DebugFile, gimli::UnitRef<'unit, R>);
29
30impl<R: gimli::Reader> ResUnit<R> {
31 pub(crate) fn unit_ref<'a>(&'a self, sections: &'a gimli::Dwarf<R>) -> gimli::UnitRef<'a, R> {
32 gimli::UnitRef::new(sections, &self.dw_unit)
33 }
34
35 pub(crate) fn dwarf_and_unit<'unit, 'ctx: 'unit>(
39 &'unit self,
40 ctx: &'ctx Context<R>,
41 ) -> LookupResult<
42 SimpleLookup<
43 Result<UnitRef<'unit, R>, Error>,
44 R,
45 impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<UnitRef<'unit, R>, Error>,
46 >,
47 > {
48 let map_dwo = move |dwo: &'unit Result<Option<Box<DwoUnit<R>>>, Error>| match dwo {
49 Ok(Some(dwo)) => Ok((DebugFile::Dwo, dwo.unit_ref())),
50 Ok(None) => Ok((DebugFile::Primary, self.unit_ref(&*ctx.sections))),
51 Err(e) => Err(*e),
52 };
53 let complete = |dwo| SimpleLookup::new_complete(map_dwo(dwo));
54
55 if let Some(dwo) = self.dwo.borrow() {
56 return complete(dwo);
57 }
58
59 let dwo_id = match self.dw_unit.dwo_id {
60 None => {
61 return complete(self.dwo.borrow_with(|| Ok(None)));
62 }
63 Some(dwo_id) => dwo_id,
64 };
65
66 let comp_dir = self.dw_unit.comp_dir.clone();
67
68 let dwo_name = self.dw_unit.dwo_name().and_then(|s| {
69 if let Some(s) = s {
70 Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?))
71 } else {
72 Ok(None)
73 }
74 });
75
76 let path = match dwo_name {
77 Ok(v) => v,
78 Err(e) => {
79 return complete(self.dwo.borrow_with(|| Err(e)));
80 }
81 };
82
83 let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| {
84 let dwo_dwarf = match dwo_dwarf {
85 None => return Ok(None),
86 Some(dwo_dwarf) => dwo_dwarf,
87 };
88 let mut dwo_units = dwo_dwarf.units();
89 let dwo_header = match dwo_units.next()? {
90 Some(dwo_header) => dwo_header,
91 None => return Ok(None),
92 };
93
94 let mut dwo_unit = dwo_dwarf.unit(dwo_header)?;
95 dwo_unit.copy_relocated_attributes(&self.dw_unit);
96 Ok(Some(Box::new(DwoUnit {
97 sections: dwo_dwarf,
98 dw_unit: dwo_unit,
99 })))
100 };
101
102 SimpleLookup::new_needs_load(
103 SplitDwarfLoad {
104 dwo_id,
105 comp_dir,
106 path,
107 parent: ctx.sections.clone(),
108 },
109 move |dwo_dwarf| map_dwo(self.dwo.borrow_with(|| process_dwo(dwo_dwarf))),
110 )
111 }
112
113 pub(crate) fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> {
114 let ilnp = match self.dw_unit.line_program {
117 Some(ref ilnp) => ilnp,
118 None => return Ok(None),
119 };
120 self.lines.borrow(self.unit_ref(sections), ilnp).map(Some)
121 }
122
123 pub(crate) fn parse_functions<'unit, 'ctx: 'unit>(
124 &'unit self,
125 ctx: &'ctx Context<R>,
126 ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>>
127 {
128 self.dwarf_and_unit(ctx).map(move |r| {
129 let (_file, unit) = r?;
130 self.functions.borrow(unit)
131 })
132 }
133
134 pub(crate) fn parse_inlined_functions<'unit, 'ctx: 'unit>(
135 &'unit self,
136 ctx: &'ctx Context<R>,
137 ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> {
138 self.dwarf_and_unit(ctx).map(move |r| {
139 let (file, unit) = r?;
140 self.functions
141 .borrow(unit)?
142 .parse_inlined_functions(file, unit, ctx)
143 })
144 }
145
146 pub(crate) fn find_location(
147 &self,
148 probe: u64,
149 sections: &gimli::Dwarf<R>,
150 ) -> Result<Option<Location<'_>>, Error> {
151 let Some(lines) = self.parse_lines(sections)? else {
152 return Ok(None);
153 };
154 lines.find_location(probe)
155 }
156
157 #[inline]
158 pub(crate) fn find_location_range(
159 &self,
160 probe_low: u64,
161 probe_high: u64,
162 sections: &gimli::Dwarf<R>,
163 ) -> Result<Option<LineLocationRangeIter<'_>>, Error> {
164 let Some(lines) = self.parse_lines(sections)? else {
165 return Ok(None);
166 };
167 lines.find_location_range(probe_low, probe_high).map(Some)
168 }
169
170 pub(crate) fn find_function_or_location<'unit, 'ctx: 'unit>(
171 &'unit self,
172 probe: u64,
173 ctx: &'ctx Context<R>,
174 ) -> LookupResult<
175 impl LookupContinuation<
176 Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>,
177 Buf = R,
178 >,
179 > {
180 self.dwarf_and_unit(ctx).map(move |r| {
181 let (file, unit) = r?;
182 let functions = self.functions.borrow(unit)?;
183 let function = match functions.find_address(probe) {
184 Some(address) => {
185 let function_index = functions.addresses[address].function;
186 let function = &functions.functions[function_index];
187 Some(function.borrow(file, unit, ctx)?)
188 }
189 None => None,
190 };
191 let location = self.find_location(probe, unit.dwarf)?;
192 Ok((function, location))
193 })
194 }
195}
196
197pub(crate) struct ResUnits<R: gimli::Reader> {
198 ranges: Box<[UnitRange]>,
199 units: Box<[ResUnit<R>]>,
200}
201
202impl<R: gimli::Reader> ResUnits<R> {
203 pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> {
204 let mut aranges = Vec::new();
208 let mut headers = sections.debug_aranges.headers();
209 while let Some(header) = headers.next()? {
210 aranges.push((header.debug_info_offset(), header.offset()));
211 }
212 aranges.sort_by_key(|i| i.0);
213
214 let mut unit_ranges = Vec::new();
215 let mut res_units = Vec::new();
216 let mut units = sections.units();
217 while let Some(header) = units.next()? {
218 let unit_id = res_units.len();
219 let offset = match header.offset().as_debug_info_offset() {
220 Some(offset) => offset,
221 None => continue,
222 };
223 let mut need_unit_range = match header.type_() {
226 gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue,
227 gimli::UnitType::Partial => {
228 false
231 }
232 _ => true,
233 };
234 let dw_unit = match sections.unit(header) {
235 Ok(dw_unit) => dw_unit,
236 Err(_) => continue,
237 };
238 let dw_unit_ref = gimli::UnitRef::new(sections, &dw_unit);
239
240 let mut lang = None;
241 if need_unit_range {
242 let mut entries = dw_unit_ref.entries_raw(None)?;
243
244 let abbrev = match entries.read_abbreviation()? {
245 Some(abbrev) => abbrev,
246 None => continue,
247 };
248
249 let mut ranges = RangeAttributes::default();
250 for spec in abbrev.attributes() {
251 let attr = entries.read_attribute(*spec)?;
252 match attr.name() {
253 gimli::DW_AT_low_pc => match attr.value() {
254 gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val),
255 gimli::AttributeValue::DebugAddrIndex(index) => {
256 ranges.low_pc = Some(dw_unit_ref.address(index)?);
257 }
258 _ => {}
259 },
260 gimli::DW_AT_high_pc => match attr.value() {
261 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
262 gimli::AttributeValue::DebugAddrIndex(index) => {
263 ranges.high_pc = Some(dw_unit_ref.address(index)?);
264 }
265 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
266 _ => {}
267 },
268 gimli::DW_AT_ranges => {
269 ranges.ranges_offset = dw_unit_ref.attr_ranges_offset(attr.value())?;
270 }
271 gimli::DW_AT_language => {
272 if let gimli::AttributeValue::Language(val) = attr.value() {
273 lang = Some(val);
274 }
275 }
276 _ => {}
277 }
278 }
279
280 let i = match ranges.ranges_offset {
293 Some(_) => None,
294 None => aranges.binary_search_by_key(&offset, |x| x.0).ok(),
295 };
296 if let Some(mut i) = i {
297 while i > 0 && aranges[i - 1].0 == offset {
301 i -= 1;
302 }
303 for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) {
304 let aranges_header = sections.debug_aranges.header(*aranges_offset)?;
305 let mut aranges = aranges_header.entries();
306 while let Some(arange) = aranges.next()? {
307 if arange.length() != 0 {
308 unit_ranges.push(UnitRange {
309 range: arange.range(),
310 unit_id,
311 min_begin: 0,
312 });
313 need_unit_range = false;
314 }
315 }
316 }
317 } else {
318 need_unit_range &= !ranges.for_each_range(dw_unit_ref, |range| {
319 unit_ranges.push(UnitRange {
320 range,
321 unit_id,
322 min_begin: 0,
323 });
324 })?;
325 }
326 }
327
328 let lines = LazyLines::new();
329 if need_unit_range {
330 if let Some(ref ilnp) = dw_unit_ref.line_program {
333 if let Ok(lines) = lines.borrow(dw_unit_ref, ilnp) {
334 for range in lines.ranges() {
335 unit_ranges.push(UnitRange {
336 range,
337 unit_id,
338 min_begin: 0,
339 })
340 }
341 }
342 }
343 }
344
345 res_units.push(ResUnit {
346 offset,
347 dw_unit,
348 lang,
349 lines,
350 functions: LazyFunctions::new(),
351 dwo: LazyResult::new(),
352 });
353 }
354
355 unit_ranges.sort_by_key(|i| i.range.end);
357
358 let mut min = !0;
361 for i in unit_ranges.iter_mut().rev() {
362 min = min.min(i.range.begin);
363 i.min_begin = min;
364 }
365
366 Ok(ResUnits {
367 ranges: unit_ranges.into_boxed_slice(),
368 units: res_units.into_boxed_slice(),
369 })
370 }
371
372 pub(crate) fn iter(&self) -> impl Iterator<Item = &ResUnit<R>> {
373 self.units.iter()
374 }
375
376 pub(crate) fn find_offset(
377 &self,
378 offset: gimli::DebugInfoOffset<R::Offset>,
379 ) -> Result<&gimli::Unit<R>, Error> {
380 match self
381 .units
382 .binary_search_by_key(&offset.0, |unit| unit.offset.0)
383 {
384 Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset),
386 Err(i) => Ok(&self.units[i - 1].dw_unit),
387 }
388 }
389
390 pub(crate) fn find(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> {
401 self.find_range(probe, probe + 1).map(|(unit, _range)| unit)
402 }
403
404 #[inline]
409 pub(crate) fn find_range(
410 &self,
411 probe_low: u64,
412 probe_high: u64,
413 ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> {
414 let pos = match self
417 .ranges
418 .binary_search_by_key(&probe_low, |i| i.range.end)
419 {
420 Ok(i) => i + 1, Err(i) => i, };
423
424 self.ranges[pos..]
426 .iter()
427 .take_while(move |i| {
428 debug_assert!(i.range.end >= probe_low);
431
432 probe_high > i.min_begin
437 })
438 .filter_map(move |i| {
439 if probe_low >= i.range.end || probe_high <= i.range.begin {
442 return None;
443 }
444 Some((&self.units[i.unit_id], &i.range))
445 })
446 }
447
448 pub(crate) fn find_location_range<'a>(
449 &'a self,
450 probe_low: u64,
451 probe_high: u64,
452 sections: &'a gimli::Dwarf<R>,
453 ) -> Result<LocationRangeIter<'a, R>, Error> {
454 let unit_iter = Box::new(self.find_range(probe_low, probe_high));
455 Ok(LocationRangeIter {
456 unit_iter,
457 iter: None,
458 probe_low,
459 probe_high,
460 sections,
461 })
462 }
463}
464
465struct DwoUnit<R: gimli::Reader> {
467 sections: Arc<gimli::Dwarf<R>>,
468 dw_unit: gimli::Unit<R>,
469}
470
471impl<R: gimli::Reader> DwoUnit<R> {
472 fn unit_ref(&self) -> gimli::UnitRef<R> {
473 gimli::UnitRef::new(&self.sections, &self.dw_unit)
474 }
475}
476
477pub(crate) struct SupUnit<R: gimli::Reader> {
478 offset: gimli::DebugInfoOffset<R::Offset>,
479 dw_unit: gimli::Unit<R>,
480}
481
482pub(crate) struct SupUnits<R: gimli::Reader> {
483 units: Box<[SupUnit<R>]>,
484}
485
486impl<R: gimli::Reader> Default for SupUnits<R> {
487 fn default() -> Self {
488 SupUnits {
489 units: Box::default(),
490 }
491 }
492}
493
494impl<R: gimli::Reader> SupUnits<R> {
495 pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> {
496 let mut sup_units = Vec::new();
497 let mut units = sections.units();
498 while let Some(header) = units.next()? {
499 let offset = match header.offset().as_debug_info_offset() {
500 Some(offset) => offset,
501 None => continue,
502 };
503 let dw_unit = match sections.unit(header) {
504 Ok(dw_unit) => dw_unit,
505 Err(_) => continue,
506 };
507 sup_units.push(SupUnit { dw_unit, offset });
508 }
509 Ok(SupUnits {
510 units: sup_units.into_boxed_slice(),
511 })
512 }
513
514 pub(crate) fn find_offset(
515 &self,
516 offset: gimli::DebugInfoOffset<R::Offset>,
517 ) -> Result<&gimli::Unit<R>, Error> {
518 match self
519 .units
520 .binary_search_by_key(&offset.0, |unit| unit.offset.0)
521 {
522 Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset),
524 Err(i) => Ok(&self.units[i - 1].dw_unit),
525 }
526 }
527}
528
529pub struct LocationRangeIter<'ctx, R: gimli::Reader> {
531 unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>,
532 iter: Option<LineLocationRangeIter<'ctx>>,
533
534 probe_low: u64,
535 probe_high: u64,
536 sections: &'ctx gimli::Dwarf<R>,
537}
538
539impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> {
540 fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> {
541 loop {
542 let iter = self.iter.take();
543 match iter {
544 None => match self.unit_iter.next() {
545 Some((unit, range)) => {
546 self.iter = unit.find_location_range(
547 cmp::max(self.probe_low, range.begin),
548 cmp::min(self.probe_high, range.end),
549 self.sections,
550 )?;
551 }
552 None => return Ok(None),
553 },
554 Some(mut iter) => {
555 if let item @ Some(_) = iter.next() {
556 self.iter = Some(iter);
557 return Ok(item);
558 }
559 }
560 }
561 }
562 }
563}
564
565impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R>
566where
567 R: gimli::Reader + 'ctx,
568{
569 type Item = (u64, u64, Location<'ctx>);
570
571 #[inline]
572 fn next(&mut self) -> Option<Self::Item> {
573 self.next_loc().unwrap_or_default()
574 }
575}
576
577#[cfg(feature = "fallible-iterator")]
578impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R>
579where
580 R: gimli::Reader + 'ctx,
581{
582 type Item = (u64, u64, Location<'ctx>);
583 type Error = Error;
584
585 #[inline]
586 fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
587 self.next_loc()
588 }
589}