Added dwarf/compileunit.py and dwarf/dwarfinfo.py and started filling in details
authoreliben <devnull@localhost>
Mon, 19 Sep 2011 09:54:32 +0000 (12:54 +0300)
committereliben <devnull@localhost>
Mon, 19 Sep 2011 09:54:32 +0000 (12:54 +0300)
elftools/common/exceptions.py
elftools/common/utils.py
elftools/dwarf/compileunit.py [new file with mode: 0644]
elftools/dwarf/dwarfinfo.py [new file with mode: 0644]
elftools/dwarf/structs.py
elftools/elf/elffile.py
z.py
zd.py

index 8a40877f76e5027b2fb873c9e576075f6609ca0c..c2da24ca6e9ac5647ecee69c8e7cfcb6103e6b8c 100644 (file)
@@ -12,3 +12,6 @@ class ELFError(Exception):
 class ELFParseError(ELFError):
     pass
 
+class DWARFError(Exception):
+    pass
+
index 8b9b9ae7d98ac41774677e002ace9f5555aef822..c0990137bda3b9b43288b9f5f16b01952dead43f 100644 (file)
@@ -28,6 +28,15 @@ def struct_parse(struct, stream, stream_pos=None):
 def elf_assert(cond, msg=''):
     """ Assert that cond is True, otherwise raise ELFError(msg)
     """
-    if not cond:
-        raise ELFError(msg)
+    _assert_with_exception(cond, msg, ELFError)
+
+
+def dwarf_assert(cond, msg=''):
+    """ Assert that cond is True, otherwise raise DWARFError(msg)
+    """
+    _assert_with_exception(cond, msg, DWARFError)
+
 
