Support for DWARFv5 debug_rnglists section (#419)
[pyelftools.git] / elftools / dwarf / ranges.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/ranges.py
3 #
4 # DWARF ranges section decoding (.debug_ranges)
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 import os
10 from collections import namedtuple
11
12 from ..common.utils import struct_parse
13
14
15 RangeEntry = namedtuple('RangeEntry', 'entry_offset entry_length begin_offset end_offset is_absolute')
16 BaseAddressEntry = namedtuple('BaseAddressEntry', 'entry_offset base_address')
17
18 def not_implemented(e):
19 raise NotImplementedError("Range list entry %s is not supported yet" % (e.entry_type,))
20
21 # Maps parsed entry types to RangeEntry/BaseAddressEntry objects
22 entry_translate = {
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
30 }
31
32 class RangeLists(object):
33 """ A single range list is a Python list consisting of RangeEntry or
34 BaseAddressEntry objects.
35
36 Since v0.29, two new parameters - version and dwarfinfo
37
38 version is used to distinguish DWARFv5 rnglists section from
39 the DWARF<=4 ranges section. Only the 4/5 distinction matters.
40
41 The dwarfinfo is needed for enumeration, because enumeration
42 requires scanning the DIEs, because ranges may overlap, even on DWARF<=4
43 """
44 def __init__(self, stream, structs, version, dwarfinfo):
45 self.stream = stream
46 self.structs = structs
47 self._max_addr = 2 ** (self.structs.address_size * 8) - 1
48 self.version = version
49 self._dwarfinfo = dwarfinfo
50
51 def get_range_list_at_offset(self, offset):
52 """ Get a range list at the given offset in the section.
53 """
54 self.stream.seek(offset, os.SEEK_SET)
55 return self._parse_range_list_from_stream()
56
57 def iter_range_lists(self):
58 """ Yield all range lists found in the section.
59 """
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))
66 all_offsets.sort()
67
68 for offset in all_offsets:
69 yield self.get_range_list_at_offset(offset)
70
71 #------ PRIVATE ------#
72
73 def _parse_range_list_from_stream(self):
74 if self.version >= 5:
75 return list(entry_translate[entry.entry_type](entry)
76 for entry
77 in struct_parse(self.structs.Dwarf_rnglists_entries, self.stream))
78 else:
79 lst = []
80 while True:
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.
88 break
89 elif begin_offset == self._max_addr:
90 # Base address selection entry
91 lst.append(BaseAddressEntry(entry_offset=entry_offset, base_address=end_offset))
92 else:
93 # Range entry
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,
99 is_absolute=False))
100 return lst