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
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
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):
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):
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':
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.
"""
)
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
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():
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,
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
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
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.
"""
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