DIE.__repr__ typo: s/chidren/children/ (#161)
[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, OrderedDict
10 import os
11
12 from ..common.exceptions import DWARFError
13 from ..common.py3compat import bytes2str, iteritems
14 from ..common.utils import struct_parse, preserve_stream_pos
15 from .enums import DW_FORM_raw2name
16
17
18 # AttributeValue - describes an attribute value in the DIE:
19 #
20 # name:
21 # The name (DW_AT_*) of this attribute
22 #
23 # form:
24 # The DW_FORM_* name of this attribute
25 #
26 # value:
27 # The value parsed from the section and translated accordingly to the form
28 # (e.g. for a DW_FORM_strp it's the actual string taken from the string table)
29 #
30 # raw_value:
31 # Raw value as parsed from the section - used for debugging and presentation
32 # (e.g. for a DW_FORM_strp it's the raw string offset into the table)
33 #
34 # offset:
35 # Offset of this attribute's value in the stream (absolute offset, relative
36 # the beginning of the whole stream)
37 #
38 AttributeValue = namedtuple(
39 'AttributeValue', 'name form value raw_value offset')
40
41
42 class DIE(object):
43 """ A DWARF debugging information entry. On creation, parses itself from
44 the stream. Each DIE is held by a CU.
45
46 Accessible attributes:
47
48 tag:
49 The DIE tag
50
51 size:
52 The size this DIE occupies in the section
53
54 offset:
55 The offset of this DIE in the stream
56
57 attributes:
58 An ordered dictionary mapping attribute names to values. It's
59 ordered to preserve the order of attributes in the section
60
61 has_children:
62 Specifies whether this DIE has children
63
64 abbrev_code:
65 The abbreviation code pointing to an abbreviation entry (note
66 that this is for informational pusposes only - this object
67 interacts with its abbreviation table transparently).
68
69 See also the public methods.
70 """
71 def __init__(self, cu, stream, offset):
72 """ cu:
73 CompileUnit object this DIE belongs to. Used to obtain context
74 information (structs, abbrev table, etc.)
75
76 stream, offset:
77 The stream and offset into it where this DIE's data is located
78 """
79 self.cu = cu
80 self.dwarfinfo = self.cu.dwarfinfo # get DWARFInfo context
81 self.stream = stream
82 self.offset = offset
83
84 self.attributes = OrderedDict()
85 self.tag = None
86 self.has_children = None
87 self.abbrev_code = None
88 self.size = 0
89 self._children = []
90 self._parent = None
91
92 self._parse_DIE()
93
94 def is_null(self):
95 """ Is this a null entry?
96 """
97 return self.tag is None
98
99 def get_parent(self):
100 """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a
101 top-level DIE).
102 """
103 return self._parent
104
105 def get_full_path(self):
106 """ Return the full path filename for the DIE.
107
108 The filename is the join of 'DW_AT_comp_dir' and 'DW_AT_name',
109 either of which may be missing in practice. Note that its value is
110 usually a string taken from the .debug_string section and the
111 returned value will be a string.
112 """
113 comp_dir_attr = self.attributes.get('DW_AT_comp_dir', None)
114 comp_dir = bytes2str(comp_dir_attr.value) if comp_dir_attr else ''
115 fname_attr = self.attributes.get('DW_AT_name', None)
116 fname = bytes2str(fname_attr.value) if fname_attr else ''
117 return os.path.join(comp_dir, fname)
118
119 def iter_children(self):
120 """ Yield all children of this DIE
121 """
122 return iter(self._children)
123
124 def iter_siblings(self):
125 """ Yield all siblings of this DIE
126 """
127 if self._parent:
128 for sibling in self._parent.iter_children():
129 if sibling is not self:
130 yield sibling
131 else:
132 raise StopIteration()
133
134 # The following methods are used while creating the DIE and should not be
135 # interesting to consumers
136 #
137 def add_child(self, die):
138 self._children.append(die)
139
140 def set_parent(self, die):
141 self._parent = die
142
143 #------ PRIVATE ------#
144
145 def __repr__(self):
146 s = 'DIE %s, size=%s, has_children=%s\n' % (
147 self.tag, self.size, self.has_children)
148 for attrname, attrval in iteritems(self.attributes):
149 s += ' |%-18s: %s\n' % (attrname, attrval)
150 return s
151
152 def __str__(self):
153 return self.__repr__()
154
155 def _parse_DIE(self):
156 """ Parses the DIE info from the section, based on the abbreviation
157 table of the CU
158 """
159 structs = self.cu.structs
160
161 # A DIE begins with the abbreviation code. Read it and use it to
162 # obtain the abbrev declaration for this DIE.
163 # Note: here and elsewhere, preserve_stream_pos is used on operations
164 # that manipulate the stream by reading data from it.
165 self.abbrev_code = struct_parse(
166 structs.Dwarf_uleb128(''), self.stream, self.offset)
167
168 # This may be a null entry
169 if self.abbrev_code == 0:
170 self.size = self.stream.tell() - self.offset
171 return
172
173 with preserve_stream_pos(self.stream):
174 abbrev_decl = self.cu.get_abbrev_table().get_abbrev(
175 self.abbrev_code)
176 self.tag = abbrev_decl['tag']
177 self.has_children = abbrev_decl.has_children()
178
179 # Guided by the attributes listed in the abbreviation declaration, parse
180 # values from the stream.
181 for name, form in abbrev_decl.iter_attr_specs():
182 attr_offset = self.stream.tell()
183 raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
184
185 value = self._translate_attr_value(form, raw_value)
186 self.attributes[name] = AttributeValue(
187 name=name,
188 form=form,
189 value=value,
190 raw_value=raw_value,
191 offset=attr_offset)
192
193 # Count and then consume any null termination bytes to avoid wrong die
194 # size calculation.
195 num_zero_terminators = 0
196 with preserve_stream_pos(self.stream):
197 while True:
198 if self.stream.read(1) == 0:
199 num_zero_terminators += 1
200 else:
201 break
202 if num_zero_terminators > 0:
203 # There was at least one zero termination -> consume all of them.
204 self.stream.read(num_zero_terminators)
205
206 self.size = self.stream.tell() - self.offset
207
208 def _translate_attr_value(self, form, raw_value):
209 """ Translate a raw attr value according to the form
210 """
211 value = None
212 if form == 'DW_FORM_strp':
213 with preserve_stream_pos(self.stream):
214 value = self.dwarfinfo.get_string_from_table(raw_value)
215 elif form == 'DW_FORM_flag':
216 value = not raw_value == 0
217 elif form == 'DW_FORM_indirect':
218 try:
219 form = DW_FORM_raw2name[raw_value]
220 except KeyError as err:
221 raise DWARFError(
222 'Found DW_FORM_indirect with unknown raw_value=' +
223 str(raw_value))
224
225 raw_value = struct_parse(
226 self.cu.structs.Dwarf_dw_form[form], self.stream)
227 # Let's hope this doesn't get too deep :-)
228 return self._translate_attr_value(form, raw_value)
229 else:
230 value = raw_value
231 return value