e6c735f585e3a94f63d4cb9f148786d553f5ea29
[pyelftools.git] / elftools / dwarf / locationlists.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/locationlists.py
3 #
4 # DWARF location lists section decoding (.debug_loc)
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 LocationExpr = namedtuple('LocationExpr', 'loc_expr')
15 LocationEntry = namedtuple('LocationEntry', 'entry_offset begin_offset end_offset loc_expr')
16 BaseAddressEntry = namedtuple('BaseAddressEntry', 'entry_offset base_address')
17
18 class LocationLists(object):
19 """ A single location list is a Python list consisting of LocationEntry or
20 BaseAddressEntry objects.
21 """
22 def __init__(self, stream, structs):
23 self.stream = stream
24 self.structs = structs
25 self._max_addr = 2 ** (self.structs.address_size * 8) - 1
26
27 def get_location_list_at_offset(self, offset):
28 """ Get a location list at the given offset in the section.
29 """
30 self.stream.seek(offset, os.SEEK_SET)
31 return self._parse_location_list_from_stream()
32
33 def iter_location_lists(self):
34 """ Yield all location lists found in the section.
35 """
36 # Just call _parse_location_list_from_stream until the stream ends
37 self.stream.seek(0, os.SEEK_END)
38 endpos = self.stream.tell()
39
40 self.stream.seek(0, os.SEEK_SET)
41 while self.stream.tell() < endpos:
42 yield self._parse_location_list_from_stream()
43
44 #------ PRIVATE ------#
45
46 def _parse_location_list_from_stream(self):
47 lst = []
48 while True:
49 entry_offset = self.stream.tell()
50 begin_offset = struct_parse(
51 self.structs.Dwarf_target_addr(''), self.stream)
52 end_offset = struct_parse(
53 self.structs.Dwarf_target_addr(''), self.stream)
54 if begin_offset == 0 and end_offset == 0:
55 # End of list - we're done.
56 break
57 elif begin_offset == self._max_addr:
58 # Base address selection entry
59 lst.append(BaseAddressEntry(entry_offset=entry_offset, base_address=end_offset))
60 else:
61 # Location list entry
62 expr_len = struct_parse(
63 self.structs.Dwarf_uint16(''), self.stream)
64 loc_expr = [struct_parse(self.structs.Dwarf_uint8(''),
65 self.stream)
66 for i in range(expr_len)]
67 lst.append(LocationEntry(
68 entry_offset=entry_offset,
69 begin_offset=begin_offset,
70 end_offset=end_offset,
71 loc_expr=loc_expr))
72 return lst
73
74 class LocationParser(object):
75 """ A parser for location information in DIEs.
76 Handles both location information contained within the attribute
77 itself (represented as a LocationExpr object) and references to
78 location lists in the .debug_loc section (represented as a
79 list).
80 """
81 def __init__(self, location_lists):
82 self.location_lists = location_lists
83
84 @staticmethod
85 def attribute_has_location(attr, dwarf_version):
86 """ Checks if a DIE attribute contains location information.
87 """
88 return (LocationParser._attribute_is_loclistptr_class(attr) and
89 (LocationParser._attribute_has_loc_expr(attr, dwarf_version) or
90 LocationParser._attribute_has_loc_list(attr, dwarf_version)))
91
92 def parse_from_attribute(self, attr, dwarf_version):
93 """ Parses a DIE attribute and returns either a LocationExpr or
94 a list.
95 """
96 if self.attribute_has_location(attr, dwarf_version):
97 if self._attribute_has_loc_expr(attr, dwarf_version):
98 return LocationExpr(attr.value)
99 elif self._attribute_has_loc_list(attr, dwarf_version):
100 return self.location_lists.get_location_list_at_offset(
101 attr.value)
102 else:
103 raise ValueError("Attribute does not have location information")
104
105 #------ PRIVATE ------#
106
107 @staticmethod
108 def _attribute_has_loc_expr(attr, dwarf_version):
109 return ((dwarf_version < 4 and attr.form.startswith('DW_FORM_block') and
110 not attr.name == 'DW_AT_const_value') or
111 attr.form == 'DW_FORM_exprloc')
112
113 @staticmethod
114 def _attribute_has_loc_list(attr, dwarf_version):
115 return ((dwarf_version < 4 and
116 attr.form in ('DW_FORM_data4', 'DW_FORM_data8') and
117 not attr.name == 'DW_AT_const_value') or
118 attr.form == 'DW_FORM_sec_offset')
119
120 @staticmethod
121 def _attribute_is_loclistptr_class(attr):
122 return (attr.name in ( 'DW_AT_location', 'DW_AT_string_length',
123 'DW_AT_const_value', 'DW_AT_return_addr',
124 'DW_AT_data_member_location',
125 'DW_AT_frame_base', 'DW_AT_segment',
126 'DW_AT_static_link', 'DW_AT_use_location',
127 'DW_AT_vtable_elem_location',
128 'DW_AT_GNU_call_site_value',
129 'DW_AT_GNU_call_site_target',
130 'DW_AT_GNU_call_site_data_value'))