created elftools/dwarf and started writing down some basic structs for data primitive...
authoreliben <devnull@localhost>
Sun, 18 Sep 2011 14:31:10 +0000 (17:31 +0300)
committereliben <devnull@localhost>
Sun, 18 Sep 2011 14:31:10 +0000 (17:31 +0300)
elftools/common/utils.py
elftools/dwarf/__init__.py [new file with mode: 0644]
elftools/dwarf/structs.py [new file with mode: 0644]
zd.py [new file with mode: 0644]

index 477d7ee52905f7640f8bf734d503412ac81b29dd..8b9b9ae7d98ac41774677e002ace9f5555aef822 100644 (file)
@@ -7,6 +7,7 @@
 # This code is in the public domain
 #-------------------------------------------------------------------------------
 from .exceptions import ELFParseError, ELFError
+from ..construct import ConstructError
 
 
 def struct_parse(struct, stream, stream_pos=None):
diff --git a/elftools/dwarf/__init__.py b/elftools/dwarf/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py
new file mode 100644 (file)
index 0000000..e6aa81c
--- /dev/null
@@ -0,0 +1,134 @@
+#-------------------------------------------------------------------------------
+# elftools: dwarf/structs.py
+#
+# Encapsulation of Construct structs for parsing DWARF, adjusted for correct
+# endianness and word-size.
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from ..construct import (
+    UBInt8, UBInt16, UBInt32, UBInt64,
+    ULInt8, ULInt16, ULInt32, ULInt64,
+    Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename,
+    )
+
+
+class DWARFStructs(object):
+    """ Accessible attributes:
+    
+            Dwarf_uint{8,16,32,64):
+                Data chunks of the common sizes
+            
+            Dwarf_xword:
+                32-bit or 64-bit word, depending on dwarfclass
+            
+            Dwarf_initial_length:
+                "Initial length field" encoding, as described in DWARFv3 spec 
+                section 7.4
+            
+            Dwarf_{u,s}leb128:
+                ULEB128 and SLEB128 variable-length encoding, as described in
+                DWARFv3 spec section 7.6
+    """
+    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:
+            self.Dwarf_uint8 = ULInt8
+            self.Dwarf_uint16 = ULInt16
+            self.Dwarf_uint32 = ULInt32
+            self.Dwarf_uint64 = ULInt64
+            self.Dwarf_xword = ULInt32 if self.dwarfclass == 32 else ULInt64
+        else:
+            self.Dwarf_uint8 = UBInt8
+            self.Dwarf_uint16 = UBInt16
+            self.Dwarf_uint32 = UBInt32
+            self.Dwarf_uint64 = UBInt64
+            self.Dwarf_xword = UBInt32 if self.dwarfclass == 32 else UBInt64
+
+        self._create_initial_length()
+    
+    def _create_initial_length(self):
+        def _InitialLength(name):
+            # Adapts a Struct that parses forward a full initial length field.
+            # Only if the first word is the continuation value, the second 
+            # word is parsed from the stream.
+            #
+            return _InitialLengthAdapter(
+                Struct(name,
+                    self.Dwarf_uint32('first'),
+                    If(lambda ctx: ctx.first == 0xFFFFFFFF,
+                        self.Dwarf_uint64('second'),
+                        elsevalue=None)))
+        self.Dwarf_initial_length = _InitialLength
+
+    def _create_leb128(self):
+        self.Dwarf_uleb128 = _ULEB128
+        self.Dwarf_sleb128 = _SLEB128
+
+
+class _InitialLengthAdapter(Adapter):
+    """ A standard Construct adapter that expects a sub-construct
+        as a struct with one or two values (first, second).
+    """
+    def _decode(self, obj, context):
+        if obj.first < 0xFFFFFF00:
+            return obj.first
+        else:
+            if obj.first == 0xFFFFFFFF:
+                return obj.second
+            else:
+                raise ConstructError("Failed decoding initial length for %X" % (
+                    obj.first))
+
+
+def _LEB128_reader():
+    """ Read LEB128 variable-length data from the stream. The data is terminated
+        by a byte with 0 in its highest bit.
+    """
+    return RepeatUntil(
+                lambda obj, ctx: ord(obj) < 0x80,
+                Field(None, 1))
+
+
+class _ULEB128Adapter(Adapter):
+    """ An adapter for ULEB128, given a sequence of bytes in a sub-construct.
+    """
+    def _decode(self, obj, context):
+        value = 0
+        for b in reversed(obj):
+            value = (value << 7) + (ord(b) & 0x7F)
+        return value
+
+
+class _SLEB128Adapter(Adapter):
+    """ An adapter for SLEB128, given a sequence of bytes in a sub-construct.
+    """
+    def _decode(self, obj, context):
+        value = 0
+        for b in reversed(obj):
+            value = (value << 7) + (ord(b) & 0x7F)
+        if ord(obj[-1]) & 0x40:
+            # negative -> sign extend
+            #
+            value |= - (1 << (7 * len(obj)))
+        return value
+
+
+def _ULEB128(name):
+    """ A construct creator for ULEB128 encoding.
+    """
+    return Rename(name, _ULEB128Adapter(_LEB128_reader()))
+
+
+def _SLEB128(name):
+    """ A construct creator for SLEB128 encoding.
+    """
+    return Rename(name, _SLEB128Adapter(_LEB128_reader()))
+
diff --git a/zd.py b/zd.py
new file mode 100644 (file)
index 0000000..6294100
--- /dev/null
+++ b/zd.py
@@ -0,0 +1,12 @@
+from elftools.dwarf.structs import DWARFStructs\r
+\r
+\r
+ds = DWARFStructs(\r
+    little_endian=True,\r
+    dwarfclass=32)\r
+\r
+print ds.Dwarf_xword('x').parse('\x04\x01\x00\x00')\r
+print ds.Dwarf_initial_length('joe').parse('\xff\xff\xff\xff\x32\x00\x00\x00\x00\x00\x00\x00')\r
+\r
+\r
+print ds.Dwarf_sleb128('kwa').parse('\x81\x7f')\r