location lists kinda working. need more docs & readelf support
authorEli Bendersky <eliben@gmail.com>
Wed, 28 Dec 2011 04:37:16 +0000 (06:37 +0200)
committerEli Bendersky <eliben@gmail.com>
Wed, 28 Dec 2011 04:37:16 +0000 (06:37 +0200)
elftools/dwarf/dwarfinfo.py
elftools/dwarf/locationlists.py
examples/dwarf_location_lists.py [new file with mode: 0644]
z.py

index cc2a7b902fcbeaf4aae1fa97bfee060a546effe9..665f4d492edfe1a76072e28ab80afe303b41a428 100644 (file)
@@ -16,6 +16,7 @@ from .compileunit import CompileUnit
 from .abbrevtable import AbbrevTable
 from .lineprogram import LineProgram
 from .callframe import CallFrameInfo
+from .locationlists import LocationLists
 
 
 # Describes a debug section
@@ -148,6 +149,12 @@ class DWARFInfo(object):
             base_structs=self.structs)
         return cfi.get_entries()
 
+    def location_lists(self):
+        """ Get a LocationLists object representing the .debug_loc section of
+            the DWARF data, or None if this section doesn't exist.
+        """
+        return LocationLists(self.debug_loc_sec.stream, self.structs)
+
     #------ PRIVATE ------#
 
     def _parse_CUs_iter(self):
index afe12e3333a5879c5624a208f3903f8ddf9bd160..2e08b0dd758b3d4b77d98ff639cb75f9d612effe 100644 (file)
@@ -6,6 +6,7 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
+import os
 from collections import namedtuple
 
 from ..common.utils import struct_parse
@@ -16,14 +17,32 @@ BaseAddressEntry = namedtuple('BaseAddressEntry', 'base_address')
 
 
 class LocationLists(object):
+    """ A single location list is a Python list consisting of LocationEntry or
+        BaseAddressEntry objects.
+    """
     def __init__(self, stream, structs):
+        self.stream = stream
+        self.structs = structs
         self._max_addr = 2 ** (self.structs.address_size * 8) - 1
         
     def get_location_list_at_offset(self, offset):
-        pass
+        """ Get a location list at the given offset in the section.
+        """
+        self.stream.seek(offset, os.SEEK_SET)
+        return self._parse_location_list_from_stream()
 
     def iter_location_lists(self):
-        pass
+        """ Yield all location lists found in the section.
+        """
+        # Just call _parse_location_list_from_stream until the stream ends
+        self.stream.seek(0, os.SEEK_END)
+        endpos = self.stream.tell()
+
+        self.stream.seek(0, os.SEEK_SET)
+        while self.stream.tell() < endpos:
+            yield self._parse_location_list_from_stream()
+
+    #------ PRIVATE ------#
 
     def _parse_location_list_from_stream(self):
         lst = []
@@ -36,9 +55,18 @@ class LocationLists(object):
                 # End of list - we're done.
                 break
             elif begin_offset == self._max_addr:
-                # base
+                # Base address selection entry
+                lst.append(BaseAddressEntry(base_address=end_offset))
             else: 
-                # entry...
-        
-
+                # Location list entry
+                expr_len = struct_parse(
+                    self.structs.Dwarf_uint16(''), self.stream)
+                loc_expr = [struct_parse(self.structs.Dwarf_uint8(''),
+                                         self.stream)
+                                for i in range(expr_len)]
+                lst.append(LocationEntry(
+                    begin_offset=begin_offset,
+                    end_offset=end_offset,
+                    loc_expr=loc_expr))
+        return lst
 
diff --git a/examples/dwarf_location_lists.py b/examples/dwarf_location_lists.py
new file mode 100644 (file)
index 0000000..266bceb
--- /dev/null
@@ -0,0 +1,76 @@
+#-------------------------------------------------------------------------------
+# elftools example: dwarf_location_lists.py
+#
+# Examine DIE entries which have location list values, and decode these
+# location lists.
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from __future__ import print_function
+import sys
+
+# If elftools is not installed, maybe we're running from the root or examples
+# dir of the source distribution
+try:
+    import elftools
+except ImportError:
+    sys.path.extend(['.', '..'])
+
+from elftools.elf.elffile import ELFFile
+
+
+def process_file(filename):
+    print('Processing file:', filename)
+    with open(filename) as f:
+        elffile = ELFFile(f)
+
+        if not elffile.has_dwarf_info():
+            print('  file has no DWARF info')
+            return
+
+        # get_dwarf_info returns a DWARFInfo context object, which is the
+        # starting point for all DWARF-based processing in pyelftools.
+        dwarfinfo = elffile.get_dwarf_info()
+
+        # The location lists are extracted by DWARFInfo from the .debug_loc
+        # section, and returned here as a LocationLists object.
+        location_lists = dwarfinfo.location_lists()
+
+        for CU in dwarfinfo.iter_CUs():
+            # DWARFInfo allows to iterate over the compile units contained in
+            # the .debug_info section. CU is a CompileUnit object, with some
+            # computed attributes (such as its offset in the section) and
+            # a header which conforms to the DWARF standard. The access to
+            # header elements is, as usual, via item-lookup.
+            print('  Found a compile unit at offset %s, length %s' % (
+                CU.cu_offset, CU['unit_length']))
+
+            # A CU provides a simple API to iterate over all the DIEs in it.
+            for DIE in CU.iter_DIEs():
+                # Go over all attributes of the DIE. Each attribute is an
+                # AttributeValue object (from elftools.dwarf.die), which we
+                # can examine.
+                for attr in DIE.attributes.itervalues():
+                    if (    attr.name == 'DW_AT_location' and
+                            attr.form in ('DW_FORM_data4', 'DW_FORM_data8')):
+                        # This is a location list. Its value is an offset into
+                        # the .debug_loc section, so we can use the location
+                        # lists object to decode it.
+                        loclist = location_lists.get_location_list_at_offset(
+                            attr.value)
+                        print('   DIE %s. attr %s.\n      %s' % (
+                            DIE.tag,
+                            attr.name,
+                            loclist))
+
+
+if __name__ == '__main__':
+    for filename in sys.argv[1:]:
+        process_file(filename)
+
+
+
+
+
+
diff --git a/z.py b/z.py
index e5f26b7c627e0ef126686ce53597f33db9bb08c2..f04c4cd8bf765d304ad9f633bdad2fe96aed533a 100644 (file)
--- a/z.py
+++ b/z.py
@@ -16,3 +16,13 @@ efile = ELFFile(stream)
 print 'elfclass', efile.elfclass
 print '===> %s sections!' % efile.num_sections() 
 print efile.header
+
+dinfo = efile.get_dwarf_info()
+from elftools.dwarf.locationlists import LocationLists
+from elftools.dwarf.descriptions import describe_DWARF_expr
+llists = LocationLists(dinfo.debug_loc_sec.stream, dinfo.structs)
+for li in  llists.get_location_list_at_offset(0):
+    print li
+    print describe_DWARF_expr(li.loc_expr, dinfo.structs)
+
+