blob: 86830fc3bedaf9a2a0538f4f29adcc08ae1f5791 [file] [log] [blame]
# elftools: dwarf/
# DWARF Debugging Information Entry
# Eli Bendersky (
# This code is in the public domain
from collections import namedtuple
import os
from ..common.exceptions import DWARFError
from ..common.py3compat import OrderedDict, bytes2str, iteritems
from ..common.utils import struct_parse, preserve_stream_pos
from .enums import DW_FORM_raw2name
# AttributeValue - describes an attribute value in the DIE:
# name:
# The name (DW_AT_*) of this attribute
# form:
# The DW_FORM_* name of this attribute
# value:
# The value parsed from the section and translated accordingly to the form
# (e.g. for a DW_FORM_strp it's the actual string taken from the string table)
# raw_value:
# Raw value as parsed from the section - used for debugging and presentation
# (e.g. for a DW_FORM_strp it's the raw string offset into the table)
# offset:
# Offset of this attribute's value in the stream (absolute offset, relative
# the beginning of the whole stream)
AttributeValue = namedtuple(
'AttributeValue', 'name form value raw_value offset')
class DIE(object):
""" A DWARF debugging information entry. On creation, parses itself from
the stream. Each DIE is held by a CU.
Accessible attributes:
The DIE tag
The size this DIE occupies in the section
The offset of this DIE in the stream
An ordered dictionary mapping attribute names to values. It's
ordered to preserve the order of attributes in the section
Specifies whether this DIE has children
The abbreviation code pointing to an abbreviation entry (note
that this is for informational pusposes only - this object
interacts with its abbreviation table transparently).
See also the public methods.
def __init__(self, cu, stream, offset):
""" cu:
CompileUnit object this DIE belongs to. Used to obtain context
information (structs, abbrev table, etc.)
stream, offset:
The stream and offset into it where this DIE's data is located
""" = cu
self.dwarfinfo = # get DWARFInfo context = stream
self.offset = offset
self.attributes = OrderedDict()
self.tag = None
self.has_children = None
self.abbrev_code = None
self.size = 0
self._children = []
self._parent = None
def is_null(self):
""" Is this a null entry?
return self.tag is None
def get_parent(self):
""" The parent DIE of this DIE. None if the DIE has no parent (i.e. a
top-level DIE).
return self._parent
def get_full_path(self):
""" Return the full path filename for the DIE.
The filename is the join of 'DW_AT_comp_dir' and 'DW_AT_name',
either of which may be missing in practice. Note that its value is
usually a string taken from the .debug_string section and the
returned value will be a string.
comp_dir_attr = self.attributes.get('DW_AT_comp_dir', None)
comp_dir = bytes2str(comp_dir_attr.value) if comp_dir_attr else ''
fname_attr = self.attributes.get('DW_AT_name', None)
fname = bytes2str(fname_attr.value) if fname_attr else ''
return os.path.join(comp_dir, fname)
def iter_children(self):
""" Yield all children of this DIE
return iter(self._children)
def iter_siblings(self):
""" Yield all siblings of this DIE
if self._parent:
for sibling in self._parent.iter_children():
if sibling is not self:
yield sibling
raise StopIteration()
# The following methods are used while creating the DIE and should not be
# interesting to consumers
def add_child(self, die):
def set_parent(self, die):
self._parent = die
#------ PRIVATE ------#
def __repr__(self):
s = 'DIE %s, size=%s, has_chidren=%s\n' % (
self.tag, self.size, self.has_children)
for attrname, attrval in iteritems(self.attributes):
s += ' |%-18s: %s\n' % (attrname, attrval)
return s
def __str__(self):
return self.__repr__()
def _parse_DIE(self):
""" Parses the DIE info from the section, based on the abbreviation
table of the CU
structs =
# A DIE begins with the abbreviation code. Read it and use it to
# obtain the abbrev declaration for this DIE.
# Note: here and elsewhere, preserve_stream_pos is used on operations
# that manipulate the stream by reading data from it.
self.abbrev_code = struct_parse(
structs.Dwarf_uleb128(''),, self.offset)
# This may be a null entry
if self.abbrev_code == 0:
self.size = - self.offset
with preserve_stream_pos(
abbrev_decl =
self.tag = abbrev_decl['tag']
self.has_children = abbrev_decl.has_children()
# Guided by the attributes listed in the abbreviation declaration, parse
# values from the stream.
for name, form in abbrev_decl.iter_attr_specs():
attr_offset =
raw_value = struct_parse(structs.Dwarf_dw_form[form],
value = self._translate_attr_value(form, raw_value)
self.attributes[name] = AttributeValue(
self.size = - self.offset
def _translate_attr_value(self, form, raw_value):
""" Translate a raw attr value according to the form
value = None
if form == 'DW_FORM_strp':
with preserve_stream_pos(
value = self.dwarfinfo.get_string_from_table(raw_value)
elif form == 'DW_FORM_flag':
value = not raw_value == 0
elif form == 'DW_FORM_indirect':
form = DW_FORM_raw2name[raw_value]
except KeyError as err:
raise DWARFError(
'Found DW_FORM_indirect with unknown raw_value=' +
raw_value = struct_parse([form],
# Let's hope this doesn't get too deep :-)
return self._translate_attr_value(form, raw_value)
value = raw_value
return value