starting tests for callframes
[pyelftools.git] / elftools / dwarf / die.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/die.py
3 #
4 # DWARF Debugging Information Entry
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from collections import namedtuple
10
11 from ..common.ordereddict import OrderedDict
12 from ..common.utils import struct_parse, preserve_stream_pos
13
14
15 # AttributeValue - describes an attribute value in the DIE:
16 #
17 # name:
18 # The name (DW_AT_*) of this attribute
19 #
20 # form:
21 # The DW_FORM_* name of this attribute
22 #
23 # value:
24 # The value parsed from the section and translated accordingly to the form
25 # (e.g. for a DW_FORM_strp it's the actual string taken from the string table)
26 #
27 # raw_value:
28 # Raw value as parsed from the section - used for debugging and presentation
29 # (e.g. for a DW_FORM_strp it's the raw string offset into the table)
30 #
31 # offset:
32 # Offset of this attribute's value in the stream (absolute offset, relative
33 # the beginning of the whole stream)
34 #
35 AttributeValue = namedtuple(
36 'AttributeValue', 'name form value raw_value offset')
37
38
39 class DIE(object):
40 """ A DWARF debugging information entry. On creation, parses itself from
41 the stream. Each DIE is held by a CU.
42
43 Accessible attributes:
44
45 tag:
46 The DIE tag
47
48 size:
49 The size this DIE occupies in the section
50
51 offset:
52 The offset of this DIE in the stream
53
54 attributes:
55 An ordered dictionary mapping attribute names to values. It's
56 ordered to preserve the order of attributes in the section
57
58 has_children:
59 Specifies whether this DIE has children
60
61 abbrev_code:
62 The abbreviation code pointing to an abbreviation entry (not
63 that this is for informational pusposes only - this object
64 interacts with its abbreviation table transparently).
65
66 See also the public methods.
67 """
68 def __init__(self, cu, stream, offset):
69 """ cu:
70 CompileUnit object this DIE belongs to. Used to obtain context
71 information (structs, abbrev table, etc.)
72
73 stream, offset:
74 The stream and offset into it where this DIE's data is located
75 """
76 self.cu = cu
77 self.dwarfinfo = self.cu.dwarfinfo # get DWARFInfo context
78 self.stream = stream
79 self.offset = offset
80
81 self.attributes = OrderedDict()
82 self.tag = None
83 self.has_children = None
84 self.abbrev_code = None
85 self.size = 0
86 self._children = []
87 self._parent = None
88
89 self._parse_DIE()
90
91 def is_null(self):
92 """ Is this a null entry?
93 """
94 return self.tag is None
95
96 def get_parent(self):
97 """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a
98 top-level DIE).
99 """
100 return self._parent
101
102 def iter_children(self):
103 """ Yield all children of this DIE
104 """
105 return iter(self._children)
106
107 def iter_siblings(self):
108 """ Yield all siblings of this DIE
109 """
110 if self._parent:
111 for sibling in self._parent.iter_children():
112 if sibling is not self:
113 yield sibling
114 else:
115 raise StopIteration()
116
117 # The following methods are used while creating the DIE and should not be
118 # interesting to consumers
119 #
120 def add_child(self, die):
121 self._children.append(die)
122
123 def set_parent(self, die):
124 self._parent = die
125
126 #------ PRIVATE ------#
127
128 def __repr__(self):
129 s = 'DIE %s, size=%s, has_chidren=%s\n' % (
130 self.tag, self.size, self.has_children)
131 for attrname, attrval in self.attributes.iteritems():
132 s += ' |%-18s: %s\n' % (attrname, attrval)
133 return s
134
135 def __str__(self):
136 return self.__repr__()
137
138 def _parse_DIE(self):
139 """ Parses the DIE info from the section, based on the abbreviation
140 table of the CU
141 """
142 structs = self.cu.structs
143
144 # A DIE begins with the abbreviation code. Read it and use it to
145 # obtain the abbrev declaration for this DIE.
146 # Note: here and elsewhere, preserve_stream_pos is used on operations
147 # that manipulate the stream by reading data from it.
148 #
149 self.abbrev_code = struct_parse(
150 structs.Dwarf_uleb128(''), self.stream, self.offset)
151
152 # This may be a null entry
153 if self.abbrev_code == 0:
154 self.size = self.stream.tell() - self.offset
155 return
156
157 with preserve_stream_pos(self.stream):
158 abbrev_decl = self.cu.get_abbrev_table().get_abbrev(
159 self.abbrev_code)
160 self.tag = abbrev_decl['tag']
161 self.has_children = abbrev_decl.has_children()
162
163 # The offset of the .debug_info section in the stream. Used to compute
164 # relative offset of attribute values to the beginning of the section.
165 section_offset = self.dwarfinfo.debug_info_sec.global_offset
166
167 # Guided by the attributes listed in the abbreviation declaration, parse
168 # values from the stream.
169 #
170 for name, form in abbrev_decl.iter_attr_specs():
171 attr_offset = self.stream.tell()
172 raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
173
174 value = self._translate_attr_value(form, raw_value)
175 self.attributes[name] = AttributeValue(
176 name=name,
177 form=form,
178 value=value,
179 raw_value=raw_value,
180 offset=attr_offset)
181
182 self.size = self.stream.tell() - self.offset
183
184 def _translate_attr_value(self, form, raw_value):
185 """ Translate a raw attr value according to the form
186 """
187 value = None
188 if form == 'DW_FORM_strp':
189 with preserve_stream_pos(self.stream):
190 value = self.dwarfinfo.get_string_from_table(raw_value)
191 elif form == 'DW_FORM_flag':
192 value = not raw_value == 0
193 elif form == 'DW_FORM_indirect':
194 form = raw_value
195 raw_value = struct_parse(
196 structs.Dwarf_dw_form[form], self.stream)
197 # Let's hope this doesn't get too deep :-)
198 return self._translate_attr_value(form, raw_value)
199 else:
200 value = raw_value
201 return value
202
203