started location_expr, added unit tests. moved readelf unittest to a more appropriate file name
diff --git a/elftools/common/utils.py b/elftools/common/utils.py
index 5358072..2b55e5e 100644
--- a/elftools/common/utils.py
+++ b/elftools/common/utils.py
@@ -10,6 +10,13 @@
from ..construct import ConstructError
+def bytelist2string(bytelist):
+ """ Convert a list of byte values (e.g. [0x10 0x20 0x00]) to a string
+ (e.g. '\x10\x20\x00').
+ """
+ return ''.join(chr(b) for b in bytelist)
+
+
def struct_parse(struct, stream, stream_pos=None):
""" Convenience function for using the given struct to parse a stream.
If stream_pos is provided, the stream is seeked to this position before
diff --git a/elftools/dwarf/descriptions.py b/elftools/dwarf/descriptions.py
index 50c7273..9cef354 100644
--- a/elftools/dwarf/descriptions.py
+++ b/elftools/dwarf/descriptions.py
@@ -211,9 +211,14 @@
return extra
-def location_list_extra(attr, die, section_offset):
- pass
-_location_list_extra = _make_extra_string('(location list)')
+def _location_list_extra(attr, die, section_offset):
+ # According to section 2.6 of the DWARF spec v3, class loclistptr means
+ # a location list, and class block means a location expression.
+ #
+ if attr.form in ('DW_FORM_data4', 'DW_FORM_data8'):
+ return '(location list)'
+ else:
+ return '<<ZZZ>> %s %s' % (attr.value, type(attr.value))
_EXTRA_INFO_DESCRIPTION_MAP = defaultdict(
@@ -246,6 +251,10 @@
DW_AT_segment=_location_list_extra,
DW_AT_static_link=_location_list_extra,
DW_AT_use_location=_location_list_extra,
+ DW_AT_allocated=_location_list_extra,
+ DW_AT_associated=_location_list_extra,
+ DW_AT_data_location=_location_list_extra,
+ DW_AT_stride=_location_list_extra,
)
diff --git a/elftools/dwarf/location_expr.py b/elftools/dwarf/location_expr.py
new file mode 100644
index 0000000..1a2d4f3
--- /dev/null
+++ b/elftools/dwarf/location_expr.py
@@ -0,0 +1,169 @@
+#-------------------------------------------------------------------------------
+# elftools: dwarf/location_expr.py
+#
+# Decoding DWARF location expressions
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from cStringIO import StringIO
+
+from ..common.utils import struct_parse, bytelist2string
+
+
+# Location expression opcodes.
+#
+DW_OP_name2opcode = dict(
+ DW_OP_addr=0x03,
+ DW_OP_deref=0x06,
+ DW_OP_const1u=0x08,
+ DW_OP_const1s=0x09,
+ DW_OP_const2u=0x0a,
+ DW_OP_const2s=0x0b,
+ DW_OP_const4u=0x0c,
+ DW_OP_const4s=0x0d,
+ DW_OP_const8u=0x0e,
+ DW_OP_const8s=0x0f,
+ DW_OP_constu=0x10,
+ DW_OP_consts=0x11,
+ DW_OP_dup=0x12,
+ DW_OP_drop=0x13,
+ DW_OP_over=0x14,
+ DW_OP_pick=0x15,
+ DW_OP_swap=0x16,
+ DW_OP_rot=0x17,
+ DW_OP_xderef=0x18,
+ DW_OP_abs=0x19,
+ DW_OP_and=0x1a,
+ DW_OP_div=0x1b,
+ DW_OP_minus=0x1c,
+ DW_OP_mod=0x1d,
+ DW_OP_mul=0x1e,
+ DW_OP_neg=0x1f,
+ DW_OP_not=0x20,
+ DW_OP_or=0x21,
+ DW_OP_plus=0x22,
+ DW_OP_plus_uconst=0x23,
+ DW_OP_shl=0x24,
+ DW_OP_shr=0x25,
+ DW_OP_shra=0x26,
+ DW_OP_xor=0x27,
+ DW_OP_bra=0x28,
+ DW_OP_eq=0x29,
+ DW_OP_ge=0x2a,
+ DW_OP_gt=0x2b,
+ DW_OP_le=0x2c,
+ DW_OP_lt=0x2d,
+ DW_OP_ne=0x2e,
+ DW_OP_skip=0x2f,
+ DW_OP_regx=0x90,
+ DW_OP_fbreg=0x91,
+ DW_OP_bregx=0x92,
+ DW_OP_piece=0x93,
+ DW_OP_deref_size=0x94,
+ DW_OP_xderef_size=0x95,
+ DW_OP_nop=0x96,
+ DW_OP_push_object_address=0x97,
+ DW_OP_call2=0x98,
+ DW_OP_call4=0x99,
+ DW_OP_call_ref=0x9a,
+ DW_OP_form_tls_address=0x9b,
+ DW_OP_call_frame_cfa=0x9c,
+ DW_OP_bit_piece=0x9d,
+ DW_OP_implicit_value=0x9e,
+ DW_OP_stack_value=0x9f,
+ DW_OP_GNU_push_tls_address=0xe0,
+ DW_OP_GNU_uninit=0xf0,
+ DW_OP_GNU_encoded_addr=0xf1,
+ DW_OP_GNU_implicit_pointer=0xf2,
+ DW_OP_GNU_entry_value=0xf3,
+ DW_OP_GNU_const_type=0xf4,
+ DW_OP_GNU_regval_type=0xf5,
+ DW_OP_GNU_deref_type=0xf6,
+ DW_OP_GNU_convert=0xf7,
+ DW_OP_GNU_reinterpret=0xf9,
+)
+
+def _generate_dynamic_values(map, prefix, index_start, index_end, value_start):
+ """ Generate values in a map (dict) dynamically. Each key starts with
+ a (string) prefix, followed by an index in the inclusive range
+ [index_start, index_end]. The values start at value_start.
+ """
+ for index in range(index_start, index_end + 1):
+ name = '%s%s' % (prefix, index)
+ value = value_start + index - index_start
+ map[name] = value
+
+_generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_lit', 0, 31, 0x30)
+_generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_reg', 0, 31, 0x50)
+_generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_breg', 0, 31, 0x70)
+
+DW_OP_opcode2name = dict((v, k) for k, v in DW_OP_name2opcode.iteritems())
+
+
+class GenericLocationExprVisitor(object):
+ def __init__(self, structs):
+ self.structs = structs
+ self.stream = None
+ self._init_dispatch_table()
+
+ self._cur_opcode = None
+ self._cur_opcode_name = None
+ self._cur_args = []
+
+ def process_expr(self, expr):
+ """ Process (visit) a location expression. Currently two possible
+ types are supported for expr:
+
+ 1. file-like stream object
+ 2. List of byte values (the result of parsed DW_FORM_block*
+ attributes).
+ """
+ if hasattr(expr, 'read') and hasattr(expr, 'seek'):
+ self.stream = expr
+ else:
+ self.stream = StringIO(bytelist2string(expr))
+
+ while True:
+ # Get the next opcode from the stream. If nothing is left in the
+ # stream, we're done.
+ byte = self.stream.read(1)
+ if len(byte) == 0:
+ break
+
+ # Decode the opcode and its name
+ self._cur_opcode = ord(byte)
+ self._cur_opcode_name = DW_OP_opcode2name[self._cur_opcode]
+
+ # Dispatch to a visitor function
+ visitor = self._dispatch_table.get(
+ self._cur_opcode,
+ self._default_visitor)
+ visitor(self._cur_opcode, self._cur_opcode_name)
+
+ # Finally call the post-visit function
+ self._after_visit(
+ self._cur_opcode, self._cur_opcode_name, self._cur_args)
+
+ def _after_visit(self, opcode, opcode_name, *args):
+ raise NotImplementedError()
+
+ def _default_visitor(self, opcode, opcode_name):
+ raise NotImplementedError()
+
+ def _visit_OP_with_no_args(self, opcode, opcode_name):
+ self._cur_args = []
+
+ def _visit_OP_addr(self, opcode, opcode_name):
+ self._cur_args = [
+ struct_parse(self.structs.Dwarf_target_addr(''), self.stream)]
+
+ def _init_dispatch_table(self):
+ self._dispatch_table = {}
+ def add(opcode_name, func):
+ self._dispatch_table[DW_OP_name2opcode[opcode_name]] = func
+
+ add('DW_OP_addr', self._visit_OP_addr)
+ add('DW_OP_deref', self._visit_OP_with_no_args)
+
+
diff --git a/tests/run_tests.py b/tests/run_readelf_tests.py
similarity index 99%
rename from tests/run_tests.py
rename to tests/run_readelf_tests.py
index a89d163..27a2bf4 100755
--- a/tests/run_tests.py
+++ b/tests/run_readelf_tests.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#-------------------------------------------------------------------------------
-# tests/run_tests.py
+# tests/run_readelf_tests.py
#
# Automatic test runner for elftools & readelf
#
diff --git a/tests/test_dwarf_location_expr.py b/tests/test_dwarf_location_expr.py
new file mode 100644
index 0000000..7653a0b
--- /dev/null
+++ b/tests/test_dwarf_location_expr.py
@@ -0,0 +1,32 @@
+import sys, unittest
+
+sys.path.extend(('..', '.'))
+from elftools.dwarf.location_expr import (
+ GenericLocationExprVisitor, DW_OP_opcode2name)
+from elftools.dwarf.structs import DWARFStructs
+
+
+class MyTestVisitor(GenericLocationExprVisitor):
+ def __init__(self, structs):
+ super(MyTestVisitor, self).__init__(structs)
+ self.results = []
+
+ def _after_visit(self, opcode, opcode_name, *args):
+ self.results.append((opcode_name, args))
+
+
+class TestGenericLocationExprVisitor(unittest.TestCase):
+ structs32 = DWARFStructs(
+ little_endian=True,
+ dwarf_format=32,
+ address_size=4)
+
+ def test_basic(self):
+ visitor = MyTestVisitor(self.structs32)
+ visitor.process_expr([0x03, 0x01, 0x02, 0, 0, 0x06, 0x06])
+ print visitor.results
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/z.py b/z.py
index 7780365..34f4bc5 100644
--- a/z.py
+++ b/z.py
@@ -34,66 +34,9 @@
for s in c.iter_siblings():
print s
-#~ print c.get_parent()
-#~ print topdie
+from elftools.dwarf.location_expr import _DW_OP_name2opcode, _DW_OP_opcode2name
-#~ def recp(d, indent=0):
- #~ s = str(d)
- #~ lines = s.split('\n')
- #~ print '\n'.join(' ' * indent + l for l in lines)
-
- #~ for c in d.iter_children():
- #~ recp(c, indent + 6)
+print hex(_DW_OP_name2opcode['DW_OP_lit14'])
+print _DW_OP_opcode2name[0x0e]
-#~ recp(topdie)
-
-#~ for c in topdie.iter_children():
- #~ print c
-#~ for die in cu._dielist:
- #~ print 'DIE %s, size=%s' % (die.tag, die.size)
- #~ for attrname, val in die.attributes.iteritems():
- #~ print ' ', attrname, val
-
-#~ topdie = cu.get_top_DIE()
-
-#~ print topdie.size, topdie.tag
-
-#~ print len(cu._dielist)
-
-#~ print dwarfinfo.structs.Dwarf_abbrev_entry.parse('\x13\x01\x01\x03\x50\x04\x00\x00')
-
-#~ 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).has_children()
-
-#~ for cu in dwarfinfo._CU:
- #~ print cu, cu.header
-
-
-
-
-#~ print efile.get_section_by_name('.debug_info').name
-
-#~ print '===> %s segments!' % efile.num_segments()
-
-#~ for sec in efile.iter_sections():
- #~ print type(sec), sec.name
- #~ if isinstance(sec, SymbolTableSection):
- #~ print ' linked string table:', sec.stringtable.name
-
-#~ for seg in efile.iter_segments():
- #~ print type(seg), seg['p_type'], seg['p_offset']
-
-#~ for sec in efile.iter_sections():
- #~ if isinstance(sec, SymbolTableSection):
- #~ print 'symbol table "%s ~~~"' % sec.name
- #~ for sym in sec.iter_symbols():
- #~ print '%-26s %s %s' % (sym.name, sym['st_info']['type'], sym['st_info']['bind'])