completed descriptions of --debug-info=frames. readelf tests run
authorEli Bendersky <eliben@gmail.com>
Wed, 14 Dec 2011 04:54:09 +0000 (06:54 +0200)
committerEli Bendersky <eliben@gmail.com>
Wed, 14 Dec 2011 04:54:09 +0000 (06:54 +0200)
elftools/dwarf/callframe.py
elftools/dwarf/descriptions.py
elftools/dwarf/dwarfinfo.py
scripts/readelf.py
tests/run_readelf_tests.py

index 2d51317a0bb7cc40147b8ac2262e4cb7a684b3c8..09f80d44988220cc5b7b65d90bf63eec3e69c167 100644 (file)
@@ -31,8 +31,9 @@ class CIE(object):
         Contains a header and a list of instructions (CallFrameInstruction).
         offset: the offset of this entry from the beginning of the section
     """
-    def __init__(self, header, instructions, offset):
+    def __init__(self, header, structs, instructions, offset):
         self.header = header
+        self.structs = structs
         self.instructions = instructions
         self.offset = offset
 
@@ -48,8 +49,9 @@ class FDE(object):
         reference to the CIE object associated with this FDE.
         offset: the offset of this entry from the beginning of the section
     """
-    def __init__(self, header, instructions, offset, cie):
+    def __init__(self, header, structs, instructions, offset, cie):
         self.header = header
+        self.structs = structs
         self.instructions = instructions
         self.offset = offset
         self.cie = cie
@@ -152,13 +154,14 @@ class CallFrameInfo(object):
 
         if is_CIE:
             self._entry_cache[offset] = CIE(
-                header=header, instructions=instructions, offset=offset)
+                header=header, instructions=instructions, offset=offset,
+                structs=entry_structs)
         else: # FDE
             with preserve_stream_pos(self.stream):
                 cie = self._parse_entry_at(header['CIE_pointer'])
             self._entry_cache[offset] = FDE(
                 header=header, instructions=instructions, offset=offset,
-                cie=cie)
+                structs=entry_structs, cie=cie)
         return self._entry_cache[offset]
 
     def _parse_instructions(self, structs, offset, end_offset):
index b40031049e67fe8b99be5ccaf932cab6c486d70d..91037d3d4f24066a8b40f95d9c07ee5149d98e4f 100644 (file)
@@ -11,7 +11,8 @@ from collections import defaultdict
 from .constants import *
 from .dwarf_expr import GenericExprVisitor
 from .die import DIE
-from ..common.utils import preserve_stream_pos
+from ..common.utils import preserve_stream_pos, dwarf_assert
+from .callframe import instruction_name, CIE, FDE
 
 
 def set_global_machine_arch(machine_arch):
@@ -37,19 +38,82 @@ def describe_attr_value(attr, die, section_offset):
     return str(val_description) + '\t' + extra_info    
 
 
-def describe_CFI_instruction(instruction, entry, context):
-    # ZZZ: just get a list of instructions and keep the context internally!!
-    """ Given a CFI instruction, return its textual description. Also needs the
-        entry that contains this instruction.
-        context is a
+def describe_CFI_instructions(entry):
+    """ Given a CFI entry (CIE or FDE), return the textual description of its
+        instructions.
     """
-    return ''
+    def _assert_FDE_instruction(instr):
+        dwarf_assert(
+            isinstance(entry, FDE), 
+            'Unexpected instruction "%s" for a CIE' % instr)
+
+    def _full_reg_name(regnum):
+        return 'r%s (%s)' % (regnum, describe_reg_name(regnum))
+
+    if isinstance(entry, CIE):
+        cie = entry
+    else: # FDE
+        cie = entry.cie
+        pc = entry['initial_location']
+
+    s = ''
+    for instr in entry.instructions:
+        name = instruction_name(instr.opcode)
+
+        if name in ('DW_CFA_offset',
+                    'DW_CFA_offset_extended', 'DW_CFA_offset_extended_sf',
+                    'DW_CFA_val_offset', 'DW_CFA_val_offset_sf'):
+            s += '  %s: %s at cfa%+d\n' % (
+                name, _full_reg_name(instr.args[0]),
+                instr.args[1] * cie['data_alignment_factor'])
+        elif name in (  'DW_CFA_restore', 'DW_CFA_restore_extended',
+                        'DW_CFA_undefined', 'DW_CFA_same_value',
+                        'DW_CFA_def_cfa_register'):
+            s += '  %s: %s\n' % (name, _full_reg_name(instr.args[0]))
+        elif name == 'DW_CFA_register':
+            s += '  %s: %s in %s' % (
+                name, _full_reg_name(instr.args[0]),
+                _full_reg_name(instr.args[1]))
+        elif name == 'DW_CFA_set_loc':
+            pc = instr.args[0]
+            s += '  %s: %08x\n' % (name, pc)
+        elif name in (  'DW_CFA_advance_loc1', 'DW_CFA_advance_loc2',
+                        'DW_CFA_advance_loc4', 'DW_CFA_advance_loc'):
+            _assert_FDE_instruction(instr)
+            factored_offset = instr.args[0] * cie['code_alignment_factor']
+            s += '  %s: %s to %08x\n' % (
+                name, factored_offset, factored_offset + pc)
+            pc += factored_offset
+        elif name in (  'DW_CFA_remember_state', 'DW_CFA_restore_state',
+                        'DW_CFA_nop'):
+            s += '  %s\n' % name
+        elif name in ( 'DW_CFA_def_cfa', 'DW_CFA_def_cfa_sf'):
+            s += '  %s: %s ofs %s\n' % (
+                name, _full_reg_name(instr.args[0]), instr.args[1])
+        elif name == 'DW_CFA_def_cfa_offset':
+            s += '  %s: %s\n' % (name, instr.args[0])
+        elif name == 'DW_CFA_def_cfa_expression':
+            expr_dumper = ExprDumper(entry.structs)
+            expr_str = dumper.process_expr(instr.args[0])
+            s += '  %s: (%s)\n' % (name, expr_str)
+        elif name == 'DW_CFA_expression':
+            expr_dumper = ExprDumper(entry.structs)
+            expr_str = dumper.process_expr(instr.args[1])
+            s += '  %s: %s (%s)\n' % (
+                name, _full_reg_name(instr.args[0]), expr_str)
+        else:
+            s += '  %s: <??>\n' % name
+
+    return s
 
 
-def describe_reg_name(regnum, machine_arch):
+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.
     """
