From 84e27b4e7fad92e89ed65be96c43d6abc0fd207e Mon Sep 17 00:00:00 2001 From: eliben Date: Sun, 6 Nov 2011 15:27:57 +0200 Subject: [PATCH] Added string dumping for location expressions, with tests --- .hgignore | 3 ++ elftools/dwarf/location_expr.py | 85 ++++++++++++++++++++++++------- tests/test_dwarf_location_expr.py | 56 ++++++++++++++------ 3 files changed, 109 insertions(+), 35 deletions(-) diff --git a/.hgignore b/.hgignore index bb6d589..f1dca2e 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,6 @@ syntax: glob *.pyc +.coverage +htmlcov + diff --git a/elftools/dwarf/location_expr.py b/elftools/dwarf/location_expr.py index da1a819..0040559 100644 --- a/elftools/dwarf/location_expr.py +++ b/elftools/dwarf/location_expr.py @@ -11,8 +11,7 @@ from cStringIO import StringIO from ..common.utils import struct_parse, bytelist2string -# Location expression opcodes. -# +# Location expression opcodes. name -> opcode mapping DW_OP_name2opcode = dict( DW_OP_addr=0x03, DW_OP_deref=0x06, @@ -86,20 +85,20 @@ _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) +# opcode -> name mapping 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.stream = None self._cur_opcode = None self._cur_opcode_name = None self._cur_args = [] - def process_expr(self, expr): + def process_expr(self, loc_expr): """ Process (visit) a location expression. Currently two possible types are supported for expr: @@ -107,11 +106,11 @@ class GenericLocationExprVisitor(object): 2. List of byte values (the result of parsed DW_FORM_block* attributes). """ - if hasattr(expr, 'read') and hasattr(expr, 'seek'): + if hasattr(loc_expr, 'read') and hasattr(loc_expr, 'seek'): # looks like a stream - self.stream = expr + self.stream = loc_expr else: - self.stream = StringIO(bytelist2string(expr)) + self.stream = StringIO(bytelist2string(loc_expr)) while True: # Get the next opcode from the stream. If nothing is left in the @@ -122,7 +121,8 @@ class GenericLocationExprVisitor(object): # Decode the opcode and its name self._cur_opcode = ord(byte) - self._cur_opcode_name = DW_OP_opcode2name[self._cur_opcode] + self._cur_opcode_name = DW_OP_opcode2name.get( + self._cur_opcode, 'OP:0x%x' % self._cur_opcode) # Will be filled in by visitors self._cur_args = [] @@ -136,11 +136,11 @@ class GenericLocationExprVisitor(object): self._after_visit( self._cur_opcode, self._cur_opcode_name, self._cur_args) - def _after_visit(self, opcode, opcode_name, *args): - raise NotImplementedError() + def _after_visit(self, opcode, opcode_name, args): + pass def _default_visitor(self, opcode, opcode_name): - raise NotImplementedError() + pass def _visit_OP_with_no_args(self, opcode, opcode_name): self._cur_args = [] @@ -153,7 +153,7 @@ class GenericLocationExprVisitor(object): """ Create a visitor method for an opcode that that accepts a single argument, specified by a struct. """ - def visitor(self, opcode, opcode_name): + def visitor(opcode, opcode_name): self._cur_args = [struct_parse(struct_arg, self.stream)] return visitor @@ -161,7 +161,7 @@ class GenericLocationExprVisitor(object): """ Create a visitor method for an opcode that that accepts two arguments, specified by structs. """ - def visitor(self, opcode, opcode_name): + def visitor(opcode, opcode_name): self._cur_args = [ struct_parse(struct_arg1, self.stream), struct_parse(struct_arg2, self.stream)] @@ -186,9 +186,13 @@ class GenericLocationExprVisitor(object): add('DW_OP_const4s', self._make_visitor_arg_struct(self.structs.Dwarf_int32(''))) add('DW_OP_const8u', - self._make_visitor_arg_struct(self.structs.Dwarf_uint64(''))) + self._make_visitor_arg_struct2( + self.structs.Dwarf_uint32(''), + self.structs.Dwarf_uint32(''))) add('DW_OP_const8s', - self._make_visitor_arg_struct(self.structs.Dwarf_int64(''))) + self._make_visitor_arg_struct2( + self.structs.Dwarf_int32(''), + self.structs.Dwarf_int32(''))) add('DW_OP_constu', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) add('DW_OP_consts', @@ -210,7 +214,7 @@ class GenericLocationExprVisitor(object): 'DW_OP_xor', 'DW_OP_eq', 'DW_OP_ge', 'DW_OP_gt', 'DW_OP_le', 'DW_OP_lt', 'DW_OP_ne', 'DW_OP_nop', 'DW_OP_push_object_address', 'DW_OP_form_tls_address', - 'DW_OP_call_frame_cfa', 'DW_OP_stack_value',]: + 'DW_OP_call_frame_cfa']: add(opname, self._visit_OP_with_no_args) for n in range(0, 32): @@ -222,13 +226,13 @@ class GenericLocationExprVisitor(object): add('DW_OP_regx', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) add('DW_OP_bregx', - self._make_visitor_arg_struct( + self._make_visitor_arg_struct2( self.structs.Dwarf_uleb128(''), self.structs.Dwarf_sleb128(''))) add('DW_OP_piece', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) add('DW_OP_bit_piece', - self._make_visitor_arg_struct( + self._make_visitor_arg_struct2( self.structs.Dwarf_uleb128(''), self.structs.Dwarf_uleb128(''))) add('DW_OP_deref_size', @@ -243,3 +247,46 @@ class GenericLocationExprVisitor(object): self._make_visitor_arg_struct(self.structs.Dwarf_offset(''))) +class LocationExpressionDumper(GenericLocationExprVisitor): + def __init__(self, structs): + super(LocationExpressionDumper, self).__init__(structs) + self._init_lookups() + self._str_parts = [] + + def get_str(self): + return '; '.join(self._str_parts) + + def _init_lookups(self): + self._ops_with_decimal_arg = set([ + 'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s', + 'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts', + 'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip', + 'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP_deref_size', + 'DW_OP_xderef_size', 'DW_OP_regx', 'DW_OP_fbreg', ]) + + for n in range(0, 32): + self._ops_with_decimal_arg.add('DW_OP_breg%s' % n) + + self._ops_with_two_decimal_args = set([ + 'DW_OP_const8u', 'DW_OP_const8s', 'DW_OP_bregx', 'DW_OP_bit_piece']) + + self._ops_with_hex_arg = set( + ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref']) + + def _after_visit(self, opcode, opcode_name, args): + self._str_parts.append(self._dump_to_string(opcode, opcode_name, args)) + + def _dump_to_string(self, opcode, opcode_name, args): + if len(args) == 0: + return opcode_name + elif opcode_name in self._ops_with_decimal_arg: + return '%s: %s' % (opcode_name, args[0]) + elif opcode_name in self._ops_with_hex_arg: + return '%s: %x' % (opcode_name, args[0]) + elif opcode_name in self._ops_with_two_decimal_args: + return '%s: %s %s' % (opcode_name, args[0], args[1]) + else: + return '' % opcode_name + + + diff --git a/tests/test_dwarf_location_expr.py b/tests/test_dwarf_location_expr.py index 7949a07..b6c2198 100644 --- a/tests/test_dwarf_location_expr.py +++ b/tests/test_dwarf_location_expr.py @@ -1,30 +1,54 @@ import sys, unittest +from cStringIO import StringIO sys.path.extend(('..', '.')) -from elftools.dwarf.location_expr import ( - GenericLocationExprVisitor, DW_OP_opcode2name) +from elftools.dwarf.location_expr import LocationExpressionDumper 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): +class TestLocationExpressionDumper(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 + def setUp(self): + self.visitor = LocationExpressionDumper(self.structs32) + + def test_basic_single(self): + self.visitor.process_expr([0x1b]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_div') + + self.setUp() + self.visitor.process_expr([0x74, 0x82, 0x01]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_breg4: 130') + + self.setUp() + self.visitor.process_expr([0x9d, 0x8f, 0x0A, 0x90, 0x01]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_bit_piece: 1295 144') + + def test_basic_sequence(self): + self.visitor.process_expr([0x03, 0x01, 0x02, 0, 0, 0x06, 0x06]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_addr: 201; DW_OP_deref; DW_OP_deref') + + self.setUp() + self.visitor.process_expr([0x15, 0xFF, 0x0b, 0xf1, 0xff]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_pick: 255; DW_OP_const2s: -15') + + self.setUp() + self.visitor.process_expr([0x1d, 0x1e, 0x1d, 0x1e, 0x1d, 0x1e]) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_mod; DW_OP_mul; DW_OP_mod; DW_OP_mul; DW_OP_mod; DW_OP_mul') + + def test_stream_input(self): + self.visitor.process_expr(StringIO('\x1b')) + self.assertEqual(self.visitor.get_str(), + 'DW_OP_div') if __name__ == '__main__': -- 2.30.2