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
13 from ..common
.exceptions
import DWARFError
14 from .dwarf_util
import _iter_CUs_in_section
17 RangeEntry
= namedtuple('RangeEntry', 'entry_offset entry_length begin_offset end_offset is_absolute')
18 BaseAddressEntry
= namedtuple('BaseAddressEntry', 'entry_offset base_address')
20 def not_implemented(e
):
21 raise NotImplementedError("Range list entry %s is not supported yet" % (e
.entry_type
,))
23 # Maps parsed entry types to RangeEntry/BaseAddressEntry objects
25 'DW_RLE_base_address' : lambda e
: BaseAddressEntry(e
.entry_offset
, e
.address
),
26 'DW_RLE_offset_pair' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_offset
, e
.end_offset
, False),
27 'DW_RLE_start_end' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_address
, e
.end_address
, True),
28 'DW_RLE_start_length' : lambda e
: RangeEntry(e
.entry_offset
, e
.entry_length
, e
.start_address
, e
.start_address
+ e
.length
, True),
29 'DW_RLE_base_addressx': not_implemented
,
30 'DW_RLE_startx_endx' : not_implemented
,
31 'DW_RLE_startx_length': not_implemented
34 class RangeListsPair(object):
35 """For those binaries that contain both a debug_ranges and a debug_rnglists section,
36 it holds a RangeLists object for both and forwards API calls to the right one based
39 def __init__(self
, streamv4
, streamv5
, structs
, dwarfinfo
=None):
40 self
._ranges
= RangeLists(streamv4
, structs
, 4, dwarfinfo
)
41 self
._rnglists
= RangeLists(streamv5
, structs
, 5, dwarfinfo
)
43 def get_range_list_at_offset(self
, offset
, cu
=None):
44 """Forwards the call to either v4 section or v5 one,
45 depending on DWARF version in the CU.
48 raise DWARFError("For this binary, \"cu\" needs to be provided")
49 section
= self
._rnglists
if cu
.header
.version
>= 5 else self
._ranges
50 return section
.get_range_list_at_offset(offset
, cu
)
52 def get_range_list_at_offset_ex(self
, offset
):
53 """Gets an untranslated v5 rangelist from the v5 section.
55 return self
._rnglists
.get_range_list_at_offset_ex(offset
)
57 def iter_range_lists(self
):
58 """Tricky proposition, since the structure of ranges and rnglists
59 is not identical. A realistic readelf implementation needs to be aware of both.
61 raise DWARFError("Iterating through two sections is not supported")
64 """See RangeLists.iter_CUs()
66 CU structure is only present in DWARFv5 rnglists sections. A well written
67 section dumper should check if one is present.
69 return self
._rnglists
.iter_CUs()
71 def iter_CU_range_lists_ex(self
, cu
):
72 """See RangeLists.iter_CU_range_lists_ex()
74 CU structure is only present in DWARFv5 rnglists sections. A well written
75 section dumper should check if one is present.
77 return self
._rnglists
.iter_CU_range_lists_ex(cu
)
79 class RangeLists(object):
80 """ A single range list is a Python list consisting of RangeEntry or
81 BaseAddressEntry objects.
83 Since v0.29, two new parameters - version and dwarfinfo
85 version is used to distinguish DWARFv5 rnglists section from
86 the DWARF<=4 ranges section. Only the 4/5 distinction matters.
88 The dwarfinfo is needed for enumeration, because enumeration
89 requires scanning the DIEs, because ranges may overlap, even on DWARF<=4
91 def __init__(self
, stream
, structs
, version
, dwarfinfo
):
93 self
.structs
= structs
94 self
._max
_addr
= 2 ** (self
.structs
.address_size
* 8) - 1
95 self
.version
= version
96 self
._dwarfinfo
= dwarfinfo
98 def get_range_list_at_offset(self
, offset
, cu
=None):
99 """ Get a range list at the given offset in the section.
101 self
.stream
.seek(offset
, os
.SEEK_SET
)
102 return self
._parse
_range
_list
_from
_stream
()
104 def get_range_list_at_offset_ex(self
, offset
):
105 """Get a DWARF v5 range list, addresses and offsets unresolved,
106 at the given offset in the section
108 return struct_parse(self
.structs
.Dwarf_rnglists_entries
, self
.stream
, offset
)
110 def iter_range_lists(self
):
111 """ Yield all range lists found in the section according to readelf rules.
112 Scans the DIEs for rangelist offsets, then pulls those.
114 # Calling parse until the stream ends is wrong, because ranges can overlap.
115 # Need to scan the DIEs to know all range locations
116 ver5
= self
.version
>= 5
117 all_offsets
= list(set(die
.attributes
['DW_AT_ranges'].value
118 for cu
in self
._dwarfinfo
.iter_CUs()
119 for die
in cu
.iter_DIEs()
120 if 'DW_AT_ranges' in die
.attributes
and (cu
.header
.version
>= 5) == ver5
))
123 for offset
in all_offsets
:
124 yield self
.get_range_list_at_offset(offset
)
127 """For DWARF5 returns an array of objects, where each one has an array of offsets
130 raise DWARFError("CU iteration in rnglists is not supported with DWARF<5")
132 structs
= next(self
._dwarfinfo
.iter_CUs()).structs
# Just pick one
133 return _iter_CUs_in_section(self
.stream
, structs
, structs
.Dwarf_rnglists_CU_header
)
135 def iter_CU_range_lists_ex(self
, cu
):
136 """For DWARF5, returns untranslated rangelists in the CU, where CU comes from iter_CUs above
139 stream
.seek(cu
.offset_table_offset
+ (64 if cu
.is64
else 32) * cu
.offset_count
)
140 while stream
.tell() < cu
.offset_after_length
+ cu
.unit_length
:
141 yield struct_parse(self
.structs
.Dwarf_rnglists_entries
, stream
);
144 #------ PRIVATE ------#
146 def _parse_range_list_from_stream(self
):
147 if self
.version
>= 5:
148 return list(entry_translate
[entry
.entry_type
](entry
)
150 in struct_parse(self
.structs
.Dwarf_rnglists_entries
, self
.stream
))
154 entry_offset
= self
.stream
.tell()
155 begin_offset
= struct_parse(
156 self
.structs
.Dwarf_target_addr(''), self
.stream
)
157 end_offset
= struct_parse(
158 self
.structs
.Dwarf_target_addr(''), self
.stream
)
159 if begin_offset
== 0 and end_offset
== 0:
160 # End of list - we're done.
162 elif begin_offset
== self
._max
_addr
:
163 # Base address selection entry
164 lst
.append(BaseAddressEntry(entry_offset
=entry_offset
, base_address
=end_offset
))
167 lst
.append(RangeEntry(
168 entry_offset
=entry_offset
,
169 entry_length
=self
.stream
.tell() - entry_offset
,
170 begin_offset
=begin_offset
,
171 end_offset
=end_offset
,