ad818920c7c9650ee2e81c0ce710e9b218f77db8
[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 abbrev_code:
59 The abbreviation code pointing to an abbreviation entry (not
60 that this is for informational pusposes only - this object
61 interacts with its abbreviation table transparently).
62
63 See also the public methods.
64 """
65 def __init__(self, cu, stream, offset):
66 """ cu:
67 CompileUnit object this DIE belongs to. Used to obtain context
68 information (structs, abbrev table, etc.)
69
70 stream, offset:
71 The stream and offset into it where this DIE's data is located
72 """
73 self.cu = cu
74 self.dwarfinfo = self.cu.dwarfinfo # get DWARFInfo context
75 self.stream = stream
76 self.offset = offset
77
78 self.attributes = OrderedDict()
79 self.tag = None
80 self.has_children = None
81 self.abbrev_code = None
82 self.size = 0
83 self._children = []
84 self._parent = None
85
86 self._parse_DIE()
87
88 def is_null(self):
89 """ Is this a null entry?
90 """
91 return self.tag is None
92
93 def get_parent(self):
94 """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a
95 top-level DIE).
96 """
97 return self._parent
98
99 def iter_children(self):
100 """ Yield all children of this DIE
101 """
102 return iter(self._children)
103
104 def iter_siblings(self):
105 """ Yield all siblings of this DIE
106 """
107 if self._parent:
108 for sibling in self._parent.iter_children():
109 if sibling is not self:
110 yield sibling
111 else:
112 raise StopIteration()
113
114 # The following methods are used while creating the DIE and should not be
115 # interesting to consumers
116 #
117 def add_child(self, die):
118 self._children.append(die)
119
120 def set_parent(self, die):
121 self._parent = die
122
123 #------ PRIVATE ------#
124
125 def __repr__(self):
126 s = 'DIE %s, size=%s, has_chidren=%s\n' % (
127 self.tag, self.size, self.has_children)
128 for attrname, attrval in self.attributes.iteritems():
129 s += ' |%-18s: %s\n' % (attrname, attrval)
130 return s
131
132 def __str__(self):
133 return self.__repr__()
134
135 def _parse_DIE(self):
136 """ Parses the DIE info from the section, based on the abbreviation
137 table of the CU
138 """
139 structs = self.cu.structs
140
141 # A DIE begins with the abbreviation code. Read it and use it to
142 # obtain the abbrev declaration for this DIE.
143 # Note: here and elsewhere, preserve_stream_pos is used on operations
144 # that manipulate the stream by reading data from it.
145 #
146 self.abbrev_code = struct_parse(
147 structs.Dwarf_uleb128(''), self.stream, self.offset)
148
149 # This may be a null entry
150 if self.abbrev_code == 0:
151 self.size = self.stream.tell() - self.offset
152 return
153
154 with preserve_stream_pos(self.stream):
155 abbrev_decl = self.cu.get_abbrev_table().get_abbrev(
156 self.abbrev_code)
157 self.tag = abbrev_decl['tag']
158 self.has_children = abbrev_decl.has_children()
159
160 # Guided by the attributes listed in the abbreviation declaration, parse
161 # values from the stream.
162 #
163 for name, form in abbrev_decl.iter_attr_specs():
164 attr_offset = self.stream.tell()
165 raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
166 value = self._translate_attr_value(form, raw_value)
167 self.attributes[name] = AttributeValue(
168 form=form,
169 value=value,
170 raw_value=raw_value,
171 offset=attr_offset)
172
173 self.size = self.stream.tell() - self.offset
174
175 def _translate_attr_value(self, form, raw_value):
176 """ Translate a raw attr value according to the form
177 """
178 value = None
179 if form == 'DW_FORM_strp':
180 with preserve_stream_pos(self.stream):
181 value = self.dwarfinfo.get_string_from_table(raw_value)
182 elif form == 'DW_FORM_flag':
183 value = not raw_value == 0
184 elif form == 'DW_FORM_indirect':
185 form = raw_value
186 raw_value = struct_parse(
187 structs.Dwarf_dw_form[form], self.stream)
188 # Let's hope this doesn't get too deep :-)
189 return self._translate_attr_value(form, raw_value)
190 else:
191 value = raw_value
192 return value
193
194