From 47a4711744568c48b0f39e1d8f1d2c589f027453 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Wed, 14 Dec 2011 06:54:09 +0200 Subject: [PATCH] completed descriptions of --debug-info=frames. readelf tests run --- elftools/dwarf/callframe.py | 11 +++-- elftools/dwarf/descriptions.py | 80 ++++++++++++++++++++++++++++++---- elftools/dwarf/dwarfinfo.py | 5 +++ scripts/readelf.py | 11 ++--- tests/run_readelf_tests.py | 21 +++++++-- 5 files changed, 108 insertions(+), 20 deletions(-) diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py index 2d51317..09f80d4 100644 --- a/elftools/dwarf/callframe.py +++ b/elftools/dwarf/callframe.py @@ -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): diff --git a/elftools/dwarf/descriptions.py b/elftools/dwarf/descriptions.py index b400310..91037d3 100644 --- a/elftools/dwarf/descriptions.py +++ b/elftools/dwarf/descriptions.py @@ -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': diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py index 7190a21..bc80224 100644 --- a/elftools/dwarf/dwarfinfo.py +++ b/elftools/dwarf/dwarfinfo.py @@ -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. """ diff --git a/scripts/readelf.py b/scripts/readelf.py index 01c4d1f..454cba3 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -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 diff --git a/tests/run_readelf_tests.py b/tests/run_readelf_tests.py index 7386e03..7df91d7 100755 --- a/tests/run_readelf_tests.py +++ b/tests/run_readelf_tests.py @@ -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 -- 2.30.2