1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/die.py
4 # DWARF Debugging Information Entry
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from collections
import namedtuple
, OrderedDict
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
18 # AttributeValue - describes an attribute value in the DIE:
21 # The name (DW_AT_*) of this attribute
24 # The DW_FORM_* name of this attribute
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)
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)
35 # Offset of this attribute's value in the stream (absolute offset, relative
36 # the beginning of the whole stream)
38 AttributeValue
= namedtuple(
39 'AttributeValue', 'name form value raw_value offset')
43 """ A DWARF debugging information entry. On creation, parses itself from
44 the stream. Each DIE is held by a CU.
46 Accessible attributes:
52 The size this DIE occupies in the section
55 The offset of this DIE in the stream
58 An ordered dictionary mapping attribute names to values. It's
59 ordered to preserve the order of attributes in the section
62 Specifies whether this DIE has children
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).
69 See also the public methods.
71 def __init__(self
, cu
, stream
, offset
):
73 CompileUnit object this DIE belongs to. Used to obtain context
74 information (structs, abbrev table, etc.)
77 The stream and offset into it where this DIE's data is located
80 self
.dwarfinfo
= self
.cu
.dwarfinfo
# get DWARFInfo context
84 self
.attributes
= OrderedDict()
86 self
.has_children
= None
87 self
.abbrev_code
= None
95 """ Is this a null entry?
97 return self
.tag
is None
100 """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a
105 def get_full_path(self
):
106 """ Return the full path filename for the DIE.
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.
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
)
119 def iter_children(self
):
120 """ Yield all children of this DIE
122 return iter(self
._children
)
124 def iter_siblings(self
):
125 """ Yield all siblings of this DIE
128 for sibling
in self
._parent
.iter_children():
129 if sibling
is not self
:
132 raise StopIteration()
134 # The following methods are used while creating the DIE and should not be
135 # interesting to consumers
137 def add_child(self
, die
):
138 self
._children
.append(die
)
140 def set_parent(self
, die
):
143 #------ PRIVATE ------#
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
)
153 return self
.__repr
__()
155 def _parse_DIE(self
):
156 """ Parses the DIE info from the section, based on the abbreviation
159 structs
= self
.cu
.structs
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
)
168 # This may be a null entry
169 if self
.abbrev_code
== 0:
170 self
.size
= self
.stream
.tell() - self
.offset
173 with
preserve_stream_pos(self
.stream
):
174 abbrev_decl
= self
.cu
.get_abbrev_table().get_abbrev(
176 self
.tag
= abbrev_decl
['tag']
177 self
.has_children
= abbrev_decl
.has_children()
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
)
185 value
= self
._translate
_attr
_value
(form
, raw_value
)
186 self
.attributes
[name
] = AttributeValue(
193 # Count and then consume any null termination bytes to avoid wrong die
195 num_zero_terminators
= 0
196 with
preserve_stream_pos(self
.stream
):
198 if self
.stream
.read(1) == 0:
199 num_zero_terminators
+= 1
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
)
206 self
.size
= self
.stream
.tell() - self
.offset
208 def _translate_attr_value(self
, form
, raw_value
):
209 """ Translate a raw attr value according to the form
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':
219 form
= DW_FORM_raw2name
[raw_value
]
220 except KeyError as err
:
222 'Found DW_FORM_indirect with unknown raw_value=' +
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
)