+def _assert_with_exception(cond, msg, exception_type):
+    if not cond:
+        raise exception_type(msg)
\ No newline at end of file
diff --git a/elftools/dwarf/compileunit.py b/elftools/dwarf/compileunit.py
new file mode 100644 (file)
index 0000000..7791b86
--- /dev/null
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------------
+# elftools: dwarf/compileunit.py
+#
+# DWARF compile unit
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+
+
+class CompileUnit(object):
+    def __init__(self, header, cu_die):
+        self.header = header
+        self.cu_die
+    
+    def __getitem__(self, name):
+        """ Implement dict-like access to header entries
+        """
+        return self.header[name]
diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py
new file mode 100644 (file)
index 0000000..651c5da
--- /dev/null
@@ -0,0 +1,83 @@
+#-------------------------------------------------------------------------------
+# elftools: dwarf/dwarfinfo.py
+#
+# DWARFInfo - Main class for accessing DWARF debug information
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from collections import namedtuple
+
+from ..common.exceptions import DWARFError
+from ..common.utils import struct_parse, dwarf_assert
+from .structs import DWARFStructs
+from .compileunit import CompileUnit
+
+
+# Describes a debug section in a stream: offset and size
+#
+DebugSectionLocator = namedtuple('DebugSectionLocator', 'offset size')
+
+
+class DWARFInfo(object):
+    """ Creation: the constructor accepts a stream (file-like object) that
+        contains debug sections, along with locators (DebugSectionLocator)
+        of the required sections. In addition, little_endian is a boolean
+        parameter specifying endianity, and dwarfclass is 32 or 64, depending
+        on the type of file the DWARF info was read from.
+    """
+    def __init__(self, 
+            stream,
+            little_endian,
+            dwarfclass,
+            debug_info_loc,
+            debug_abbrev_loc,
+            debug_str_loc,
+            debug_line_loc):
+        self.stream = stream
+        self.debug_info_loc = debug_info_loc
+        self.debug_abbrev_loc = debug_abbrev_loc
+        self.debug_str_loc = debug_str_loc
+        self.debug_line_loc = debug_line_loc
+        
+        self.little_endian = little_endian
+        self.dwarfclass = dwarfclass
+        self.structs = DWARFStructs(
+            little_endian=self.little_endian,
+            dwarfclass=self.dwarfclass)
+        
+        self._CU = self._parse_CUs()
+    
+    def initial_lenght_field_size(self):
+        """ Size of an initial length field.
+        """
+        return 4 if self.dwarfclass == 32 else 12
+
+    def _parse_CUs(self):
+        """ Parse CU entries from debug_info and return them as a list of
+            containers.
+        """
+        offset = self.debug_info_loc.offset
+        section_boundary = self.debug_info_loc.offset + self.debug_info_loc.length
+        CUlist = []
+        while offset < section_boundary:
+            cu_header = struct_parse(
+                self.structs.Dwarf_CU_header, self.stream, offset)
+            dwarf_assert(self._is_supported_version(cu_header['version']))
+            CUlist.append(CompileUnit(cu_header, None))
+            # Compute the offset of the next CU in the section. The unit_length
+            # field of the CU header contains its size not including the length
+            # field itself.
+            offset = (  offset + 
+                        cu['unit_length'] + 
+                        self.initial_lenght_field_size())
+        return CUlist
+        
+    def _is_supported_version(self, version):
+        """ DWARF version supported by this parser
+        """
+        return 2 <= version <= 3
+
+
+
+
index e6aa81c4688601cc7661b0deb8473c94453e7a2c..104cee96a7dc23bae247684a2c7f5f9c7c86a117 100644 (file)
@@ -15,28 +15,31 @@ from ..construct import (
 
 
 class DWARFStructs(object):
-    """ Accessible attributes:
+    """ Accessible attributes (mostly described by in chapter 7 of the DWARF
+        spec v3):
     
             Dwarf_uint{8,16,32,64):
                 Data chunks of the common sizes
             
-            Dwarf_xword:
-                32-bit or 64-bit word, depending on dwarfclass
+            Dwarf_xword, Dwarf_offset:
+                32-bit or 64-bit word, depending on dwarfclass (xword and offset
+                are synonyms here).
             
             Dwarf_initial_length:
-                "Initial length field" encoding, as described in DWARFv3 spec 
+                "Initial length field" encoding
                 section 7.4
             
             Dwarf_{u,s}leb128:
-                ULEB128 and SLEB128 variable-length encoding, as described in
-                DWARFv3 spec section 7.6
+                ULEB128 and SLEB128 variable-length encoding
+            
+            Dwarf_CU_header:
+                Compilation unit header
     """
     def __init__(self, little_endian=True, dwarfclass=32):
         assert dwarfclass == 32 or dwarfclass == 64
         self.little_endian = little_endian
         self.dwarfclass = dwarfclass        
         self._create_structs()
-        self._create_leb128()
 
     def _create_structs(self):
         if self.little_endian:
@@ -51,9 +54,12 @@ class DWARFStructs(object):
             self.Dwarf_uint32 = UBInt32
             self.Dwarf_uint64 = UBInt64
             self.Dwarf_xword = UBInt32 if self.dwarfclass == 32 else UBInt64
+        self.Dwarf_offset = self.Dwarf_xword
 
         self._create_initial_length()
-    
+        self._create_leb128()
+        self._create_cu_header()
+
     def _create_initial_length(self):
         def _InitialLength(name):
             # Adapts a Struct that parses forward a full initial length field.
@@ -72,6 +78,13 @@ class DWARFStructs(object):
         self.Dwarf_uleb128 = _ULEB128
         self.Dwarf_sleb128 = _SLEB128
 
+    def _create_cu_header(self):
+        self.Dwarf_CU_header = Struct('Dwarf_CU_header',
+            self.Dwarf_initial_length('unit_length'),
+            self.Dwarf_uint16('version'),
+            self.Dwarf_offset('debug_abbrev_offset'),
+            self.Dwarf_uint8('address_size'))
+
 
 class _InitialLengthAdapter(Adapter):
     """ A standard Construct adapter that expects a sub-construct
index 2c00fb5839662cb76581b85e7cb2292152c1901b..8195c7f895026be7cfbaa4d42783280a59d168d5 100644 (file)
@@ -17,7 +17,10 @@ from .segments import Segment, InterpSegment
 
 
 class ELFFile(object):
-    """ Accessible attributes:
+    """ Creation: the constructor accepts a stream (file-like object) with the
+        contents of an ELF file.
+    
+        Accessible attributes:
         
             elfclass: 
                 32 or 64 - specifies the word size of the target machine
diff --git a/z.py b/z.py
index dc0eee9bfcfb04de2accd42a08b1d87261835bb8..bddd6e0127a303260f412e17f94bbaac63cc3f69 100644 (file)
--- a/z.py
+++ b/z.py
@@ -10,7 +10,7 @@ from elftools.elf.sections import *
 # read a little-endian, 64-bit file
 es = ELFStructs(True, 64)
 
-stream = open('binfiles/z.elf', 'rb')
+stream = open('tests/testfiles/z.elf', 'rb')
 #stream = open('binfiles/z32.elf', 'rb')
 
 efile = ELFFile(stream)
diff --git a/zd.py b/zd.py
index 62941005aeb500daec12bcb501fd46089ca29ec1..44e6dbfadc80c272f6b14af78df6ab46b08c855b 100644 (file)
--- a/zd.py
+++ b/zd.py
@@ -1,4 +1,9 @@
+# Just a script for playing around with pyelftools during testing\r
+# please ignore it!\r
+#\r
+\r
 from elftools.dwarf.structs import DWARFStructs\r
+from elftools.dwarf.dwarfinfo import DWARFInfo\r
 \r
 \r
 ds = DWARFStructs(\r