blob: ab272693fbca5063d9d355b6cb0c866558992dce [file] [log] [blame]
#-------------------------------------------------------------------------------
# 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,
)
from .enums import *
class DWARFStructs(object):
""" Exposes Construct structs suitable for parsing information from DWARF
sections. Configurable with endianity and format (32 or 64-bit)
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_offset:
32-bit or 64-bit word, depending on dwarf_format
Dwarf_initial_length:
"Initial length field" encoding
section 7.4
Dwarf_{u,s}leb128:
ULEB128 and SLEB128 variable-length encoding
Dwarf_CU_header:
Compilation unit header
Dwarf_abbrev_entry:
Abbreviation table entry - doesn't include the initial code,
only the contents.
See also the documentation of public methods.
"""
def __init__(self, little_endian=True, dwarf_format=32):
assert dwarf_format == 32 or dwarf_format == 64
self.little_endian = little_endian
self.dwarf_format = dwarf_format
self._create_structs()
def initial_lenght_field_size(self):
""" Size of an initial length field.
"""
return 4 if self.dwarf_format == 32 else 12
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_offset = ULInt32 if self.dwarf_format == 32 else ULInt64
else:
self.Dwarf_uint8 = UBInt8
self.Dwarf_uint16 = UBInt16
self.Dwarf_uint32 = UBInt32
self.Dwarf_uint64 = UBInt64
self.Dwarf_offest = UBInt32 if self.dwarf_format == 32 else UBInt64
self._create_initial_length()
self._create_leb128()
self._create_cu_header()
self._create_abbrev_entry()
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
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'))
def _create_abbrev_entry(self):
self.Dwarf_abbrev_entry = Struct('Dwarf_abbrev_entry',
self.Dwarf_uleb128('tag'), # ZZZ: wrap in enums
self.Dwarf_uint8('children_flag'),
RepeatUntil(lambda obj, ctx: obj.name == obj.form == 0,
Struct('spec',
self.Dwarf_uleb128('name'),
self.Dwarf_uleb128('form'))))
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()))