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 for spec
in abbrev
.attributes() {
108 match entries
.read_attribute(*spec
) {
110 Err(e
) => return Err(e
),
117 // The binary search requires the addresses to be sorted.
119 // It also requires them to be non-overlapping. In practice, overlapping
120 // function ranges are unlikely, so we don't try to handle that yet.
122 // It's possible for multiple functions to have the same address range if the
123 // compiler can detect and remove functions with identical code. In that case
124 // we'll nondeterministically return one of them.
125 addresses
.sort_by_key(|x
| x
.range
.begin
);
128 functions
: functions
.into_boxed_slice(),
129 addresses
: addresses
.into_boxed_slice(),
133 pub(crate) fn find_address(&self, probe
: u64) -> Option
<usize> {
135 .binary_search_by(|address
| {
136 if probe
< address
.range
.begin
{
138 } else if probe
>= address
.range
.end
{
147 pub(crate) fn parse_inlined_functions(
149 unit
: &gimli
::Unit
<R
>,
151 ) -> Result
<(), Error
> {
152 for function
in &*self.functions
{
155 .borrow_with(|| Function
::parse(function
.0, unit
, dwarf
))
157 .map_err(Error
::clone
)?
;
163 impl<R
: gimli
::Reader
> Function
<R
> {
165 dw_die_offset
: gimli
::UnitOffset
<R
::Offset
>,
166 unit
: &gimli
::Unit
<R
>,
168 ) -> Result
<Self, Error
> {
169 let mut entries
= unit
.entries_raw(Some(dw_die_offset
))?
;
170 let depth
= entries
.next_depth();
171 let abbrev
= entries
.read_abbreviation()?
.unwrap();
172 debug_assert_eq
!(abbrev
.tag(), gimli
::DW_TAG_subprogram
);
175 for spec
in abbrev
.attributes() {
176 match entries
.read_attribute(*spec
) {
179 gimli
::DW_AT_linkage_name
| gimli
::DW_AT_MIPS_linkage_name
=> {
180 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
184 gimli
::DW_AT_name
=> {
186 name
= dwarf
.sections
.attr_string(unit
, attr
.value()).ok();
189 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
191 name
= name_attr(attr
.value(), unit
, dwarf
, 16)?
;
197 Err(e
) => return Err(e
),
201 let mut inlined_functions
= Vec
::new();
202 let mut inlined_addresses
= Vec
::new();
203 Function
::parse_children(
208 &mut inlined_functions
,
209 &mut inlined_addresses
,
213 // Sort ranges in "breadth-first traversal order", i.e. first by call_depth
214 // and then by range.begin. This allows finding the range containing an
215 // address at a certain depth using binary search.
216 // Note: Using DFS order, i.e. ordering by range.begin first and then by
217 // call_depth, would not work! Consider the two examples
218 // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and
219 // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]".
220 // In this example, if you want to look up address 7 at depth 0, and you
221 // encounter [0..2 at depth 1], are you before or after the target range?
223 inlined_addresses
.sort_by(|r1
, r2
| {
224 if r1
.call_depth
< r2
.call_depth
{
226 } else if r1
.call_depth
> r2
.call_depth
{
228 } else if r1
.range
.begin
< r2
.range
.begin
{
230 } else if r1
.range
.begin
> r2
.range
.begin
{
240 inlined_functions
: inlined_functions
.into_boxed_slice(),
241 inlined_addresses
: inlined_addresses
.into_boxed_slice(),
246 entries
: &mut gimli
::EntriesRaw
<R
>,
248 unit
: &gimli
::Unit
<R
>,
250 inlined_functions
: &mut Vec
<InlinedFunction
<R
>>,
251 inlined_addresses
: &mut Vec
<InlinedFunctionAddress
>,
252 inlined_depth
: usize,
253 ) -> Result
<(), Error
> {
255 let dw_die_offset
= entries
.next_offset();
256 let next_depth
= entries
.next_depth();
257 if next_depth
<= depth
{
260 if let Some(abbrev
) = entries
.read_abbreviation()?
{
262 gimli
::DW_TAG_subprogram
=> {
263 Function
::skip(entries
, abbrev
, next_depth
)?
;
265 gimli
::DW_TAG_inlined_subroutine
=> {
266 InlinedFunction
::parse(
279 for spec
in abbrev
.attributes() {
280 match entries
.read_attribute(*spec
) {
282 Err(e
) => return Err(e
),
292 entries
: &mut gimli
::EntriesRaw
<R
>,
293 abbrev
: &gimli
::Abbreviation
,
295 ) -> Result
<(), Error
> {
296 // TODO: use DW_AT_sibling
297 for spec
in abbrev
.attributes() {
298 match entries
.read_attribute(*spec
) {
300 Err(e
) => return Err(e
),
303 while entries
.next_depth() > depth
{
304 if let Some(abbrev
) = entries
.read_abbreviation()?
{
305 for spec
in abbrev
.attributes() {
306 match entries
.read_attribute(*spec
) {
308 Err(e
) => return Err(e
),
316 /// Build the list of inlined functions that contain `probe`.
317 pub(crate) fn find_inlined_functions(
320 ) -> iter
::Rev
<maybe_small
::IntoIter
<&InlinedFunction
<R
>>> {
321 // `inlined_functions` is ordered from outside to inside.
322 let mut inlined_functions
= maybe_small
::Vec
::new();
323 let mut inlined_addresses
= &self.inlined_addresses
[..];
325 let current_depth
= inlined_functions
.len();
326 // Look up (probe, current_depth) in inline_ranges.
327 // `inlined_addresses` is sorted in "breadth-first traversal order", i.e.
328 // by `call_depth` first, and then by `range.begin`. See the comment at
329 // the sort call for more information about why.
330 let search
= inlined_addresses
.binary_search_by(|range
| {
331 if range
.call_depth
> current_depth
{
333 } else if range
.call_depth
< current_depth
{
335 } else if range
.range
.begin
> probe
{
337 } else if range
.range
.end
<= probe
{
343 if let Ok(index
) = search
{
344 let function_index
= inlined_addresses
[index
].function
;
345 inlined_functions
.push(&self.inlined_functions
[function_index
]);
346 inlined_addresses
= &inlined_addresses
[index
+ 1..];
351 inlined_functions
.into_iter().rev()
355 impl<R
: gimli
::Reader
> InlinedFunction
<R
> {
357 dw_die_offset
: gimli
::UnitOffset
<R
::Offset
>,
358 entries
: &mut gimli
::EntriesRaw
<R
>,
359 abbrev
: &gimli
::Abbreviation
,
361 unit
: &gimli
::Unit
<R
>,
363 inlined_functions
: &mut Vec
<InlinedFunction
<R
>>,
364 inlined_addresses
: &mut Vec
<InlinedFunctionAddress
>,
365 inlined_depth
: usize,
366 ) -> Result
<(), Error
> {
367 let mut ranges
= RangeAttributes
::default();
369 let mut call_file
= 0;
370 let mut call_line
= 0;
371 let mut call_column
= 0;
372 for spec
in abbrev
.attributes() {
373 match entries
.read_attribute(*spec
) {
374 Ok(ref attr
) => match attr
.name() {
375 gimli
::DW_AT_low_pc
=> {
376 if let gimli
::AttributeValue
::Addr(val
) = attr
.value() {
377 ranges
.low_pc
= Some(val
);
380 gimli
::DW_AT_high_pc
=> match attr
.value() {
381 gimli
::AttributeValue
::Addr(val
) => ranges
.high_pc
= Some(val
),
382 gimli
::AttributeValue
::Udata(val
) => ranges
.size
= Some(val
),
385 gimli
::DW_AT_ranges
=> {
386 ranges
.ranges_offset
=
387 dwarf
.sections
.attr_ranges_offset(unit
, attr
.value())?
;
389 gimli
::DW_AT_linkage_name
| gimli
::DW_AT_MIPS_linkage_name
=> {
390 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
394 gimli
::DW_AT_name
=> {
396 name
= dwarf
.sections
.attr_string(unit
, attr
.value()).ok();
399 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
401 name
= name_attr(attr
.value(), unit
, dwarf
, 16)?
;
404 gimli
::DW_AT_call_file
=> {
405 if let gimli
::AttributeValue
::FileIndex(fi
) = attr
.value() {
409 gimli
::DW_AT_call_line
=> {
410 call_line
= attr
.udata_value().unwrap_or(0) as u32;
412 gimli
::DW_AT_call_column
=> {
413 call_column
= attr
.udata_value().unwrap_or(0) as u32;
417 Err(e
) => return Err(e
),
421 let function_index
= inlined_functions
.len();
422 inlined_functions
.push(InlinedFunction
{
430 ranges
.for_each_range(&dwarf
.sections
, unit
, |range
| {
431 inlined_addresses
.push(InlinedFunctionAddress
{
433 call_depth
: inlined_depth
,
434 function
: function_index
,
438 Function
::parse_children(
451 attr
: gimli
::AttributeValue
<R
>,
452 unit
: &gimli
::Unit
<R
>,
454 recursion_limit
: usize,
455 ) -> Result
<Option
<R
>, Error
>
459 if recursion_limit
== 0 {
464 gimli
::AttributeValue
::UnitRef(offset
) => name_entry(unit
, offset
, dwarf
, recursion_limit
),
465 gimli
::AttributeValue
::DebugInfoRef(dr
) => {
466 let res_unit
= dwarf
.find_unit(dr
)?
;
469 gimli
::UnitOffset(dr
.0 - res_unit
.offset
.0),
474 gimli
::AttributeValue
::DebugInfoRefSup(dr
) => {
475 if let Some(sup_dwarf
) = dwarf
.sup
.as_ref() {
476 let res_unit
= sup_dwarf
.find_unit(dr
)?
;
479 gimli
::UnitOffset(dr
.0 - res_unit
.offset
.0),
492 unit
: &gimli
::Unit
<R
>,
493 offset
: gimli
::UnitOffset
<R
::Offset
>,
495 recursion_limit
: usize,
496 ) -> Result
<Option
<R
>, Error
>
500 let mut entries
= unit
.entries_raw(Some(offset
))?
;
501 let abbrev
= if let Some(abbrev
) = entries
.read_abbreviation()?
{
504 return Err(gimli
::Error
::NoEntryAtGivenOffset
);
509 for spec
in abbrev
.attributes() {
510 match entries
.read_attribute(*spec
) {
511 Ok(ref attr
) => match attr
.name() {
512 gimli
::DW_AT_linkage_name
| gimli
::DW_AT_MIPS_linkage_name
=> {
513 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
514 return Ok(Some(val
));
517 gimli
::DW_AT_name
=> {
518 if let Ok(val
) = dwarf
.sections
.attr_string(unit
, attr
.value()) {
522 gimli
::DW_AT_abstract_origin
| gimli
::DW_AT_specification
=> {
523 next
= Some(attr
.value());
527 Err(e
) => return Err(e
),
535 if let Some(next
) = next
{
536 return name_attr(next
, unit
, dwarf
, recursion_limit
- 1);