]> git.proxmox.com Git - rustc.git/blob - vendor/addr2line/src/function.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / vendor / addr2line / src / function.rs
1 use alloc::boxed::Box;
2 use alloc::vec::Vec;
3 use core::cmp::Ordering;
4 use core::iter;
5
6 use crate::lazy::LazyCell;
7 use crate::maybe_small;
8 use crate::{Error, RangeAttributes, ResDwarf};
9
10 pub(crate) struct Functions<R: gimli::Reader> {
11 /// List of all `DW_TAG_subprogram` details in the unit.
12 pub(crate) functions: Box<
13 [(
14 gimli::UnitOffset<R::Offset>,
15 LazyCell<Result<Function<R>, Error>>,
16 )],
17 >,
18 /// List of `DW_TAG_subprogram` address ranges in the unit.
19 pub(crate) addresses: Box<[FunctionAddress]>,
20 }
21
22 /// A single address range for a function.
23 ///
24 /// It is possible for a function to have multiple address ranges; this
25 /// is handled by having multiple `FunctionAddress` entries with the same
26 /// `function` field.
27 pub(crate) struct FunctionAddress {
28 range: gimli::Range,
29 /// An index into `Functions::functions`.
30 pub(crate) function: usize,
31 }
32
33 pub(crate) struct Function<R: gimli::Reader> {
34 pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>,
35 pub(crate) name: Option<R>,
36 /// List of all `DW_TAG_inlined_subroutine` details in this function.
37 inlined_functions: Box<[InlinedFunction<R>]>,
38 /// List of `DW_TAG_inlined_subroutine` address ranges in this function.
39 inlined_addresses: Box<[InlinedFunctionAddress]>,
40 }
41
42 pub(crate) struct InlinedFunctionAddress {
43 range: gimli::Range,
44 call_depth: usize,
45 /// An index into `Function::inlined_functions`.
46 function: usize,
47 }
48
49 pub(crate) struct InlinedFunction<R: gimli::Reader> {
50 pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>,
51 pub(crate) name: Option<R>,
52 pub(crate) call_file: u64,
53 pub(crate) call_line: u32,
54 pub(crate) call_column: u32,
55 }
56
57 impl<R: gimli::Reader> Functions<R> {
58 pub(crate) fn parse(unit: &gimli::Unit<R>, dwarf: &ResDwarf<R>) -> Result<Functions<R>, Error> {
59 let mut functions = Vec::new();
60 let mut addresses = Vec::new();
61 let mut entries = unit.entries_raw(None)?;
62 while !entries.is_empty() {
63 let dw_die_offset = entries.next_offset();
64 if let Some(abbrev) = entries.read_abbreviation()? {
65 if abbrev.tag() == gimli::DW_TAG_subprogram {
66 let mut ranges = RangeAttributes::default();
67 for spec in abbrev.attributes() {
68 match entries.read_attribute(*spec) {
69 Ok(ref attr) => {
70 match attr.name() {
71 gimli::DW_AT_low_pc => {
72 if let gimli::AttributeValue::Addr(val) = attr.value() {
73 ranges.low_pc = Some(val);
74 }
75 }
76 gimli::DW_AT_high_pc => match attr.value() {
77 gimli::AttributeValue::Addr(val) => {
78 ranges.high_pc = Some(val)
79 }
80 gimli::AttributeValue::Udata(val) => {
81 ranges.size = Some(val)
82 }
83 _ => {}
84 },
85 gimli::DW_AT_ranges => {
86 ranges.ranges_offset = dwarf
87 .sections
88 .attr_ranges_offset(unit, attr.value())?;
89 }
90 _ => {}
91 };
92 }
93 Err(e) => return Err(e),
94 }
95 }
96
97 let function_index = functions.len();
98 if ranges.for_each_range(&dwarf.sections, unit, |range| {
99 addresses.push(FunctionAddress {
100 range,
101 function: function_index,
102 });
103 })? {
104 functions.push((dw_die_offset, LazyCell::new()));
105 }
106 } else {
107 entries.skip_attributes(abbrev.attributes())?;
108 }
109 }
110 }
111
112 // The binary search requires the addresses to be sorted.
113 //
114 // It also requires them to be non-overlapping. In practice, overlapping
115 // function ranges are unlikely, so we don't try to handle that yet.
116 //
117 // It's possible for multiple functions to have the same address range if the
118 // compiler can detect and remove functions with identical code. In that case
119 // we'll nondeterministically return one of them.
120 addresses.sort_by_key(|x| x.range.begin);
121
122 Ok(Functions {
123 functions: functions.into_boxed_slice(),
124 addresses: addresses.into_boxed_slice(),
125 })
126 }
127
128 pub(crate) fn find_address(&self, probe: u64) -> Option<usize> {
129 self.addresses
130 .binary_search_by(|address| {
131 if probe < address.range.begin {
132 Ordering::Greater
133 } else if probe >= address.range.end {
134 Ordering::Less
135 } else {
136 Ordering::Equal
137 }
138 })
139 .ok()
140 }
141
142 pub(crate) fn parse_inlined_functions(
143 &self,
144 unit: &gimli::Unit<R>,
145 dwarf: &ResDwarf<R>,
146 ) -> Result<(), Error> {
147 for function in &*self.functions {
148 function
149 .1
150 .borrow_with(|| Function::parse(function.0, unit, dwarf))
151 .as_ref()
152 .map_err(Error::clone)?;
153 }
154 Ok(())
155 }
156 }
157
158 impl<R: gimli::Reader> Function<R> {
159 pub(crate) fn parse(
160 dw_die_offset: gimli::UnitOffset<R::Offset>,
161 unit: &gimli::Unit<R>,
162 dwarf: &ResDwarf<R>,
163 ) -> Result<Self, Error> {
164 let mut entries = unit.entries_raw(Some(dw_die_offset))?;
165 let depth = entries.next_depth();
166 let abbrev = entries.read_abbreviation()?.unwrap();
167 debug_assert_eq!(abbrev.tag(), gimli::DW_TAG_subprogram);
168
169 let mut name = None;
170 for spec in abbrev.attributes() {
171 match entries.read_attribute(*spec) {
172 Ok(ref attr) => {
173 match attr.name() {
174 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
175 if let Ok(val) = dwarf.sections.attr_string(unit, attr.value()) {
176 name = Some(val);
177 }
178 }
179 gimli::DW_AT_name => {
180 if name.is_none() {
181 name = dwarf.sections.attr_string(unit, attr.value()).ok();
182 }
183 }
184 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
185 if name.is_none() {
186 name = name_attr(attr.value(), unit, dwarf, 16)?;
187 }
188 }
189 _ => {}
190 };
191 }
192 Err(e) => return Err(e),
193 }
194 }
195
196 let mut inlined_functions = Vec::new();
197 let mut inlined_addresses = Vec::new();
198 Function::parse_children(
199 &mut entries,
200 depth,
201 unit,
202 dwarf,
203 &mut inlined_functions,
204 &mut inlined_addresses,
205 0,
206 )?;
207
208 // Sort ranges in "breadth-first traversal order", i.e. first by call_depth
209 // and then by range.begin. This allows finding the range containing an
210 // address at a certain depth using binary search.
211 // Note: Using DFS order, i.e. ordering by range.begin first and then by
212 // call_depth, would not work! Consider the two examples
213 // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and
214 // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]".
215 // In this example, if you want to look up address 7 at depth 0, and you
216 // encounter [0..2 at depth 1], are you before or after the target range?
217 // You don't know.
218 inlined_addresses.sort_by(|r1, r2| {
219 if r1.call_depth < r2.call_depth {
220 Ordering::Less
221 } else if r1.call_depth > r2.call_depth {
222 Ordering::Greater
223 } else if r1.range.begin < r2.range.begin {
224 Ordering::Less
225 } else if r1.range.begin > r2.range.begin {
226 Ordering::Greater
227 } else {
228 Ordering::Equal
229 }
230 });
231
232 Ok(Function {
233 dw_die_offset,
234 name,
235 inlined_functions: inlined_functions.into_boxed_slice(),
236 inlined_addresses: inlined_addresses.into_boxed_slice(),
237 })
238 }
239
240 fn parse_children(
241 entries: &mut gimli::EntriesRaw<R>,
242 depth: isize,
243 unit: &gimli::Unit<R>,
244 dwarf: &ResDwarf<R>,
245 inlined_functions: &mut Vec<InlinedFunction<R>>,
246 inlined_addresses: &mut Vec<InlinedFunctionAddress>,
247 inlined_depth: usize,
248 ) -> Result<(), Error> {
249 loop {
250 let dw_die_offset = entries.next_offset();
251 let next_depth = entries.next_depth();
252 if next_depth <= depth {
253 return Ok(());
254 }
255 if let Some(abbrev) = entries.read_abbreviation()? {
256 match abbrev.tag() {
257 gimli::DW_TAG_subprogram => {
258 Function::skip(entries, abbrev, next_depth)?;
259 }
260 gimli::DW_TAG_inlined_subroutine => {
261 InlinedFunction::parse(
262 dw_die_offset,
263 entries,
264 abbrev,
265 next_depth,
266 unit,
267 dwarf,
268 inlined_functions,
269 inlined_addresses,
270 inlined_depth,
271 )?;
272 }
273 _ => {
274 entries.skip_attributes(abbrev.attributes())?;
275 }
276 }
277 }
278 }
279 }
280
281 fn skip(
282 entries: &mut gimli::EntriesRaw<R>,
283 abbrev: &gimli::Abbreviation,
284 depth: isize,
285 ) -> Result<(), Error> {
286 // TODO: use DW_AT_sibling
287 entries.skip_attributes(abbrev.attributes())?;
288 while entries.next_depth() > depth {
289 if let Some(abbrev) = entries.read_abbreviation()? {
290 entries.skip_attributes(abbrev.attributes())?;
291 }
292 }
293 Ok(())
294 }
295
296 /// Build the list of inlined functions that contain `probe`.
297 pub(crate) fn find_inlined_functions(
298 &self,
299 probe: u64,
300 ) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>> {
301 // `inlined_functions` is ordered from outside to inside.
302 let mut inlined_functions = maybe_small::Vec::new();
303 let mut inlined_addresses = &self.inlined_addresses[..];
304 loop {
305 let current_depth = inlined_functions.len();
306 // Look up (probe, current_depth) in inline_ranges.
307 // `inlined_addresses` is sorted in "breadth-first traversal order", i.e.
308 // by `call_depth` first, and then by `range.begin`. See the comment at
309 // the sort call for more information about why.
310 let search = inlined_addresses.binary_search_by(|range| {
311 if range.call_depth > current_depth {
312 Ordering::Greater
313 } else if range.call_depth < current_depth {
314 Ordering::Less
315 } else if range.range.begin > probe {
316 Ordering::Greater
317 } else if range.range.end <= probe {
318 Ordering::Less
319 } else {
320 Ordering::Equal
321 }
322 });
323 if let Ok(index) = search {
324 let function_index = inlined_addresses[index].function;
325 inlined_functions.push(&self.inlined_functions[function_index]);
326 inlined_addresses = &inlined_addresses[index + 1..];
327 } else {
328 break;
329 }
330 }
331 inlined_functions.into_iter().rev()
332 }
333 }
334
335 impl<R: gimli::Reader> InlinedFunction<R> {
336 fn parse(
337 dw_die_offset: gimli::UnitOffset<R::Offset>,
338 entries: &mut gimli::EntriesRaw<R>,
339 abbrev: &gimli::Abbreviation,
340 depth: isize,
341 unit: &gimli::Unit<R>,
342 dwarf: &ResDwarf<R>,
343 inlined_functions: &mut Vec<InlinedFunction<R>>,
344 inlined_addresses: &mut Vec<InlinedFunctionAddress>,
345 inlined_depth: usize,
346 ) -> Result<(), Error> {
347 let mut ranges = RangeAttributes::default();
348 let mut name = None;
349 let mut call_file = 0;
350 let mut call_line = 0;
351 let mut call_column = 0;
352 for spec in abbrev.attributes() {
353 match entries.read_attribute(*spec) {
354 Ok(ref attr) => match attr.name() {
355 gimli::DW_AT_low_pc => {
356 if let gimli::AttributeValue::Addr(val) = attr.value() {
357 ranges.low_pc = Some(val);
358 }
359 }
360 gimli::DW_AT_high_pc => match attr.value() {
361 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
362 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
363 _ => {}
364 },
365 gimli::DW_AT_ranges => {
366 ranges.ranges_offset =
367 dwarf.sections.attr_ranges_offset(unit, attr.value())?;
368 }
369 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
370 if let Ok(val) = dwarf.sections.attr_string(unit, attr.value()) {
371 name = Some(val);
372 }
373 }
374 gimli::DW_AT_name => {
375 if name.is_none() {
376 name = dwarf.sections.attr_string(unit, attr.value()).ok();
377 }
378 }
379 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
380 if name.is_none() {
381 name = name_attr(attr.value(), unit, dwarf, 16)?;
382 }
383 }
384 gimli::DW_AT_call_file => {
385 if let gimli::AttributeValue::FileIndex(fi) = attr.value() {
386 call_file = fi;
387 }
388 }
389 gimli::DW_AT_call_line => {
390 call_line = attr.udata_value().unwrap_or(0) as u32;
391 }
392 gimli::DW_AT_call_column => {
393 call_column = attr.udata_value().unwrap_or(0) as u32;
394 }
395 _ => {}
396 },
397 Err(e) => return Err(e),
398 }
399 }
400
401 let function_index = inlined_functions.len();
402 inlined_functions.push(InlinedFunction {
403 dw_die_offset,
404 name,
405 call_file,
406 call_line,
407 call_column,
408 });
409
410 ranges.for_each_range(&dwarf.sections, unit, |range| {
411 inlined_addresses.push(InlinedFunctionAddress {
412 range,
413 call_depth: inlined_depth,
414 function: function_index,
415 });
416 })?;
417
418 Function::parse_children(
419 entries,
420 depth,
421 unit,
422 dwarf,
423 inlined_functions,
424 inlined_addresses,
425 inlined_depth + 1,
426 )
427 }
428 }
429
430 fn name_attr<R>(
431 attr: gimli::AttributeValue<R>,
432 unit: &gimli::Unit<R>,
433 dwarf: &ResDwarf<R>,
434 recursion_limit: usize,
435 ) -> Result<Option<R>, Error>
436 where
437 R: gimli::Reader,
438 {
439 if recursion_limit == 0 {
440 return Ok(None);
441 }
442
443 match attr {
444 gimli::AttributeValue::UnitRef(offset) => name_entry(unit, offset, dwarf, recursion_limit),
445 gimli::AttributeValue::DebugInfoRef(dr) => {
446 let res_unit = dwarf.find_unit(dr)?;
447 name_entry(
448 &res_unit.dw_unit,
449 gimli::UnitOffset(dr.0 - res_unit.offset.0),
450 dwarf,
451 recursion_limit,
452 )
453 }
454 gimli::AttributeValue::DebugInfoRefSup(dr) => {
455 if let Some(sup_dwarf) = dwarf.sup.as_ref() {
456 let res_unit = sup_dwarf.find_unit(dr)?;
457 name_entry(
458 &res_unit.dw_unit,
459 gimli::UnitOffset(dr.0 - res_unit.offset.0),
460 sup_dwarf,
461 recursion_limit,
462 )
463 } else {
464 Ok(None)
465 }
466 }
467 _ => Ok(None),
468 }
469 }
470
471 fn name_entry<R>(
472 unit: &gimli::Unit<R>,
473 offset: gimli::UnitOffset<R::Offset>,
474 dwarf: &ResDwarf<R>,
475 recursion_limit: usize,
476 ) -> Result<Option<R>, Error>
477 where
478 R: gimli::Reader,
479 {
480 let mut entries = unit.entries_raw(Some(offset))?;
481 let abbrev = if let Some(abbrev) = entries.read_abbreviation()? {
482 abbrev
483 } else {
484 return Err(gimli::Error::NoEntryAtGivenOffset);
485 };
486
487 let mut name = None;
488 let mut next = None;
489 for spec in abbrev.attributes() {
490 match entries.read_attribute(*spec) {
491 Ok(ref attr) => match attr.name() {
492 gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
493 if let Ok(val) = dwarf.sections.attr_string(unit, attr.value()) {
494 return Ok(Some(val));
495 }
496 }
497 gimli::DW_AT_name => {
498 if let Ok(val) = dwarf.sections.attr_string(unit, attr.value()) {
499 name = Some(val);
500 }
501 }
502 gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
503 next = Some(attr.value());
504 }
505 _ => {}
506 },
507 Err(e) => return Err(e),
508 }
509 }
510
511 if name.is_some() {
512 return Ok(name);
513 }
514
515 if let Some(next) = next {
516 return name_attr(next, unit, dwarf, recursion_limit - 1);
517 }
518
519 Ok(None)
520 }