+    if machine_arch is None:
+        machine_arch = _MACHINE_ARCH
+
     if machine_arch == 'x86':
         return _REG_NAMES_x86[regnum]
     elif machine_arch == 'x64':
index 7190a21f48934ddaeb4962a20ad97c40b01edbe0..bc80224d9634a72da62473d400cfaf803da3bcbd 100644 (file)
@@ -132,6 +132,11 @@ class DWARFInfo(object):
         else:
             return None
 
+    def has_CFI(self):
+        """ Does this dwarf info has a CFI section?
+        """
+        return self.debug_frame_sec is not None
+
     def CFI_entries(self):
         """ Get a list of CFI entries from the .debug_frame section.
         """
index 01c4d1f3e2493919a8fb61dce89558bc41cb03be..454cba374fefbd91f8f26d0099e0e317f3d445a9 100755 (executable)
@@ -36,7 +36,7 @@ from elftools.elf.descriptions import (
     )
 from elftools.dwarf.dwarfinfo import DWARFInfo
 from elftools.dwarf.descriptions import (
-    describe_attr_value, set_global_machine_arch, describe_CFI_instruction)
+    describe_attr_value, set_global_machine_arch, describe_CFI_instructions)
 from elftools.dwarf.constants import (
     DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
 from elftools.dwarf.callframe import CIE, FDE
@@ -604,6 +604,8 @@ class ReadElf(object):
     def _dump_debug_frames(self):
         """ Dump the raw frame information from .debug_frame
         """
+        if not self._dwarfinfo.has_CFI():
+            return
         self._emitline('Contents of the .debug_frame section:')
 
         for entry in self._dwarfinfo.CFI_entries():
@@ -615,6 +617,7 @@ class ReadElf(object):
                 self._emitline('  Code alignment factor: %u' % entry['code_alignment_factor'])
                 self._emitline('  Data alignment factor: %d' % entry['data_alignment_factor'])
                 self._emitline('  Return address column: %d' % entry['return_address_register'])
+                self._emitline()
             else: # FDE
                 self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                     entry.offset,
@@ -624,10 +627,8 @@ class ReadElf(object):
                     entry['initial_location'],
                     entry['initial_location'] + entry['address_range']))
 
-            if len(entry.instructions) > 0:
-                self._emitline('')
-                for instr in entry.instructions:
-                    self._emitline(describe_CFI_instruction(instr, entry))
+            self._emit(describe_CFI_instructions(entry))
+        self._emitline()
 
     def _emit(self, s=''):
         """ Emit an object to output
index 7386e03c27062b2adcca2322042b0e71e1229298..7df91d79950fb8992cf09095d6484fc4ed1efe3f 100755 (executable)
@@ -52,7 +52,8 @@ def run_test_on_file(filename):
     testlog.info("Running test on file '%s'" % filename)
     for option in [
             '-e', '-s', '-r', '-x.text', '-p.shstrtab',
-            '--debug-dump=info', '--debug-dump=decodedline']:
+            '--debug-dump=info', '--debug-dump=decodedline',
+            '--debug-dump=frames']:
         testlog.info("..option='%s'" % option)
         # stdouts will be a 2-element list: output of readelf and output 
         # of scripts/readelf.py
@@ -80,6 +81,7 @@ def run_test_on_file(filename):
 
 def compare_output(s1, s2):
     """ Compare stdout strings s1 and s2.
+        s1 is from readelf, s2 from elftools readelf.py
         Return pair success, errmsg. If comparison succeeds, success is True
         and errmsg is empty. Otherwise success is False and errmsg holds a
         description of the mismatch.
@@ -93,14 +95,27 @@ def compare_output(s1, s2):
     """
     def prepare_lines(s):
         return [line for line in s.lower().splitlines() if line.strip() != '']
+    def filter_readelf_lines(lines):
+        filter_out = False
+        for line in lines:
+            if 'of the .eh_frame section' in line:
+                filter_out = True
+            elif 'of the .debug_frame section' in line:
+                filter_out = False
+            if not filter_out:
+                yield line
+        
     lines1 = prepare_lines(s1)
     lines2 = prepare_lines(s2)
+
+    lines1 = list(filter_readelf_lines(lines1))
+
+    flag_after_symtable = False
+
     if len(lines1) != len(lines2):
         return False, 'Number of lines different: %s vs %s' % (
                 len(lines1), len(lines2))
 
-    flag_after_symtable = False
-
     for i in range(len(lines1)):
         if 'symbol table' in lines1[i]:
             flag_after_symtable = True