3 use core
::cmp
::Ordering
;
6 use crate::lazy
::LazyCell
;
7 use crate::maybe_small
;
8 use crate::{Error, RangeAttributes, ResDwarf}
;
10 pub(crate) struct Functions
<R
: gimli
::Reader
> {
11 /// List of all `DW_TAG_subprogram` details in the unit.
12 pub(crate) functions
: Box
<
14 gimli
::UnitOffset
<R
::Offset
>,
15 LazyCell
<Result
<Function
<R
>, Error
>>,
18 /// List of `DW_TAG_subprogram` address ranges in the unit.
19 pub(crate) addresses
: Box
<[FunctionAddress
]>,
22 /// A single address range for a function.
24 /// It is possible for a function to have multiple address ranges; this
25 /// is handled by having multiple `FunctionAddress` entries with the same
27 pub(crate) struct FunctionAddress
{
29 /// An index into `Functions::functions`.
30 pub(crate) function
: usize,
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
]>,
42 pub(crate) struct InlinedFunctionAddress
{
45 /// An index into `Function::inlined_functions`.
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,
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
) {
71 gimli
::DW_AT_low_pc
=> {
72 if let gimli
::AttributeValue
::Addr(val
) = attr
.value() {
73 ranges
.low_pc
= Some(val
);
76 gimli
::DW_AT_high_pc
=> match attr
.value() {
77 gimli
::AttributeValue
::Addr(val
) => {
78 ranges
.high_pc
= Some(val
)
80 gimli
::AttributeValue
::Udata(val
) => {
81 ranges
.size
= Some(val
)
85 gimli
::DW_AT_ranges
=> {
86 ranges
.ranges_offset
= dwarf
88 .attr_ranges_offset(unit
, attr
.value())?
;
93 Err(e
) => return Err(e
),
97 let function_index
= functions
.len();
98 if ranges
.for_each_range(&dwarf
.sections
, unit
, |range
| {
99 addresses
.push(FunctionAddress
{
101 function
: function_index
,
104 functions
.push((dw_die_offset
, LazyCell
::new()));
107 entries
.skip_attributes(abbrev
.attributes())?
;
112 // The binary search requires the addresses to be sorted.
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.
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
);
123 functions
: functions
.into_boxed_slice(),
124 addresses
: addresses
.into_boxed_slice(),
128 pub(crate) fn find_address(&self, probe
: u64) -> Option
<usize> {
130 .binary_search_by(|address
| {
131 if probe
< address
.range
.begin
{
133 } else if probe
>= address
.range
.end
{
142 pub(crate) fn parse_inlined_functions(
144 unit
: &gimli
::Unit
<R
>,
146 ) -> Result
<(), Error
> {
147 for function
in &*self.functions
{
150 .borrow_with(|| Function
::parse(function
.0, unit
, dwarf
))
152 .map_err(Error
::clone
)?
;
158 impl<R
: gimli
::Reader
> Function
<R
> {
160 dw_die_offset
: gimli
::UnitOffset
<R
::Offset
>,
161 unit
: &gimli
::Unit
<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
);
170 for spec
in abbrev
.attributes() {
171 match entries
.read_attribute(*spec
) {
174 gimli
::DW_AT_linkage_name
| gimli
::DW_AT_MIPS_linkage_name
=> {
175 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
179 gimli
::DW_AT_name
=> {
181 name
= dwarf
.sections
.attr_string(unit
, attr
.value()).ok();
184 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
186 name
= name_attr(attr
.value(), unit
, dwarf
, 16)?
;
192 Err(e
) => return Err(e
),
196 let mut inlined_functions
= Vec
::new();
197 let mut inlined_addresses
= Vec
::new();
198 Function
::parse_children(
203 &mut inlined_functions
,
204 &mut inlined_addresses
,
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?
218 inlined_addresses
.sort_by(|r1
, r2
| {
219 if r1
.call_depth
< r2
.call_depth
{
221 } else if r1
.call_depth
> r2
.call_depth
{
223 } else if r1
.range
.begin
< r2
.range
.begin
{
225 } else if r1
.range
.begin
> r2
.range
.begin
{
235 inlined_functions
: inlined_functions
.into_boxed_slice(),
236 inlined_addresses
: inlined_addresses
.into_boxed_slice(),
241 entries
: &mut gimli
::EntriesRaw
<R
>,
243 unit
: &gimli
::Unit
<R
>,
245 inlined_functions
: &mut Vec
<InlinedFunction
<R
>>,
246 inlined_addresses
: &mut Vec
<InlinedFunctionAddress
>,
247 inlined_depth
: usize,
248 ) -> Result
<(), Error
> {
250 let dw_die_offset
= entries
.next_offset();
251 let next_depth
= entries
.next_depth();
252 if next_depth
<= depth
{
255 if let Some(abbrev
) = entries
.read_abbreviation()?
{
257 gimli
::DW_TAG_subprogram
=> {
258 Function
::skip(entries
, abbrev
, next_depth
)?
;
260 gimli
::DW_TAG_inlined_subroutine
=> {
261 InlinedFunction
::parse(
274 entries
.skip_attributes(abbrev
.attributes())?
;
282 entries
: &mut gimli
::EntriesRaw
<R
>,
283 abbrev
: &gimli
::Abbreviation
,
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())?
;
296 /// Build the list of inlined functions that contain `probe`.
297 pub(crate) fn find_inlined_functions(
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
[..];
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
{
313 } else if range
.call_depth
< current_depth
{
315 } else if range
.range
.begin
> probe
{
317 } else if range
.range
.end
<= probe
{
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..];
331 inlined_functions
.into_iter().rev()
335 impl<R
: gimli
::Reader
> InlinedFunction
<R
> {
337 dw_die_offset
: gimli
::UnitOffset
<R
::Offset
>,
338 entries
: &mut gimli
::EntriesRaw
<R
>,
339 abbrev
: &gimli
::Abbreviation
,
341 unit
: &gimli
::Unit
<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();
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
);
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
),
365 gimli
::DW_AT_ranges
=> {
366 ranges
.ranges_offset
=
367 dwarf
.sections
.attr_ranges_offset(unit
, attr
.value())?
;
369 gimli
::DW_AT_linkage_name
| gimli
::DW_AT_MIPS_linkage_name
=> {
370 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
374 gimli
::DW_AT_name
=> {
376 name
= dwarf
.sections
.attr_string(unit
, attr
.value()).ok();
379 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
381 name
= name_attr(attr
.value(), unit
, dwarf
, 16)?
;
384 gimli
::DW_AT_call_file
=> {
385 if let gimli
::AttributeValue
::FileIndex(fi
) = attr
.value() {
389 gimli
::DW_AT_call_line
=> {
390 call_line
= attr
.udata_value().unwrap_or(0) as u32;
392 gimli
::DW_AT_call_column
=> {
393 call_column
= attr
.udata_value().unwrap_or(0) as u32;
397 Err(e
) => return Err(e
),
401 let function_index
= inlined_functions
.len();
402 inlined_functions
.push(InlinedFunction
{
410 ranges
.for_each_range(&dwarf
.sections
, unit
, |range
| {
411 inlined_addresses
.push(InlinedFunctionAddress
{
413 call_depth
: inlined_depth
,
414 function
: function_index
,
418 Function
::parse_children(
431 attr
: gimli
::AttributeValue
<R
>,
432 unit
: &gimli
::Unit
<R
>,
434 recursion_limit
: usize,
435 ) -> Result
<Option
<R
>, Error
>
439 if recursion_limit
== 0 {
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
)?
;
449 gimli
::UnitOffset(dr
.0 - res_unit
.offset
.0),
454 gimli
::AttributeValue
::DebugInfoRefSup(dr
) => {
455 if let Some(sup_dwarf
) = dwarf
.sup
.as_ref() {
456 let res_unit
= sup_dwarf
.find_unit(dr
)?
;
459 gimli
::UnitOffset(dr
.0 - res_unit
.offset
.0),
472 unit
: &gimli
::Unit
<R
>,
473 offset
: gimli
::UnitOffset
<R
::Offset
>,
475 recursion_limit
: usize,
476 ) -> Result
<Option
<R
>, Error
>
480 let mut entries
= unit
.entries_raw(Some(offset
))?
;
481 let abbrev
= if let Some(abbrev
) = entries
.read_abbreviation()?
{
484 return Err(gimli
::Error
::NoEntryAtGivenOffset
);
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
));
497 gimli
::DW_AT_name
=> {
498 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
502 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
503 next
= Some(attr
.value());
507 Err(e
) => return Err(e
),
515 if let Some(next
) = next
{
516 return name_attr(next
, unit
, dwarf
, recursion_limit
- 1);