From a7c2547b9c7414598a3dab42f6fabe0570531f90 Mon Sep 17 00:00:00 2001 From: eliben Date: Sun, 18 Sep 2011 17:31:10 +0300 Subject: [PATCH] created elftools/dwarf and started writing down some basic structs for data primitives and various encodings the format uses --- elftools/common/utils.py | 1 + elftools/dwarf/__init__.py | 0 elftools/dwarf/structs.py | 134 +++++++++++++++++++++++++++++++++++++ zd.py | 12 ++++ 4 files changed, 147 insertions(+) create mode 100644 elftools/dwarf/__init__.py create mode 100644 elftools/dwarf/structs.py create mode 100644 zd.py diff --git a/elftools/common/utils.py b/elftools/common/utils.py index 477d7ee..8b9b9ae 100644 --- a/elftools/common/utils.py +++ b/elftools/common/utils.py @@ -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 index 0000000..e69de29 diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py new file mode 100644 index 0000000..e6aa81c --- /dev/null +++ b/elftools/dwarf/structs.py @@ -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 index 0000000..6294100 --- /dev/null +++ b/zd.py @@ -0,0 +1,12 @@ +from elftools.dwarf.structs import DWARFStructs + + +ds = DWARFStructs( + little_endian=True, + dwarfclass=32) + +print ds.Dwarf_xword('x').parse('\x04\x01\x00\x00') +print ds.Dwarf_initial_length('joe').parse('\xff\xff\xff\xff\x32\x00\x00\x00\x00\x00\x00\x00') + + +print ds.Dwarf_sleb128('kwa').parse('\x81\x7f') -- 2.30.2