added Dwarf_lineprog_header struct, with some rudimentary tests
authorEli Bendersky <eliben@gmail.com>
Fri, 2 Dec 2011 07:03:08 +0000 (09:03 +0200)
committerEli Bendersky <eliben@gmail.com>
Fri, 2 Dec 2011 07:03:08 +0000 (09:03 +0200)
elftools/common/construct_utils.py [new file with mode: 0644]
elftools/dwarf/structs.py
tests/test_dwarf_structs.py [new file with mode: 0644]

diff --git a/elftools/common/construct_utils.py b/elftools/common/construct_utils.py
new file mode 100644 (file)
index 0000000..d3e311a
--- /dev/null
@@ -0,0 +1,45 @@
+#-------------------------------------------------------------------------------
+# elftools: common/construct_utils.py
+#
+# Some complementary construct utilities
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from ..construct import Subconstruct, ConstructError, ArrayError
+
+
+class RepeatUntilExcluding(Subconstruct):
+    """ A version of construct's RepeatUntil that doesn't include the last 
+        element (which casued the repeat to exit) in the return value.
+        
+        Only parsing is currently implemented.
+        
+        P.S. removed some code duplication
+    """
+    __slots__ = ["predicate"]
+    def __init__(self, predicate, subcon):
+        Subconstruct.__init__(self, subcon)
+        self.predicate = predicate
+        self._clear_flag(self.FLAG_COPY_CONTEXT)
+        self._set_flag(self.FLAG_DYNAMIC)
+    def _parse(self, stream, context):
+        obj = []
+        try:
+            context_for_subcon = context
+            if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+                context_for_subcon = context.__copy__()
+            
+            while True:
+                subobj = self.subcon._parse(stream, context_for_subcon)
+                if self.predicate(subobj, context):
+                    break
+                obj.append(subobj)
+        except ConstructError, ex:
+            raise ArrayError("missing terminator", ex)
+        return obj
+    def _build(self, obj, stream, context):
+        raise NotImplementedError('no building')
+    def _sizeof(self, context):
+        raise SizeofError("can't calculate size")
+
index a35f3620a5e1bcd86b85a0eb427b723224ca5be6..6d71657a1b15c5814a353ae80dccf624528e9d6d 100644 (file)
@@ -11,8 +11,9 @@ from ..construct import (
     UBInt8, UBInt16, UBInt32, UBInt64, ULInt8, ULInt16, ULInt32, ULInt64,
     SBInt8, SBInt16, SBInt32, SBInt64, SLInt8, SLInt16, SLInt32, SLInt64,
     Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename, Enum,
-    PrefixedArray, CString,
+    Array, PrefixedArray, CString, Embed,
     )
+from ..common.construct_utils import RepeatUntilExcluding
 
 from .enums import *
 
@@ -55,6 +56,9 @@ class DWARFStructs(object):
                 A dictionary mapping 'DW_FORM_*' keys into construct Structs
                 that parse such forms. These Structs have already been given
                 dummy names.
+
+            Dwarf_lineprog_header (+):
+                Line program header
         
         See also the documentation of public methods.
     """
@@ -112,6 +116,7 @@ class DWARFStructs(object):
         self._create_cu_header()
         self._create_abbrev_declaration()
         self._create_dw_form()
+        self._create_lineprog_header()
 
     def _create_initial_length(self):
         def _InitialLength(name):
@@ -180,6 +185,36 @@ class DWARFStructs(object):
             DW_FORM_indirect=self.Dwarf_uleb128(''),
         )
 
+    def _create_lineprog_header(self):
+        # A file entry is terminated by a NULL byte, so we don't want to parse
+        # past it. Therefore an If is used.
+        file_entry = Struct('file_entry',
+            CString('name'),
+            If(lambda ctx: len(ctx.name) != 0,
+                Embed(Struct('',
+                    self.Dwarf_uleb128('dir_index'),
+                    self.Dwarf_uleb128('mtime'),
+                    self.Dwarf_uleb128('length')))))
+
+        self.Dwarf_lineprog_header = Struct('Dwarf_lineprog_header',
+            self.Dwarf_initial_length('unit_length'),
+            self.Dwarf_uint16('version'),
+            self.Dwarf_offset('header_length'),
+            self.Dwarf_uint8('minimum_instruction_length'),
+            self.Dwarf_uint8('default_is_stmt'),
+            self.Dwarf_int8('line_base'),
+            self.Dwarf_uint8('line_range'),
+            self.Dwarf_uint8('opcode_base'),
+            Array(lambda ctx: ctx['opcode_base'] - 1, 
+                  self.Dwarf_uint8('standard_opcode_lengths')),
+            RepeatUntilExcluding(
+                lambda obj, ctx: obj == '',
+                CString('include_directory')),
+            RepeatUntilExcluding(
+                lambda obj, ctx: len(obj.name) == 0,
+                file_entry),
+            )
+        
     def _make_block_struct(self, length_field):
         """ Create a struct for DW_FORM_block<size> 
         """
diff --git a/tests/test_dwarf_structs.py b/tests/test_dwarf_structs.py
new file mode 100644 (file)
index 0000000..632aa66
--- /dev/null
@@ -0,0 +1,39 @@
+import sys, unittest
+
+sys.path.extend(['.', '..'])
+from elftools.dwarf.structs import DWARFStructs
+
+
+class TestDWARFStructs(unittest.TestCase):
+    def test_lineprog_header(self):
+        ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
+
+        c = ds.Dwarf_lineprog_header.parse(
+                '\x04\x10\x00\x00' +    # initial lenght
+                '\x05\x02' +            # version
+                '\x20\x00\x00\x00' +    # header length
+                '\x05\x10\x40\x50' +    # until and including line_range
+                '\x06' +                # opcode_base
+                '\x00\x01\x04\x08\x0C' + # standard_opcode_lengths
+                # 2 dir names followed by a NULL
+                '\x61\x62\x00\x70\x00\x00' + 
+                # a file entry
+                '\x61\x72\x00\x0C\x0D\x0F' + 
+                # and another entry
+                '\x45\x50\x51\x00\x86\x12\x07\x08' +
+                # followed by NULL
+                '\x00')
+
+        self.assertEqual(c.version, 0x205)
+        self.assertEqual(c.opcode_base, 6)
+        self.assertEqual(c.standard_opcode_lengths, [0, 1, 4, 8, 12])
+        self.assertEqual(c.include_directory, ['ab', 'p'])
+        self.assertEqual(len(c.file_entry), 2)
+        self.assertEqual(c.file_entry[0].name, 'ar')
+        self.assertEqual(c.file_entry[1].name, 'EPQ')
+        self.assertEqual(c.file_entry[1].dir_index, 0x12 * 128 + 6)
+
+
+if __name__ == '__main__':
+    unittest.main()
+