/rust/registry/src/index.crates.io-1949cf8c6b5b557f/addr2line-0.25.1/src/unit.rs
Line | Count | Source |
1 | | use alloc::boxed::Box; |
2 | | use alloc::sync::Arc; |
3 | | use alloc::vec::Vec; |
4 | | use core::cmp; |
5 | | |
6 | | use crate::{ |
7 | | Context, DebugFile, Error, Function, Functions, LazyFunctions, LazyLines, LazyResult, |
8 | | LineLocationRangeIter, Lines, Location, LookupContinuation, LookupResult, RangeAttributes, |
9 | | SimpleLookup, SplitDwarfLoad, |
10 | | }; |
11 | | |
12 | | pub(crate) struct UnitRange { |
13 | | unit_id: usize, |
14 | | min_begin: u64, |
15 | | range: gimli::Range, |
16 | | } |
17 | | |
18 | | pub(crate) struct ResUnit<R: gimli::Reader> { |
19 | | offset: gimli::DebugInfoOffset<R::Offset>, |
20 | | dw_unit: gimli::Unit<R>, |
21 | | pub(crate) lang: Option<gimli::DwLang>, |
22 | | lines: LazyLines, |
23 | | functions: LazyFunctions<R>, |
24 | | dwo: LazyResult<Option<Box<DwoUnit<R>>>>, |
25 | | } |
26 | | |
27 | | type UnitRef<'unit, R> = (DebugFile, gimli::UnitRef<'unit, R>); |
28 | | |
29 | | impl<R: gimli::Reader> ResUnit<R> { |
30 | 0 | pub(crate) fn unit_ref<'a>(&'a self, sections: &'a gimli::Dwarf<R>) -> gimli::UnitRef<'a, R> { |
31 | 0 | gimli::UnitRef::new(sections, &self.dw_unit) |
32 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::unit_ref Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::unit_ref |
33 | | |
34 | | /// Returns the DWARF sections and the unit. |
35 | | /// |
36 | | /// Loads the DWO unit if necessary. |
37 | 0 | pub(crate) fn dwarf_and_unit<'unit, 'ctx: 'unit>( |
38 | 0 | &'unit self, |
39 | 0 | ctx: &'ctx Context<R>, |
40 | 0 | ) -> LookupResult< |
41 | 0 | SimpleLookup< |
42 | 0 | Result<UnitRef<'unit, R>, Error>, |
43 | 0 | R, |
44 | 0 | impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<UnitRef<'unit, R>, Error>, |
45 | 0 | >, |
46 | 0 | > { |
47 | 0 | let map_dwo = move |dwo: &'unit Result<Option<Box<DwoUnit<R>>>, Error>| match dwo { |
48 | 0 | Ok(Some(dwo)) => Ok((DebugFile::Dwo, dwo.unit_ref())), |
49 | 0 | Ok(None) => Ok((DebugFile::Primary, self.unit_ref(&*ctx.sections))), |
50 | 0 | Err(e) => Err(*e), |
51 | 0 | }; Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#0}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#0} |
52 | 0 | let complete = |dwo| SimpleLookup::new_complete(map_dwo(dwo)); Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#1}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#1} |
53 | | |
54 | 0 | if let Some(dwo) = self.dwo.get() { |
55 | 0 | return complete(dwo); |
56 | 0 | } |
57 | | |
58 | 0 | let dwo_id = match self.dw_unit.dwo_id { |
59 | | None => { |
60 | 0 | return complete(self.dwo.get_or_init(|| Ok(None))); Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#2}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#2} |
61 | | } |
62 | 0 | Some(dwo_id) => dwo_id, |
63 | | }; |
64 | | |
65 | 0 | let comp_dir = self.dw_unit.comp_dir.clone(); |
66 | | |
67 | 0 | let dwo_name = self.dw_unit.dwo_name().and_then(|s| { |
68 | 0 | if let Some(s) = s { |
69 | 0 | Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?)) |
70 | | } else { |
71 | 0 | Ok(None) |
72 | | } |
73 | 0 | }); Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#3}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#3} |
74 | | |
75 | 0 | let path = match dwo_name { |
76 | 0 | Ok(v) => v, |
77 | 0 | Err(e) => { |
78 | 0 | return complete(self.dwo.get_or_init(|| Err(e))); Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#4}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#4} |
79 | | } |
80 | | }; |
81 | | |
82 | 0 | let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| { |
83 | 0 | let dwo_dwarf = match dwo_dwarf { |
84 | 0 | None => return Ok(None), |
85 | 0 | Some(dwo_dwarf) => dwo_dwarf, |
86 | | }; |
87 | 0 | let mut dwo_units = dwo_dwarf.units(); |
88 | 0 | let dwo_header = match dwo_units.next()? { |
89 | 0 | Some(dwo_header) => dwo_header, |
90 | 0 | None => return Ok(None), |
91 | | }; |
92 | | |
93 | 0 | let mut dwo_unit = dwo_dwarf.unit(dwo_header)?; |
94 | 0 | dwo_unit.copy_relocated_attributes(&self.dw_unit); |
95 | 0 | Ok(Some(Box::new(DwoUnit { |
96 | 0 | sections: dwo_dwarf, |
97 | 0 | dw_unit: dwo_unit, |
98 | 0 | }))) |
99 | 0 | }; Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#5}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#5} |
100 | | |
101 | 0 | SimpleLookup::new_needs_load( |
102 | 0 | SplitDwarfLoad { |
103 | 0 | dwo_id, |
104 | 0 | comp_dir, |
105 | 0 | path, |
106 | 0 | parent: ctx.sections.clone(), |
107 | 0 | }, |
108 | 0 | move |dwo_dwarf| map_dwo(self.dwo.get_or_init(|| process_dwo(dwo_dwarf))), Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#6}Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}::{closure#0}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit::{closure#6}::{closure#0} |
109 | | ) |
110 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::dwarf_and_unit |
111 | | |
112 | 0 | pub(crate) fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> { |
113 | | // NB: line information is always stored in the main debug file so this does not need |
114 | | // to handle DWOs. |
115 | 0 | let ilnp = match self.dw_unit.line_program { |
116 | 0 | Some(ref ilnp) => ilnp, |
117 | 0 | None => return Ok(None), |
118 | | }; |
119 | 0 | self.lines.borrow(self.unit_ref(sections), ilnp).map(Some) |
120 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::parse_lines Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::parse_lines |
121 | | |
122 | 0 | pub(crate) fn parse_functions<'unit, 'ctx: 'unit>( |
123 | 0 | &'unit self, |
124 | 0 | ctx: &'ctx Context<R>, |
125 | 0 | ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>> |
126 | | { |
127 | 0 | self.dwarf_and_unit(ctx).map(move |r| { |
128 | 0 | let (_file, unit) = r?; |
129 | 0 | self.functions.borrow(unit) |
130 | 0 | }) |
131 | 0 | } |
132 | | |
133 | 0 | pub(crate) fn parse_inlined_functions<'unit, 'ctx: 'unit>( |
134 | 0 | &'unit self, |
135 | 0 | ctx: &'ctx Context<R>, |
136 | 0 | ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> { |
137 | 0 | self.dwarf_and_unit(ctx).map(move |r| { |
138 | 0 | let (file, unit) = r?; |
139 | 0 | self.functions |
140 | 0 | .borrow(unit)? |
141 | 0 | .parse_inlined_functions(file, unit, ctx) |
142 | 0 | }) |
143 | 0 | } |
144 | | |
145 | 0 | pub(crate) fn find_location( |
146 | 0 | &self, |
147 | 0 | probe: u64, |
148 | 0 | sections: &gimli::Dwarf<R>, |
149 | 0 | ) -> Result<Option<Location<'_>>, Error> { |
150 | 0 | let Some(lines) = self.parse_lines(sections)? else { |
151 | 0 | return Ok(None); |
152 | | }; |
153 | 0 | lines.find_location(probe) |
154 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_location Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::find_location |
155 | | |
156 | | #[inline] |
157 | 0 | pub(crate) fn find_location_range( |
158 | 0 | &self, |
159 | 0 | probe_low: u64, |
160 | 0 | probe_high: u64, |
161 | 0 | sections: &gimli::Dwarf<R>, |
162 | 0 | ) -> Result<Option<LineLocationRangeIter<'_>>, Error> { |
163 | 0 | let Some(lines) = self.parse_lines(sections)? else { |
164 | 0 | return Ok(None); |
165 | | }; |
166 | 0 | lines.find_location_range(probe_low, probe_high).map(Some) |
167 | 0 | } |
168 | | |
169 | 0 | pub(crate) fn find_function_or_location<'unit, 'ctx: 'unit>( |
170 | 0 | &'unit self, |
171 | 0 | probe: u64, |
172 | 0 | ctx: &'ctx Context<R>, |
173 | 0 | ) -> LookupResult< |
174 | 0 | impl LookupContinuation< |
175 | 0 | Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>, |
176 | 0 | Buf = R, |
177 | 0 | >, |
178 | 0 | > { |
179 | 0 | self.dwarf_and_unit(ctx).map(move |r| { |
180 | 0 | let (file, unit) = r?; |
181 | 0 | let functions = self.functions.borrow(unit)?; |
182 | 0 | let function = match functions.find_address(probe) { |
183 | 0 | Some(address) => { |
184 | 0 | let function_index = functions.addresses[address].function; |
185 | 0 | let function = &functions.functions[function_index]; |
186 | 0 | Some(function.borrow(file, unit, ctx)?) |
187 | | } |
188 | 0 | None => None, |
189 | | }; |
190 | 0 | let location = self.find_location(probe, &ctx.sections)?; |
191 | 0 | Ok((function, location)) |
192 | 0 | }) Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::find_function_or_location::{closure#0} |
193 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location Unexecuted instantiation: <addr2line::unit::ResUnit<_>>::find_function_or_location |
194 | | } |
195 | | |
196 | | pub(crate) struct ResUnits<R: gimli::Reader> { |
197 | | ranges: Box<[UnitRange]>, |
198 | | units: Box<[ResUnit<R>]>, |
199 | | } |
200 | | |
201 | | impl<R: gimli::Reader> ResUnits<R> { |
202 | 0 | pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> { |
203 | | // Find all the references to compilation units in .debug_aranges. |
204 | | // Note that we always also iterate through all of .debug_info to |
205 | | // find compilation units, because .debug_aranges may be missing some. |
206 | 0 | let mut aranges = Vec::new(); |
207 | 0 | let mut headers = sections.debug_aranges.headers(); |
208 | 0 | while let Some(header) = headers.next()? { |
209 | 0 | aranges.push((header.debug_info_offset(), header.offset())); |
210 | 0 | } |
211 | 0 | aranges.sort_by_key(|i| i.0); |
212 | | |
213 | 0 | let mut unit_ranges = Vec::new(); |
214 | 0 | let mut res_units = Vec::new(); |
215 | 0 | let mut units = sections.units(); |
216 | 0 | while let Some(header) = units.next()? { |
217 | 0 | let unit_id = res_units.len(); |
218 | 0 | let offset = match header.offset().as_debug_info_offset() { |
219 | 0 | Some(offset) => offset, |
220 | 0 | None => continue, |
221 | | }; |
222 | | // We mainly want compile units, but we may need to follow references to entries |
223 | | // within other units for function names. We don't need anything from type units. |
224 | 0 | let mut need_unit_range = match header.type_() { |
225 | 0 | gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue, |
226 | | gimli::UnitType::Partial => { |
227 | | // Partial units are only needed for references from other units. |
228 | | // They shouldn't have any address ranges. |
229 | 0 | false |
230 | | } |
231 | 0 | _ => true, |
232 | | }; |
233 | 0 | let dw_unit = match sections.unit(header) { |
234 | 0 | Ok(dw_unit) => dw_unit, |
235 | 0 | Err(_) => continue, |
236 | | }; |
237 | 0 | let dw_unit_ref = gimli::UnitRef::new(sections, &dw_unit); |
238 | | |
239 | 0 | let mut lang = None; |
240 | 0 | if need_unit_range { |
241 | 0 | let mut entries = dw_unit_ref.entries_raw(None)?; |
242 | | |
243 | 0 | let abbrev = match entries.read_abbreviation()? { |
244 | 0 | Some(abbrev) => abbrev, |
245 | 0 | None => continue, |
246 | | }; |
247 | | |
248 | 0 | let mut ranges = RangeAttributes::default(); |
249 | 0 | for spec in abbrev.attributes() { |
250 | 0 | let attr = entries.read_attribute(*spec)?; |
251 | 0 | match attr.name() { |
252 | 0 | gimli::DW_AT_low_pc => match attr.value() { |
253 | 0 | gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val), |
254 | 0 | gimli::AttributeValue::DebugAddrIndex(index) => { |
255 | 0 | ranges.low_pc = Some(dw_unit_ref.address(index)?); |
256 | | } |
257 | 0 | _ => {} |
258 | | }, |
259 | 0 | gimli::DW_AT_high_pc => match attr.value() { |
260 | 0 | gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), |
261 | 0 | gimli::AttributeValue::DebugAddrIndex(index) => { |
262 | 0 | ranges.high_pc = Some(dw_unit_ref.address(index)?); |
263 | | } |
264 | 0 | gimli::AttributeValue::Udata(val) => ranges.size = Some(val), |
265 | 0 | _ => {} |
266 | | }, |
267 | | gimli::DW_AT_ranges => { |
268 | 0 | ranges.ranges_offset = dw_unit_ref.attr_ranges_offset(attr.value())?; |
269 | | } |
270 | | gimli::DW_AT_language => { |
271 | 0 | if let gimli::AttributeValue::Language(val) = attr.value() { |
272 | 0 | lang = Some(val); |
273 | 0 | } |
274 | | } |
275 | 0 | _ => {} |
276 | | } |
277 | | } |
278 | | |
279 | | // Find the address ranges for the CU, using in order of preference: |
280 | | // - DW_AT_ranges |
281 | | // - .debug_aranges |
282 | | // - DW_AT_low_pc/DW_AT_high_pc |
283 | | // |
284 | | // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice, |
285 | | // but the feeling is that DW_AT_ranges is more likely to be reliable or complete |
286 | | // if it is present. |
287 | | // |
288 | | // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because |
289 | | // it has been observed on macOS that DW_AT_ranges was not emitted even for |
290 | | // discontiguous CUs. |
291 | 0 | let i = match ranges.ranges_offset { |
292 | 0 | Some(_) => None, |
293 | 0 | None => aranges.binary_search_by_key(&offset, |x| x.0).ok(), |
294 | | }; |
295 | 0 | if let Some(mut i) = i { |
296 | | // There should be only one set per CU, but in practice multiple |
297 | | // sets have been observed. This is probably a compiler bug, but |
298 | | // either way we need to handle it. |
299 | 0 | while i > 0 && aranges[i - 1].0 == offset { |
300 | 0 | i -= 1; |
301 | 0 | } |
302 | 0 | for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) {Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::parse::{closure#2}Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::parse::{closure#2} |
303 | 0 | let aranges_header = sections.debug_aranges.header(*aranges_offset)?; |
304 | 0 | let mut aranges = aranges_header.entries(); |
305 | 0 | while let Some(arange) = aranges.next().transpose() { |
306 | 0 | let Ok(arange) = arange else { |
307 | | // Ignore errors. In particular, this will ignore address overflow. |
308 | | // This has been seen for a unit that had a single variable |
309 | | // with rustc 1.89.0. |
310 | | // |
311 | | // This relies on `ArangeEntryIter::next` fusing for errors that |
312 | | // can't be ignored. |
313 | 0 | continue; |
314 | | }; |
315 | 0 | if arange.length() != 0 { |
316 | 0 | unit_ranges.push(UnitRange { |
317 | 0 | range: arange.range(), |
318 | 0 | unit_id, |
319 | 0 | min_begin: 0, |
320 | 0 | }); |
321 | 0 | need_unit_range = false; |
322 | 0 | } |
323 | | } |
324 | | } |
325 | 0 | } |
326 | 0 | if need_unit_range { |
327 | 0 | need_unit_range = !ranges.for_each_range(dw_unit_ref, |range| { |
328 | 0 | unit_ranges.push(UnitRange { |
329 | 0 | range, |
330 | 0 | unit_id, |
331 | 0 | min_begin: 0, |
332 | 0 | }); |
333 | 0 | })?; Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::parse::{closure#3}Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::parse::{closure#3} |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | 0 | let lines = LazyLines::new(); |
338 | 0 | if need_unit_range { |
339 | | // The unit did not declare any ranges. |
340 | | // Try to get some ranges from the line program sequences. |
341 | 0 | if let Some(ref ilnp) = dw_unit_ref.line_program { |
342 | 0 | if let Ok(lines) = lines.borrow(dw_unit_ref, ilnp) { |
343 | 0 | for range in lines.ranges() { |
344 | 0 | unit_ranges.push(UnitRange { |
345 | 0 | range, |
346 | 0 | unit_id, |
347 | 0 | min_begin: 0, |
348 | 0 | }) |
349 | | } |
350 | 0 | } |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | 0 | res_units.push(ResUnit { |
355 | 0 | offset, |
356 | 0 | dw_unit, |
357 | 0 | lang, |
358 | 0 | lines, |
359 | 0 | functions: LazyFunctions::new(), |
360 | 0 | dwo: LazyResult::new(), |
361 | 0 | }); |
362 | | } |
363 | | |
364 | | // Sort this for faster lookup in `Self::find_range`. |
365 | 0 | unit_ranges.sort_by_key(|i| i.range.end); |
366 | | |
367 | | // Calculate the `min_begin` field now that we've determined the order of |
368 | | // CUs. |
369 | 0 | let mut min = !0; |
370 | 0 | for i in unit_ranges.iter_mut().rev() { |
371 | 0 | min = min.min(i.range.begin); |
372 | 0 | i.min_begin = min; |
373 | 0 | } |
374 | | |
375 | 0 | Ok(ResUnits { |
376 | 0 | ranges: unit_ranges.into_boxed_slice(), |
377 | 0 | units: res_units.into_boxed_slice(), |
378 | 0 | }) |
379 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::parse Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::parse |
380 | | |
381 | 0 | pub(crate) fn iter(&self) -> impl Iterator<Item = &ResUnit<R>> { |
382 | 0 | self.units.iter() |
383 | 0 | } |
384 | | |
385 | 0 | pub(crate) fn find_offset( |
386 | 0 | &self, |
387 | 0 | offset: gimli::DebugInfoOffset<R::Offset>, |
388 | 0 | ) -> Result<&gimli::Unit<R>, Error> { |
389 | 0 | match self |
390 | 0 | .units |
391 | 0 | .binary_search_by_key(&offset.0, |unit| unit.offset.0) |
392 | | { |
393 | | // There is never a DIE at the unit offset or before the first unit. |
394 | 0 | Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset), |
395 | 0 | Err(i) => Ok(&self.units[i - 1].dw_unit), |
396 | | } |
397 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_offset Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::find_offset |
398 | | |
399 | | /// Finds the CUs for the function address given. |
400 | | /// |
401 | | /// There might be multiple CUs whose range contains this address. |
402 | | /// Weak symbols have shown up in the wild which cause this to happen |
403 | | /// but otherwise this can happen if the CU has non-contiguous functions |
404 | | /// but only reports a single range. |
405 | | /// |
406 | | /// Consequently we return an iterator for all CUs which may contain the |
407 | | /// address, and the caller must check if there is actually a function or |
408 | | /// location in the CU for that address. |
409 | 0 | pub(crate) fn find(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> { |
410 | 0 | self.find_range(probe, probe + 1).map(|(unit, _range)| unit) |
411 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::find |
412 | | |
413 | | /// Finds the CUs covering the range of addresses given. |
414 | | /// |
415 | | /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple |
416 | | /// ranges for the same unit. |
417 | | #[inline] |
418 | 0 | pub(crate) fn find_range( |
419 | 0 | &self, |
420 | 0 | probe_low: u64, |
421 | 0 | probe_high: u64, |
422 | 0 | ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> { |
423 | | // Find the position of the next range after a range which |
424 | | // ends at `probe_low` or lower. |
425 | 0 | let pos = match self |
426 | 0 | .ranges |
427 | 0 | .binary_search_by_key(&probe_low, |i| i.range.end) |
428 | | { |
429 | 0 | Ok(i) => i + 1, // Range `i` ends at exactly `probe_low`. |
430 | 0 | Err(i) => i, // Range `i - 1` ends at a lower address. |
431 | | }; |
432 | | |
433 | | // Iterate from that position to find matching CUs. |
434 | 0 | self.ranges[pos..] |
435 | 0 | .iter() |
436 | 0 | .take_while(move |i| { |
437 | | // We know that this CU's end is at least `probe_low` because |
438 | | // of our sorted array. |
439 | 0 | debug_assert!(i.range.end >= probe_low); |
440 | | |
441 | | // Each entry keeps track of the minimum begin address for the |
442 | | // remainder of the array of unit ranges. If our probe is before |
443 | | // the minimum range begin of this entry, then it's guaranteed |
444 | | // to not fit in any subsequent entries, so we break out. |
445 | 0 | probe_high > i.min_begin |
446 | 0 | }) Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_range::{closure#1}Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::find_range::{closure#1} |
447 | 0 | .filter_map(move |i| { |
448 | | // If this CU doesn't actually contain this address, move to the |
449 | | // next CU. |
450 | 0 | if probe_low >= i.range.end || probe_high <= i.range.begin { |
451 | 0 | return None; |
452 | 0 | } |
453 | 0 | Some((&self.units[i.unit_id], &i.range)) |
454 | 0 | }) Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_range::{closure#2}Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::find_range::{closure#2} |
455 | 0 | } Unexecuted instantiation: <addr2line::unit::ResUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_range Unexecuted instantiation: <addr2line::unit::ResUnits<_>>::find_range |
456 | | |
457 | 0 | pub(crate) fn find_location_range<'a>( |
458 | 0 | &'a self, |
459 | 0 | probe_low: u64, |
460 | 0 | probe_high: u64, |
461 | 0 | sections: &'a gimli::Dwarf<R>, |
462 | 0 | ) -> Result<LocationRangeIter<'a, R>, Error> { |
463 | 0 | let unit_iter = Box::new(self.find_range(probe_low, probe_high)); |
464 | 0 | Ok(LocationRangeIter { |
465 | 0 | unit_iter, |
466 | 0 | iter: None, |
467 | 0 | probe_low, |
468 | 0 | probe_high, |
469 | 0 | sections, |
470 | 0 | }) |
471 | 0 | } |
472 | | } |
473 | | |
474 | | /// A DWO unit has its own DWARF sections. |
475 | | struct DwoUnit<R: gimli::Reader> { |
476 | | sections: Arc<gimli::Dwarf<R>>, |
477 | | dw_unit: gimli::Unit<R>, |
478 | | } |
479 | | |
480 | | impl<R: gimli::Reader> DwoUnit<R> { |
481 | 0 | fn unit_ref(&self) -> gimli::UnitRef<'_, R> { |
482 | 0 | gimli::UnitRef::new(&self.sections, &self.dw_unit) |
483 | 0 | } Unexecuted instantiation: <addr2line::unit::DwoUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::unit_ref Unexecuted instantiation: <addr2line::unit::DwoUnit<_>>::unit_ref |
484 | | } |
485 | | |
486 | | pub(crate) struct SupUnit<R: gimli::Reader> { |
487 | | offset: gimli::DebugInfoOffset<R::Offset>, |
488 | | dw_unit: gimli::Unit<R>, |
489 | | } |
490 | | |
491 | | pub(crate) struct SupUnits<R: gimli::Reader> { |
492 | | units: Box<[SupUnit<R>]>, |
493 | | } |
494 | | |
495 | | impl<R: gimli::Reader> Default for SupUnits<R> { |
496 | 0 | fn default() -> Self { |
497 | 0 | SupUnits { |
498 | 0 | units: Box::default(), |
499 | 0 | } |
500 | 0 | } Unexecuted instantiation: <addr2line::unit::SupUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>> as core::default::Default>::default Unexecuted instantiation: <addr2line::unit::SupUnits<_> as core::default::Default>::default |
501 | | } |
502 | | |
503 | | impl<R: gimli::Reader> SupUnits<R> { |
504 | 0 | pub(crate) fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> { |
505 | 0 | let mut sup_units = Vec::new(); |
506 | 0 | let mut units = sections.units(); |
507 | 0 | while let Some(header) = units.next()? { |
508 | 0 | let offset = match header.offset().as_debug_info_offset() { |
509 | 0 | Some(offset) => offset, |
510 | 0 | None => continue, |
511 | | }; |
512 | 0 | let dw_unit = match sections.unit(header) { |
513 | 0 | Ok(dw_unit) => dw_unit, |
514 | 0 | Err(_) => continue, |
515 | | }; |
516 | 0 | sup_units.push(SupUnit { dw_unit, offset }); |
517 | | } |
518 | 0 | Ok(SupUnits { |
519 | 0 | units: sup_units.into_boxed_slice(), |
520 | 0 | }) |
521 | 0 | } Unexecuted instantiation: <addr2line::unit::SupUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::parse Unexecuted instantiation: <addr2line::unit::SupUnits<_>>::parse |
522 | | |
523 | 0 | pub(crate) fn find_offset( |
524 | 0 | &self, |
525 | 0 | offset: gimli::DebugInfoOffset<R::Offset>, |
526 | 0 | ) -> Result<&gimli::Unit<R>, Error> { |
527 | 0 | match self |
528 | 0 | .units |
529 | 0 | .binary_search_by_key(&offset.0, |unit| unit.offset.0) |
530 | | { |
531 | | // There is never a DIE at the unit offset or before the first unit. |
532 | 0 | Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset), |
533 | 0 | Err(i) => Ok(&self.units[i - 1].dw_unit), |
534 | | } |
535 | 0 | } Unexecuted instantiation: <addr2line::unit::SupUnits<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_offset Unexecuted instantiation: <addr2line::unit::SupUnits<_>>::find_offset |
536 | | } |
537 | | |
538 | | /// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`. |
539 | | pub struct LocationRangeIter<'ctx, R: gimli::Reader> { |
540 | | unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>, |
541 | | iter: Option<LineLocationRangeIter<'ctx>>, |
542 | | |
543 | | probe_low: u64, |
544 | | probe_high: u64, |
545 | | sections: &'ctx gimli::Dwarf<R>, |
546 | | } |
547 | | |
548 | | impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> { |
549 | 0 | fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> { |
550 | | loop { |
551 | 0 | let iter = self.iter.take(); |
552 | 0 | match iter { |
553 | 0 | None => match self.unit_iter.next() { |
554 | 0 | Some((unit, range)) => { |
555 | 0 | self.iter = unit.find_location_range( |
556 | 0 | cmp::max(self.probe_low, range.begin), |
557 | 0 | cmp::min(self.probe_high, range.end), |
558 | 0 | self.sections, |
559 | 0 | )?; |
560 | | } |
561 | 0 | None => return Ok(None), |
562 | | }, |
563 | 0 | Some(mut iter) => { |
564 | 0 | if let item @ Some(_) = iter.next() { |
565 | 0 | self.iter = Some(iter); |
566 | 0 | return Ok(item); |
567 | 0 | } |
568 | | } |
569 | | } |
570 | | } |
571 | 0 | } |
572 | | } |
573 | | |
574 | | impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R> |
575 | | where |
576 | | R: gimli::Reader + 'ctx, |
577 | | { |
578 | | type Item = (u64, u64, Location<'ctx>); |
579 | | |
580 | | #[inline] |
581 | 0 | fn next(&mut self) -> Option<Self::Item> { |
582 | 0 | self.next_loc().unwrap_or_default() |
583 | 0 | } |
584 | | } |
585 | | |
586 | | #[cfg(feature = "fallible-iterator")] |
587 | | impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R> |
588 | | where |
589 | | R: gimli::Reader + 'ctx, |
590 | | { |
591 | | type Item = (u64, u64, Location<'ctx>); |
592 | | type Error = Error; |
593 | | |
594 | | #[inline] |
595 | | fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> { |
596 | | self.next_loc() |
597 | | } |
598 | | } |