fixed default address size passing from ELFFile to DWARFInfo, which makes it possible...
authorEli Bendersky <eliben@gmail.com>
Sat, 10 Dec 2011 18:32:32 +0000 (20:32 +0200)
committerEli Bendersky <eliben@gmail.com>
Sat, 10 Dec 2011 18:32:32 +0000 (20:32 +0200)
elftools/dwarf/callframe.py
elftools/dwarf/dwarfinfo.py
elftools/elf/elffile.py
examples/elfclass_address_size.py [new file with mode: 0644]
z.py

index e352e52fc3a69617445d14880e39edd2678e3729..77ccbb150dbbd92405a3aed0ff0ebc8cff72406e 100644 (file)
@@ -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
+
+
+
 
index 807e707612486baaa97cede8c3a7fd58dcdc1385..7190a21f48934ddaeb4962a20ad97c40b01edbe0 100644 (file)
@@ -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):
index 9fd26348c5f6763c281323dc1fe4c29aca3a230c..32cf63195443bb0a56b17eecaf16e67bffddf56d 100644 (file)
@@ -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 (file)
index 0000000..c0cd94b
--- /dev/null
@@ -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 632f37605d7ade9144c3158009243c0c9c16cc75..e77d90316ad82a2387b985a63e40d75edde7558c 100644 (file)
--- 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()