1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/ranges.py
4 # DWARF ranges section decoding (.debug_ranges)
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
10 from collections
import namedtuple
12 from ..common
.utils
import struct_parse
15 RangeEntry
= namedtuple('RangeEntry', 'entry_offset entry_length begin_offset end_offset is_absolute')
16 BaseAddressEntry
= namedtuple('BaseAddressEntry', 'entry_offset base_address')
18 def not_implemented(e
):
19 raise NotImplementedError("Range list entry %s is not supported yet" % (e
.entry_type
,))
21 # Maps parsed entry types to RangeEntry/BaseAddressEntry objects
23 'DW_RLE_base_address' : lambda e
: BaseAddressEntry(e
.entry_offset
, e
.address
),
24 'DW_RLE_offset_pair' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_offset
, e
.end_offset
, False),
25 'DW_RLE_start_end' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_address
, e
.end_address
, True),
26 'DW_RLE_start_length' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_address
, e
.start_address
+ e
.length
, True),
27 'DW_RLE_base_addressx': not_implemented
,
28 'DW_RLE_startx_endx' : not_implemented
,
29 'DW_RLE_startx_length': not_implemented
32 class RangeLists(object):
33 """ A single range list is a Python list consisting of RangeEntry or
34 BaseAddressEntry objects.
36 Since v0.29, two new parameters - version and dwarfinfo
38 version is used to distinguish DWARFv5 rnglists section from
39 the DWARF<=4 ranges section. Only the 4/5 distinction matters.
41 The dwarfinfo is needed for enumeration, because enumeration
42 requires scanning the DIEs, because ranges may overlap, even on DWARF<=4
44 def __init__(self
, stream
, structs
, version
, dwarfinfo
):
46 self
.structs
= structs
47 self
._max
_addr
= 2 ** (self
.structs
.address_size
* 8) - 1
48 self
.version
= version
49 self
._dwarfinfo
= dwarfinfo
51 def get_range_list_at_offset(self
, offset
):
52 """ Get a range list at the given offset in the section.
54 self
.stream
.seek(offset
, os
.SEEK_SET
)
55 return self
._parse
_range
_list
_from
_stream
()
57 def iter_range_lists(self
):
58 """ Yield all range lists found in the section.
60 # Calling parse until the stream ends is wrong, because ranges can overlap.
61 # Need to scan the DIEs to know all range locations
62 all_offsets
= list(set(die
.attributes
['DW_AT_ranges'].value
63 for cu
in self
._dwarfinfo
.iter_CUs()
64 for die
in cu
.iter_DIEs()
65 if 'DW_AT_ranges' in die
.attributes
))
68 for offset
in all_offsets
:
69 yield self
.get_range_list_at_offset(offset
)
71 #------ PRIVATE ------#
73 def _parse_range_list_from_stream(self
):
75 return list(entry_translate
[entry
.entry_type
](entry
)
77 in struct_parse(self
.structs
.Dwarf_rnglists_entries
, self
.stream
))
81 entry_offset
= self
.stream
.tell()
82 begin_offset
= struct_parse(
83 self
.structs
.Dwarf_target_addr(''), self
.stream
)
84 end_offset
= struct_parse(
85 self
.structs
.Dwarf_target_addr(''), self
.stream
)
86 if begin_offset
== 0 and end_offset
== 0:
87 # End of list - we're done.
89 elif begin_offset
== self
._max
_addr
:
90 # Base address selection entry
91 lst
.append(BaseAddressEntry(entry_offset
=entry_offset
, base_address
=end_offset
))
94 lst
.append(RangeEntry(
95 entry_offset
=entry_offset
,
96 entry_length
=self
.stream
.tell() - entry_offset
,
97 begin_offset
=begin_offset
,
98 end_offset
=end_offset
,