--- /dev/null
+# http://code.activestate.com/recipes/576693/ (r9)\r
+# Created by Raymond Hettinger on Wed, 18 Mar 2009 (MIT) \r
+#\r
+# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 \r
+# and pypy.\r
+# Passes Python2.7's test suite and incorporates all the latest updates.\r
+#\r
+\r
+try:\r
+ from thread import get_ident as _get_ident\r
+except ImportError:\r
+ from dummy_thread import get_ident as _get_ident\r
+\r
+try:\r
+ from _abcoll import KeysView, ValuesView, ItemsView\r
+except ImportError:\r
+ pass\r
+\r
+\r
+class OrderedDict(dict):\r
+ 'Dictionary that remembers insertion order'\r
+ # An inherited dict maps keys to values.\r
+ # The inherited dict provides __getitem__, __len__, __contains__, and get.\r
+ # The remaining methods are order-aware.\r
+ # Big-O running times for all methods are the same as for regular dictionaries.\r
+\r
+ # The internal self.__map dictionary maps keys to links in a doubly linked list.\r
+ # The circular doubly linked list starts and ends with a sentinel element.\r
+ # The sentinel element never gets deleted (this simplifies the algorithm).\r
+ # Each link is stored as a list of length three: [PREV, NEXT, KEY].\r
+\r
+ def __init__(self, *args, **kwds):\r
+ '''Initialize an ordered dictionary. Signature is the same as for\r
+ regular dictionaries, but keyword arguments are not recommended\r
+ because their insertion order is arbitrary.\r
+\r
+ '''\r
+ if len(args) > 1:\r
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))\r
+ try:\r
+ self.__root\r
+ except AttributeError:\r
+ self.__root = root = [] # sentinel node\r
+ root[:] = [root, root, None]\r
+ self.__map = {}\r
+ self.__update(*args, **kwds)\r
+\r
+ def __setitem__(self, key, value, dict_setitem=dict.__setitem__):\r
+ 'od.__setitem__(i, y) <==> od[i]=y'\r
+ # Setting a new item creates a new link which goes at the end of the linked\r
+ # list, and the inherited dictionary is updated with the new key/value pair.\r
+ if key not in self:\r
+ root = self.__root\r
+ last = root[0]\r
+ last[1] = root[0] = self.__map[key] = [last, root, key]\r
+ dict_setitem(self, key, value)\r
+\r
+ def __delitem__(self, key, dict_delitem=dict.__delitem__):\r
+ 'od.__delitem__(y) <==> del od[y]'\r
+ # Deleting an existing item uses self.__map to find the link which is\r
+ # then removed by updating the links in the predecessor and successor nodes.\r
+ dict_delitem(self, key)\r
+ link_prev, link_next, key = self.__map.pop(key)\r
+ link_prev[1] = link_next\r
+ link_next[0] = link_prev\r
+\r
+ def __iter__(self):\r
+ 'od.__iter__() <==> iter(od)'\r
+ root = self.__root\r
+ curr = root[1]\r
+ while curr is not root:\r
+ yield curr[2]\r
+ curr = curr[1]\r
+\r
+ def __reversed__(self):\r
+ 'od.__reversed__() <==> reversed(od)'\r
+ root = self.__root\r
+ curr = root[0]\r
+ while curr is not root:\r
+ yield curr[2]\r
+ curr = curr[0]\r
+\r
+ def clear(self):\r
+ 'od.clear() -> None. Remove all items from od.'\r
+ try:\r
+ for node in self.__map.itervalues():\r
+ del node[:]\r
+ root = self.__root\r
+ root[:] = [root, root, None]\r
+ self.__map.clear()\r
+ except AttributeError:\r
+ pass\r
+ dict.clear(self)\r
+\r
+ def popitem(self, last=True):\r
+ '''od.popitem() -> (k, v), return and remove a (key, value) pair.\r
+ Pairs are returned in LIFO order if last is true or FIFO order if false.\r
+\r
+ '''\r
+ if not self:\r
+ raise KeyError('dictionary is empty')\r
+ root = self.__root\r
+ if last:\r
+ link = root[0]\r
+ link_prev = link[0]\r
+ link_prev[1] = root\r
+ root[0] = link_prev\r
+ else:\r
+ link = root[1]\r
+ link_next = link[1]\r
+ root[1] = link_next\r
+ link_next[0] = root\r
+ key = link[2]\r
+ del self.__map[key]\r
+ value = dict.pop(self, key)\r
+ return key, value\r
+\r
+ # -- the following methods do not depend on the internal structure --\r
+\r
+ def keys(self):\r
+ 'od.keys() -> list of keys in od'\r
+ return list(self)\r
+\r
+ def values(self):\r
+ 'od.values() -> list of values in od'\r
+ return [self[key] for key in self]\r
+\r
+ def items(self):\r
+ 'od.items() -> list of (key, value) pairs in od'\r
+ return [(key, self[key]) for key in self]\r
+\r
+ def iterkeys(self):\r
+ 'od.iterkeys() -> an iterator over the keys in od'\r
+ return iter(self)\r
+\r
+ def itervalues(self):\r
+ 'od.itervalues -> an iterator over the values in od'\r
+ for k in self:\r
+ yield self[k]\r
+\r
+ def iteritems(self):\r
+ 'od.iteritems -> an iterator over the (key, value) items in od'\r
+ for k in self:\r
+ yield (k, self[k])\r
+\r
+ def update(*args, **kwds):\r
+ '''od.update(E, **F) -> None. Update od from dict/iterable E and F.\r
+\r
+ If E is a dict instance, does: for k in E: od[k] = E[k]\r
+ If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]\r
+ Or if E is an iterable of items, does: for k, v in E: od[k] = v\r
+ In either case, this is followed by: for k, v in F.items(): od[k] = v\r
+\r
+ '''\r
+ if len(args) > 2:\r
+ raise TypeError('update() takes at most 2 positional '\r
+ 'arguments (%d given)' % (len(args),))\r
+ elif not args:\r
+ raise TypeError('update() takes at least 1 argument (0 given)')\r
+ self = args[0]\r
+ # Make progressively weaker assumptions about "other"\r
+ other = ()\r
+ if len(args) == 2:\r
+ other = args[1]\r
+ if isinstance(other, dict):\r
+ for key in other:\r
+ self[key] = other[key]\r
+ elif hasattr(other, 'keys'):\r
+ for key in other.keys():\r
+ self[key] = other[key]\r
+ else:\r
+ for key, value in other:\r
+ self[key] = value\r
+ for key, value in kwds.items():\r
+ self[key] = value\r
+\r
+ __update = update # let subclasses override update without breaking __init__\r
+\r
+ __marker = object()\r
+\r
+ def pop(self, key, default=__marker):\r
+ '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.\r
+ If key is not found, d is returned if given, otherwise KeyError is raised.\r
+\r
+ '''\r
+ if key in self:\r
+ result = self[key]\r
+ del self[key]\r
+ return result\r
+ if default is self.__marker:\r
+ raise KeyError(key)\r
+ return default\r
+\r
+ def setdefault(self, key, default=None):\r
+ 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'\r
+ if key in self:\r
+ return self[key]\r
+ self[key] = default\r
+ return default\r
+\r
+ def __repr__(self, _repr_running={}):\r
+ 'od.__repr__() <==> repr(od)'\r
+ call_key = id(self), _get_ident()\r
+ if call_key in _repr_running:\r
+ return '...'\r
+ _repr_running[call_key] = 1\r
+ try:\r
+ if not self:\r
+ return '%s()' % (self.__class__.__name__,)\r
+ return '%s(%r)' % (self.__class__.__name__, self.items())\r
+ finally:\r
+ del _repr_running[call_key]\r
+\r
+ def __reduce__(self):\r
+ 'Return state information for pickling'\r
+ items = [[k, self[k]] for k in self]\r
+ inst_dict = vars(self).copy()\r
+ for k in vars(OrderedDict()):\r
+ inst_dict.pop(k, None)\r
+ if inst_dict:\r
+ return (self.__class__, (items,), inst_dict)\r
+ return self.__class__, (items,)\r
+\r
+ def copy(self):\r
+ 'od.copy() -> a shallow copy of od'\r
+ return self.__class__(self)\r
+\r
+ @classmethod\r
+ def fromkeys(cls, iterable, value=None):\r
+ '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S\r
+ and values equal to v (which defaults to None).\r
+\r
+ '''\r
+ d = cls()\r
+ for key in iterable:\r
+ d[key] = value\r
+ return d\r
+\r
+ def __eq__(self, other):\r
+ '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive\r
+ while comparison to a regular mapping is order-insensitive.\r
+\r
+ '''\r
+ if isinstance(other, OrderedDict):\r
+ return len(self)==len(other) and self.items() == other.items()\r
+ return dict.__eq__(self, other)\r
+\r
+ def __ne__(self, other):\r
+ return not self == other\r
+\r
+ # -- the following methods are only used in Python 2.7 --\r
+\r
+ def viewkeys(self):\r
+ "od.viewkeys() -> a set-like object providing a view on od's keys"\r
+ return KeysView(self)\r
+\r
+ def viewvalues(self):\r
+ "od.viewvalues() -> an object providing a view on od's values"\r
+ return ValuesView(self)\r
+\r
+ def viewitems(self):\r
+ "od.viewitems() -> a set-like object providing a view on od's items"\r
+ return ItemsView(self)\r
+\r
+\r
+\r
+\r
+#-------------------------------------------------------------------------------\r
+if __name__ == "__main__":\r
+ od = OrderedDict()\r
+ d = dict()\r
+ \r
+ for key in ['joe', 'more', 'tem', 'opsdf', 'dsf']:\r
+ od[key] = d[key] = key + '1'\r
+ \r
+ for k in d:\r
+ print k, d[k]\r
+ \r
+ print '-------- ordered ----------'\r
+ \r
+ for k in od:\r
+ print k, od[k]\r
class AbbrevDecl(object):
""" Wraps a parsed abbreviation declaration, exposing its fields with
dict-like access, and adding some convenience methods.
+
+ The abbreviation declaration represents an "entry" that points to it.
"""
def __init__(self, code, decl):
self.code = code
self.decl = decl
def has_children(self):
+ """ Does the entry have children?
+ """
return self['children_flag'] == 'DW_CHILDREN_yes'
-
+
+ def iter_attr_specs(self):
+ """ Iterate over the attribute specifications for the entry. Yield
+ (name, form) pairs.
+ """
+ for attr_spec in self['attr_spec']:
+ # Ignore the terminating 'null' spec
+ if attr_spec.name == 'DW_AT_null':
+ break
+ yield attr_spec.name, attr_spec.form
+
def __getitem__(self, entry):
return self.decl[entry]
-
-
-
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
+from .die import DIE
class CompileUnit(object):
- def __init__(self, header, dwarfinfo, structs):
+ def __init__(self, header, dwarfinfo, structs, cu_die_offset):
""" header:
CU header for this compile unit
structs:
A DWARFStructs instance suitable for this compile unit
+
+ cu_die_offset:
+ Offset in the stream of the top DIE of this CU
"""
self.dwarfinfo = dwarfinfo
self.header = header
self.structs = structs
-
+ self.cu_die_offset = cu_die_offset
+
+ # The abbreviation table for this CU. Filled lazily when DIEs are
+ # requested.
+ self._abbrev_table = None
+
+ def get_abbrev_table(self):
+ """ Get the abbreviation table (AbbrevTable object) for this CU
+ """
+ if self._abbrev_table is None:
+ self._abbrev_table = self.dwarfinfo.get_abbrev_table(
+ self['debug_abbrev_offset'])
+ return self._abbrev_table
+
+ def get_top_DIE(self):
+ """ Get the top DIE (which is either a DW_TAG_compile_unit or
+ DW_TAG_partial_unit) of this CU
+ """
+ return DIE(
+ cu=self,
+ stream=self.dwarfinfo.stream,
+ offset=self.cu_die_offset)
+
def __getitem__(self, name):
""" Implement dict-like access to header entries
"""
return self.header[name]
-
-
-
+
\ No newline at end of file
--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools: dwarf/die.py
+#
+# DWARF Debugging Information Entry
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from collections import namedtuple
+
+from ..common.ordereddict import OrderedDict
+from ..common.utils import struct_parse
+
+
+# Describes an attribute value in the DIE: form and actual value
+#
+AttributeValue = namedtuple('AttributeValue', 'form value')
+
+
+class DIE(object):
+ """ A DWARF debugging information entry. On creation, parses itself from
+ the stream. Each DIE is held by a CU.
+
+ Accessible attributes:
+
+ tag:
+ The DIE tag
+
+ length:
+ The size this DIE occupies in the section
+
+ attributes:
+ An ordered dictionary mapping attribute names to values
+ """
+ def __init__(self, cu, stream, offset):
+ """ cu:
+ CompileUnit object this DIE belongs to. Used to obtain context
+ information (structs, abbrev table, etc.)
+
+ stream, offset:
+ The stream and offset into it where this DIE's data is located
+ """
+ self.cu = cu
+ self.stream = stream
+ self.offset = offset
+ self._parse_DIE()
+
+ def _parse_DIE(self):
+ """ Parses the DIE info from the section, based on the abbreviation
+ table of the CU
+ """
+ saved_offset = self.offset
+ structs = self.cu.structs
+
+ # The DIE begins with the abbreviation code. Read it and use it to
+ # obtain the abbrev declaration for this DIE
+ #
+ abbrev_code = struct_parse(structs.Dwarf_uleb128(''), self.stream)
+ abbrev = self.cu.get_abbrev_table().get_abbrev(abbrev_code)
+
+ print abbrev_code, abbrev, abbrev.decl
+
+
# Cache for abbrev tables: a dict keyed by offset
self._abbrevtable_cache = {}
+ def num_CUs(self):
+ """ Number of compile units in the debug info
+ """
+ return len(self._CU)
+
+ def get_CU(self, n):
+ """ Get the compile unit (CompileUnit object) at index #n
+ """
+ return self._CU[n]
+
+ def iter_CUs(self):
+ """ Yield all the compile units (CompileUnit objects) in the debug info
+ """
+ for i in range(self.num_CUs()):
+ yield self.get_CU(i)
+
def get_abbrev_table(self, offset):
""" Get an AbbrevTable from the given offset in the debug_abbrev
section.
offset=offset + self.debug_abbrev_loc.offset)
return self._abbrevtable_cache[offset]
+ def info_offset2absolute(self, offset):
+ """ Given an offset into the debug_info section, translate it to an
+ absolute offset into the stream. Raise an exception if the offset
+ exceeds the section bounds.
+ """
+ dwarf_assert(
+ offset < self.debug_info_loc.size,
+ "Offset '0x%x' to debug_info out of section bounds" % offset)
+ return offset + self.debug_info_loc.offset
+
def _parse_CUs(self):
""" Parse CU entries from debug_info.
"""
cu_header = struct_parse(
cu_structs.Dwarf_CU_header, self.stream, offset)
+ cu_die_offset = self.stream.tell()
dwarf_assert(
self._is_supported_version(cu_header['version']),
"Expected supported DWARF version. Got '%s'" % cu_header['version'])
CUlist.append(CompileUnit(
header=cu_header,
dwarfinfo=self,
- structs=cu_structs))
+ structs=cu_structs,
+ cu_die_offset=cu_die_offset))
# 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.
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16,
- DW_FORM_sec_offset = 0x17,
- DW_FORM_exprloc = 0x18,
- DW_FORM_flag_present = 0x19,
- DW_FORM_ref_sig8 = 0x20,
_default_ = Pass,
)
UBInt8, UBInt16, UBInt32, UBInt64,
ULInt8, ULInt16, ULInt32, ULInt64,
Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename, Enum,
+ PrefixedArray, CString,
)
from .enums import *
Dwarf_abbrev_declaration:
Abbreviation table declaration - doesn't include the initial
code, only the contents.
+
+ Dwarf_dw_form:
+ A dictionary mapping 'DW_FORM_*' keys into construct Structs
+ that parse such forms. These Structs have already been given
+ dummy names.
See also the documentation of public methods.
"""
self.Dwarf_uint16 = UBInt16
self.Dwarf_uint32 = UBInt32
self.Dwarf_uint64 = UBInt64
- self.Dwarf_offest = UBInt32 if self.dwarf_format == 32 else UBInt64
+ self.Dwarf_offset = UBInt32 if self.dwarf_format == 32 else UBInt64
self._create_initial_length()
self._create_leb128()
self._create_cu_header()
self._create_abbrev_declaration()
+ self._create_dw_form()
def _create_initial_length(self):
def _InitialLength(name):
RepeatUntil(
lambda obj, ctx:
obj.name == 'DW_AT_null' and obj.form == 'DW_FORM_null',
- Struct('spec',
+ Struct('attr_spec',
Enum(self.Dwarf_uleb128('name'), **ENUM_DW_AT),
Enum(self.Dwarf_uleb128('form'), **ENUM_DW_FORM))))
+ def _create_dw_form(self):
+ self.Dwarf_dw_form = dict(
+ DW_FORM_addr=self.Dwarf_offset(''),
+
+ DW_FORM_block1=self._make_block_struct(self.Dwarf_uint8),
+ DW_FORM_block2=self._make_block_struct(self.Dwarf_uint16),
+ DW_FORM_block4=self._make_block_struct(self.Dwarf_uint32),
+ DW_FORM_block=self._make_block_struct(self.Dwarf_uleb128),
+
+ # All DW_FORM_data<n> forms are assumed to be unsigned
+ DW_FORM_data1=self.Dwarf_uint8(''),
+ DW_FORM_data2=self.Dwarf_uint16(''),
+ DW_FORM_data4=self.Dwarf_uint32(''),
+ DW_FORM_data8=self.Dwarf_uint64(''),
+ DW_FORM_sdata=self.Dwarf_sleb128(''),
+ DW_FORM_udata=self.Dwarf_uleb128(''),
+
+ DW_FORM_string=CString(''),
+ DW_FORM_strp=self.Dwarf_offset(''),
+ DW_FORM_flag=self.Dwarf_uint8(''),
+
+ DW_FORM_ref1=self.Dwarf_uint8(''),
+ DW_FORM_ref2=self.Dwarf_uint16(''),
+ DW_FORM_ref4=self.Dwarf_uint32(''),
+ DW_FORM_ref8=self.Dwarf_uint64(''),
+ DW_FORM_ref_udata=self.Dwarf_uleb128(''),
+ DW_FORM_ref_addr=self.Dwarf_offset(''),
+
+ DW_FORM_indirect=self.Dwarf_uleb128(''),
+ )
+
+ def _make_block_struct(self, length_field):
+ """ Create a struct for DW_FORM_block<size>
+ """
+ return PrefixedArray(
+ subcon=self.Dwarf_uint8('elem'),
+ length_field=length_field(''))
+
class _InitialLengthAdapter(Adapter):
""" A standard Construct adapter that expects a sub-construct
"""
return Rename(name, _SLEB128Adapter(_LEB128_reader()))
+
dwarfinfo = efile.get_dwarf_info()
+cu = dwarfinfo.get_CU(1)
+print cu.get_top_DIE()
+
#~ print dwarfinfo.structs.Dwarf_abbrev_entry.parse('\x13\x01\x01\x03\x50\x04\x00\x00')
-print id(dwarfinfo.get_abbrev_table(0))
-print id(dwarfinfo.get_abbrev_table(0))
-pprint.pprint(dwarfinfo.get_abbrev_table(0)._abbrev_map)
+#~ abbrevtable = dwarfinfo.get_abbrev_table(95)
+#~ print id(abbrevtable)
+#~ pprint.pprint(abbrevtable._abbrev_map)
+
+#~ ab1 = abbrevtable.get_abbrev(2)
+#~ print ab1.has_children()
+#~ for name, form in ab1.iter_attr_specs():
+ #~ print name, form
-print dwarfinfo.get_abbrev_table(0).get_abbrev(1).decl
-print dwarfinfo.get_abbrev_table(0).get_abbrev(1).has_children()
+#~ print dwarfinfo.get_abbrev_table(0).get_abbrev(1).has_children()
#~ for cu in dwarfinfo._CU:
#~ print cu, cu.header
from elftools.dwarf.dwarfinfo import DWARFInfo\r
\r
\r
-ds = DWARFStructs(little_endian=True,\r
- dwarfclass=32)\r
+ds = DWARFStructs(little_endian=True, dwarf_format=32)\r
\r
-print ds.Dwarf_xword('x').parse('\x04\x01\x00\x00')\r
+print ds.Dwarf_offset('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
+\r
+s = ds.Dwarf_dw_form['DW_FORM_block']\r
+#~ s = ds.Dwarf_dw_form['DW_FORM_addr']\r
+\r
+print s.parse('\x04\x12\x13\x13\x16')\r
+#~ print s.parse('\x04\x00\x12\x13')\r