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