From 5b13b17533901b22427cfa0c25fbf8ccb6764c61 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Sat, 10 Dec 2011 20:32:32 +0200 Subject: [PATCH] fixed default address size passing from ELFFile to DWARFInfo, which makes it possible to correctly read .debug_frame sections --- elftools/dwarf/callframe.py | 72 +++++++++++++++++++++++++------ elftools/dwarf/dwarfinfo.py | 18 ++++++-- elftools/elf/elffile.py | 1 + examples/elfclass_address_size.py | 33 ++++++++++++++ z.py | 14 +----- 5 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 examples/elfclass_address_size.py diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py index e352e52..77ccbb1 100644 --- a/elftools/dwarf/callframe.py +++ b/elftools/dwarf/callframe.py @@ -6,15 +6,19 @@ # Eli Bendersky (eliben@gmail.com) # This code is in the public domain #------------------------------------------------------------------------------- -from collections import namedtuple - from ..common.utils import (struct_parse, dwarf_assert) from .structs import DWARFStructs from .constants import * -CallFrameInstruction = namedtuple( - 'CallFrameInstruction', 'opcode args') +class CallFrameInstruction(object): + def __init__(self, opcode, args): + self.opcode = opcode + self.args = args + + def __repr__(self): + return '%s (0x%x): %s' % ( + instruction_name(self.opcode), self.opcode, self.args) class CIE(object): @@ -44,6 +48,12 @@ class CallFrameInfo(object): self.stream = stream self.size = size self.base_structs = base_structs + self.entries = None + + def get_entries(self): + if self.entries is None: + self.entries = self._parse_entries() + return self.entries def _parse_entries(self): entries = [] @@ -62,16 +72,16 @@ class CallFrameInfo(object): # Read the next field to see whether this is a CIE or FDE CIE_id = struct_parse( - entry_structs.Dwarf_offset('').parse, self.stream) + entry_structs.Dwarf_offset(''), self.stream) is_CIE = ( - dwarf_format == 32 and CIE_id = 0xFFFFFFFF or + (dwarf_format == 32 and CIE_id == 0xFFFFFFFF) or CIE_id == 0xFFFFFFFFFFFFFFFF) if is_CIE: - header_struct = self.Dwarf_CIE_header + header_struct = entry_structs.Dwarf_CIE_header else: - header_struct = self.Dwarf_FDE_header + header_struct = entry_structs.Dwarf_FDE_header # Parse the header, which goes up to and including the # return_address_register field @@ -80,14 +90,21 @@ class CallFrameInfo(object): # For convenience, compute the end offset for this entry end_offset = ( - offset + header.length - structs.initial_length_field_size()) + offset + header.length + + entry_structs.initial_length_field_size()) # At this point self.stream is at the start of the instruction list # for this entry instructions = self._parse_instructions( - structs, self.stream.tell(), end_offset) + entry_structs, self.stream.tell(), end_offset) + new_entry_class = CIE if is_CIE else FDE + entries.append(new_entry_class( + header=header, + instructions=instructions)) # ZZZ: for FDE's, I need some offset->CIE mapping cache stored + offset = self.stream.tell() + return entries def _parse_instructions(self, structs, offset, end_offset): """ Parse a list of CFI instructions from self.stream, starting with @@ -99,8 +116,8 @@ class CallFrameInfo(object): opcode = struct_parse(structs.Dwarf_uint8(''), self.stream, offset) args = [] - primary = opcode & 0b11000000 - primary_arg = opcode & 0b00111111 + primary = opcode & _PRIMARY_MASK + primary_arg = opcode & _PRIMARY_ARG_MASK if primary == DW_CFA_advance_loc: args = [primary_arg] elif primary == DW_CFA_offset: @@ -147,8 +164,37 @@ class CallFrameInfo(object): struct_parse(structs.Dwarf_uleb128(''), self.stream), struct_parse(structs.Dwarf_sleb128(''), self.stream)] else: - dwarf_assert(False, 'Unknown CFI opcode: %s' % opcode) + dwarf_assert(False, 'Unknown CFI opcode: 0x%x' % opcode) instructions.append(CallFrameInstruction(opcode=opcode, args=args)) + print instructions[-1] offset = self.stream.tell() + return instructions + + +def instruction_name(opcode): + """ Given an opcode, return the instruction name. + """ + primary = opcode & _PRIMARY_MASK + if primary == 0: + return _OPCODE_NAME_MAP[opcode] + else: + return _OPCODE_NAME_MAP[primary] + + +#---------------- PRIVATE ----------------# + +_PRIMARY_MASK = 0b11000000 +_PRIMARY_ARG_MASK = 0b00111111 + +# This dictionary is filled by automatically scanning the constants module +# for DW_CFA_* instructions, and mapping their values to names. Since all +# names were imported from constants with `import *`, we look in globals() +_OPCODE_NAME_MAP = {} +for name in list(globals().iterkeys()): + if name.startswith('DW_CFA'): + _OPCODE_NAME_MAP[globals()[name]] = name + + + diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py index 807e707..7190a21 100644 --- a/elftools/dwarf/dwarfinfo.py +++ b/elftools/dwarf/dwarfinfo.py @@ -15,6 +15,7 @@ from .structs import DWARFStructs from .compileunit import CompileUnit from .abbrevtable import AbbrevTable from .lineprogram import LineProgram +from .callframe import CallFrameInfo # Describes a debug section @@ -32,14 +33,16 @@ DebugSectionDescriptor = namedtuple('DebugSectionDescriptor', # DWARFInfo to be independent from any specific file format/container. # # little_endian: -# boolean flag specifying whether the data in the file is little endian. +# boolean flag specifying whether the data in the file is little endian # # machine_arch: # Machine architecture as a string. For example 'x86' or 'x64' # +# default_address_size: +# The default address size for the container file (sizeof pointer, in bytes) # DwarfConfig = namedtuple('DwarfConfig', - 'little_endian machine_arch') + 'little_endian machine_arch default_address_size') class DWARFInfo(object): @@ -72,7 +75,7 @@ class DWARFInfo(object): self.structs = DWARFStructs( little_endian=self.config.little_endian, dwarf_format=32, - address_size=4) + address_size=self.config.default_address_size) # A list of CUs. Populated lazily when they're actually requested. self._CUs = None @@ -129,6 +132,15 @@ class DWARFInfo(object): else: return None + def CFI_entries(self): + """ Get a list of CFI entries from the .debug_frame section. + """ + cfi = CallFrameInfo( + stream=self.debug_frame_sec.stream, + size=self.debug_frame_sec.size, + base_structs=self.structs) + return cfi.get_entries() + #------ PRIVATE ------# def _parse_CUs(self): diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 9fd2634..32cf631 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -135,6 +135,7 @@ class ELFFile(object): return DWARFInfo( config=DwarfConfig( little_endian=self.little_endian, + default_address_size=self.elfclass / 8, machine_arch=self.get_machine_arch()), debug_info_sec=debug_sections['.debug_info'], debug_abbrev_sec=debug_sections['.debug_abbrev'], diff --git a/examples/elfclass_address_size.py b/examples/elfclass_address_size.py new file mode 100644 index 0000000..c0cd94b --- /dev/null +++ b/examples/elfclass_address_size.py @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------------- +# elftools example: elfclass_address_size.py +# +# This example explores the ELF class (32 or 64-bit) and address size in each +# of the CUs in the DWARF information. +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- +import sys +from elftools.elf.elffile import ELFFile + + +def process_file(filename): + with open(filename) as f: + elffile = ELFFile(f) + print '%s: elfclass is %s' % (filename, elffile.elfclass) + + if elffile.has_dwarf_info(): + dwarfinfo = elffile.get_dwarf_info() + for CU in dwarfinfo.iter_CUs(): + print ' CU at offset 0x%x. address_size is %s' % ( + CU.cu_offset, CU['address_size']) + + +def main(): + for filename in sys.argv[1:]: + process_file(filename) + + +if __name__ == '__main__': + main() + diff --git a/z.py b/z.py index 632f376..e77d903 100644 --- a/z.py +++ b/z.py @@ -9,8 +9,6 @@ from elftools.elf.sections import * from elftools.elf.relocation import * -# read a little-endian, 64-bit file -es = ELFStructs(True, 64) stream = open('tests/testfiles/exe_simple64.elf', 'rb') #stream = open('binfiles/z32.elf', 'rb') @@ -22,14 +20,4 @@ print '===> %s sections!' % efile.num_sections() #~ print efile.has_dwarf_info() dwarfinfo = efile.get_dwarf_info() -CUs = list(dwarfinfo.iter_CUs()) -print 'num CUs:', len(CUs) -print 'CU:', CUs[2] - -lp = dwarfinfo.line_program_for_CU(CUs[2]) -print 'lp:', lp, lp.header -print 'linetable:', lp.get_line_table() -#for lp in dwarfinfo.iter_line_programs(): - #print lp - #print lp.header - +cfi_entries = dwarfinfo.CFI_entries() -- 2.30.2