Update version to 0.25 to prepare for release
[pyelftools.git] / elftools / dwarf / die.py
old mode 100644 (file)
new mode 100755 (executable)
index f49efc9..184ff8c
@@ -6,15 +6,21 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-from collections import namedtuple
+from collections import namedtuple, OrderedDict
+import os
 
-from ..common.ordereddict import OrderedDict
+from ..common.exceptions import DWARFError
+from ..common.py3compat import 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: 
+# AttributeValue - describes an attribute value in the DIE:
 #
-# form: 
+# name:
+#   The name (DW_AT_*) of this attribute
+#
+# form:
 #   The DW_FORM_* name of this attribute
 #
 # value:
@@ -26,42 +32,47 @@ from ..common.utils import struct_parse, preserve_stream_pos
 #   (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
+#   Offset of this attribute's value in the stream (absolute offset, relative
+#   the beginning of the whole stream)
 #
 AttributeValue = namedtuple(
-    'AttributeValue', 'form value raw_value offset')
+    '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:
-        
+
             tag:
                 The DIE tag
-        
+
             size:
                 The size this DIE occupies in the section
-            
+
             offset:
                 The offset of this DIE in the stream
-            
+
             attributes:
-                An ordered dictionary mapping attribute names to values. It's 
-                ordered to enable both efficient name->value mapping and
-                preserve the order of attributes in the section
-            
+                An ordered dictionary mapping attribute names to values. It's
+                ordered to preserve the order of attributes in the section
+
             has_children:
                 Specifies whether this DIE has children
-        
+
+            abbrev_code:
+                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
         """
@@ -69,32 +80,47 @@ class DIE(object):
         self.dwarfinfo = self.cu.dwarfinfo # get DWARFInfo context
         self.stream = 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
-        
-        self._parse_DIE()   
-    
+
+        self._parse_DIE()
+
     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 
+        """ 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
         """
@@ -110,59 +136,73 @@ class DIE(object):
     #
     def add_child(self, die):
         self._children.append(die)
-    
+
     def set_parent(self, die):
         self._parent = die
 
     #------ PRIVATE ------#
-    
+
     def __repr__(self):
-        s = 'DIE %s, size=%s, has_chidren=%s\n' % (
+        s = 'DIE %s, size=%s, has_children=%s\n' % (
             self.tag, self.size, self.has_children)
-        for attrname, attrval in self.attributes.iteritems():
+        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 = self.cu.structs
-        
-        # A DIE begins with the abbreviation code. Read it and use it to 
+
+        # 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.
-        #
-        abbrev_code = struct_parse(
+        self.abbrev_code = struct_parse(
             structs.Dwarf_uleb128(''), self.stream, self.offset)
-        
+
         # This may be a null entry
-        if abbrev_code == 0:
+        if self.abbrev_code == 0:
             self.size = self.stream.tell() - self.offset
             return
-        
+
         with preserve_stream_pos(self.stream):
-            abbrev_decl = self.cu.get_abbrev_table().get_abbrev(abbrev_code)
+            abbrev_decl = self.cu.get_abbrev_table().get_abbrev(
+                self.abbrev_code)
         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 = self.stream.tell()
             raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
-            value = self._translate_attr_value(form, raw_value)            
+
+            value = self._translate_attr_value(form, raw_value)
             self.attributes[name] = AttributeValue(
+                name=name,
                 form=form,
                 value=value,
                 raw_value=raw_value,
                 offset=attr_offset)
-        
+
+        # Count and then consume any null termination bytes to avoid wrong die
+        # size calculation.
+        num_zero_terminators = 0
+        with preserve_stream_pos(self.stream):
+            while True:
+                if self.stream.read(1) == 0:
+                    num_zero_terminators += 1
+                else:
+                    break
+        if num_zero_terminators > 0:
+            # There was at least one zero termination -> consume all of them.
+            self.stream.read(num_zero_terminators)
+
         self.size = self.stream.tell() - self.offset
 
     def _translate_attr_value(self, form, raw_value):
@@ -175,13 +215,17 @@ class DIE(object):
         elif form == 'DW_FORM_flag':
             value = not raw_value == 0
         elif form == 'DW_FORM_indirect':
-            form = raw_value
+            try:
+                form = DW_FORM_raw2name[raw_value]
+            except KeyError as err:
+                raise DWARFError(
+                        'Found DW_FORM_indirect with unknown raw_value=' +
+                        str(raw_value))
+
             raw_value = struct_parse(
-                structs.Dwarf_dw_form[form], self.stream)
+                self.cu.structs.Dwarf_dw_form[form], self.stream)
             # Let's hope this doesn't get too deep :-)
             return self._translate_attr_value(form, raw_value)
         else:
             value = raw_value
         return value
-        
-         
\ No newline at end of file