From 4682c96bff4d31c189f1b951aa27005128cdcb88 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Fri, 16 Dec 2011 06:39:49 +0200 Subject: [PATCH] readelf --debug-dump=frames-interp initial implementation completed. some tests vs. readelf run --- elftools/dwarf/callframe.py | 3 +-- elftools/dwarf/descriptions.py | 26 +++++++++++++++++++++ scripts/readelf.py | 42 ++++++++++++++++++++++++++++------ tests/run_readelf_tests.py | 2 +- tests/test_callframe.py | 3 --- 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py index 9821572..c2a7ec2 100644 --- a/elftools/dwarf/callframe.py +++ b/elftools/dwarf/callframe.py @@ -326,8 +326,7 @@ class CFIEntry(object): elif name == 'DW_CFA_restore_state': cur_line = line_stack.pop() - if len(self.instructions) > 0: - table.append(cur_line) + table.append(cur_line) return DecodedCallFrameTable(table=table, reg_order=reg_order) diff --git a/elftools/dwarf/descriptions.py b/elftools/dwarf/descriptions.py index 1cf2633..4647a41 100644 --- a/elftools/dwarf/descriptions.py +++ b/elftools/dwarf/descriptions.py @@ -111,6 +111,22 @@ def describe_CFI_instructions(entry): return s +def describe_CFI_register_rule(rule): + s = _DESCR_CFI_REGISTER_RULE_TYPE[rule.type] + if rule.type in ('OFFSET', 'VAL_OFFSET'): + s += '%+d' % rule.arg + elif rule.type == 'REGISTER': + s += describe_reg_name(rule.arg) + return s + + +def describe_CFI_CFA_rule(rule): + if rule.expr: + return 'exp' + else: + return '%s%+d' % (describe_reg_name(rule.reg), rule.offset) + + def describe_reg_name(regnum, machine_arch=None): """ Provide a textual description for a register name, given its serial number. The number is expected to be valid. @@ -290,6 +306,16 @@ _DESCR_DW_ORD = { DW_ORD_col_major: '(column major)', } +_DESCR_CFI_REGISTER_RULE_TYPE = dict( + UNDEFINED='u', + SAME_VALUE='s', + OFFSET='c', + VAL_OFFSET='v', + REGISTER='', + EXPRESSION='exp', + VAL_EXPRESSION='vexp', + ARCHITECTURAL='a', +) def _make_extra_mapper(mapping, default, default_interpolate_value=False): """ Create a mapping function from attribute parameters to an extra diff --git a/scripts/readelf.py b/scripts/readelf.py index 10b17ac..a7296f5 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -9,6 +9,7 @@ #------------------------------------------------------------------------------- import os, sys from optparse import OptionParser +from itertools import ifilter import string @@ -36,8 +37,10 @@ from elftools.elf.descriptions import ( ) from elftools.dwarf.dwarfinfo import DWARFInfo from elftools.dwarf.descriptions import ( - describe_reg_name, - describe_attr_value, set_global_machine_arch, describe_CFI_instructions) + describe_reg_name, describe_attr_value, set_global_machine_arch, + describe_CFI_instructions, describe_CFI_register_rule, + describe_CFI_CFA_rule, + ) from elftools.dwarf.constants import ( DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file) from elftools.dwarf.callframe import CIE, FDE @@ -651,6 +654,7 @@ class ReadElf(object): entry['code_alignment_factor'], entry['data_alignment_factor'], entry['return_address_register'])) + ra_regnum = entry['return_address_register'] else: # FDE self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % ( entry.offset, @@ -659,19 +663,43 @@ class ReadElf(object): entry.cie.offset, entry['initial_location'], entry['initial_location'] + entry['address_range'])) + ra_regnum = entry.cie['return_address_register'] # Print the heading row for the decoded table self._emit(' LOC') self._emit(' ' if entry.structs.address_size == 4 else ' ') - self._emit('CFA ') + self._emit(' CFA ') + # Decode the table nad look at the registers it describes. + # We build reg_order here to match readelf's order. In particular, + # registers are sorted by their number, and the register matching + # ra_regnum is always listed last with a special heading. decoded_table = entry.get_decoded() - for regnum in decoded_table.reg_order: + reg_order = sorted(ifilter( + lambda r: r != ra_regnum, + decoded_table.reg_order)) + + # Headings for the registers + for regnum in reg_order: self._emit('%-6s' % describe_reg_name(regnum)) self._emitline('ra ') - - - + + # Now include ra_regnum in reg_order to print its values similarly + # to the other registers. + reg_order.append(ra_regnum) + for line in decoded_table.table: + self._emit(self._format_hex( + line['pc'], fullhex=True, lead0x=False)) + self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa'])) + + for regnum in reg_order: + if regnum in line: + s = describe_CFI_register_rule(line[regnum]) + else: + s = 'u' + self._emit('%-6s' % s) + self._emitline() + self._emitline() def _emit(self, s=''): """ Emit an object to output diff --git a/tests/run_readelf_tests.py b/tests/run_readelf_tests.py index 7df91d7..ca3b656 100755 --- a/tests/run_readelf_tests.py +++ b/tests/run_readelf_tests.py @@ -53,7 +53,7 @@ def run_test_on_file(filename): for option in [ '-e', '-s', '-r', '-x.text', '-p.shstrtab', '--debug-dump=info', '--debug-dump=decodedline', - '--debug-dump=frames']: + '--debug-dump=frames', '--debug-dump=frames-interp']: testlog.info("..option='%s'" % option) # stdouts will be a 2-element list: output of readelf and output # of scripts/readelf.py diff --git a/tests/test_callframe.py b/tests/test_callframe.py index fa0ac80..30e405b 100644 --- a/tests/test_callframe.py +++ b/tests/test_callframe.py @@ -104,7 +104,6 @@ class TestCallFrame(unittest.TestCase): decoded_FDE = entries[1].get_decoded() self.assertEqual(decoded_FDE.reg_order, list(range(9))) - #self.assertEqual(len(decoded_FDE.table), 1) self.assertEqual(decoded_FDE.table[0]['cfa'].reg, 7) self.assertEqual(decoded_FDE.table[0]['cfa'].offset, 0) self.assertEqual(decoded_FDE.table[0]['pc'], 0x11223344) @@ -120,8 +119,6 @@ class TestCallFrame(unittest.TestCase): self.assertEqual(decoded_FDE.table[5][4].arg, -12) self.assertEqual(decoded_FDE.table[6]['pc'], 0x11223344 + 64) self.assertEqual(decoded_FDE.table[9]['pc'], 0x11223344 + 76) - self.assertEqual(decoded_FDE.table[9][6].type, RegisterRule.SAME_VALUE) - self.assertEqual(decoded_FDE.table[10]['pc'], 0x11223344 + 80) def test_describe_CFI_instructions(self): # The data here represents a single CIE -- 2.30.2