starting skeleton implementation of call frame parsing
authorEli Bendersky <eliben@gmail.com>
Fri, 9 Dec 2011 14:53:18 +0000 (16:53 +0200)
committerEli Bendersky <eliben@gmail.com>
Fri, 9 Dec 2011 14:53:18 +0000 (16:53 +0200)
elftools/dwarf/callframe.py [new file with mode: 0644]
elftools/dwarf/dwarfinfo.py
elftools/dwarf/lineprogram.py
elftools/dwarf/structs.py
elftools/elf/elffile.py

diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py
new file mode 100644 (file)
index 0000000..280caa4
--- /dev/null
@@ -0,0 +1,35 @@
+#-------------------------------------------------------------------------------
+# elftools: dwarf/callframe.py
+#
+# DWARF call frame information
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from ..common.utils import (struct_parse)
+from .structs import DWARFStructs
+
+
+class CallFrameInfo(object):
+    def __init__(self, stream, size, base_structs):
+        self.stream = stream
+        self.size = size
+        self.base_structs = base_structs
+
+    def _parse_entries(self):
+        offset = 0
+        while offset < self.size:
+            entry_length = struct_parse(
+                self.base_structs.Dwarf_uint32(''), self.stream, offset)
+            dwarf_format = 64 if entry_length == 0xFFFFFFFF else 32
+
+            entry_structs = DWARFStructs(
+                little_endian=self.base_structs.little_endian,
+                dwarf_format=dwarf_format,
+                address_size=self.base_structs.address_size)
+
+            # ZZZ: it will be easier to split entry reading:
+            # header: what comes before the instructions
+            # the instructions are parsed separately (their length is computed
+            # from the length and the tell() after parsing the header)
+
index e0c9fd053b993de0189095e8b4070b3111070cf9..807e707612486baaa97cede8c3a7fd58dcdc1385 100644 (file)
@@ -36,7 +36,7 @@ DebugSectionDescriptor = namedtuple('DebugSectionDescriptor',
 #
 # machine_arch:
 #   Machine architecture as a string. For example 'x86' or 'x64'
-#               
+#
 #
 DwarfConfig = namedtuple('DwarfConfig',
     'little_endian machine_arch')
@@ -50,6 +50,7 @@ class DWARFInfo(object):
             config,
             debug_info_sec,
             debug_abbrev_sec,
+            debug_frame_sec,
             debug_str_sec,
             debug_line_sec):
         """ config:
@@ -61,6 +62,7 @@ class DWARFInfo(object):
         self.config = config
         self.debug_info_sec = debug_info_sec
         self.debug_abbrev_sec = debug_abbrev_sec
+        self.debug_frame_sec = debug_frame_sec
         self.debug_str_sec = debug_str_sec
         self.debug_line_sec = debug_line_sec
 
@@ -71,13 +73,13 @@ class DWARFInfo(object):
             little_endian=self.config.little_endian,
             dwarf_format=32,
             address_size=4)
-        
+
         # A list of CUs. Populated lazily when they're actually requested.
         self._CUs = None
-        
+
         # Cache for abbrev tables: a dict keyed by offset
         self._abbrevtable_cache = {}
-    
+
     def iter_CUs(self):
         """ Yield all the compile units (CompileUnit objects) in the debug info
         """
@@ -88,12 +90,12 @@ class DWARFInfo(object):
     def get_abbrev_table(self, offset):
         """ Get an AbbrevTable from the given offset in the debug_abbrev
             section.
-            
+
             The only verification done on the offset is that it's within the
             bounds of the section (if not, an exception is raised).
             It is the caller's responsibility to make sure the offset actually
             points to a valid abbreviation table.
-            
+
             AbbrevTable objects are cached internally (two calls for the same
             offset will return the same object).
         """
@@ -106,13 +108,13 @@ class DWARFInfo(object):
                 stream=self.debug_abbrev_sec.stream,
                 offset=offset)
         return self._abbrevtable_cache[offset]
-    
+
     def get_string_from_table(self, offset):
         """ Obtain a string from the string table section, given an offset 
             relative to the section.
         """
         return parse_cstring_from_stream(self.debug_str_sec.stream, offset)
-    
+
     def line_program_for_CU(self, CU):
         """ Given a CU object, fetch the line program it points to from the
             .debug_line section.
@@ -126,9 +128,9 @@ class DWARFInfo(object):
                     top_DIE.attributes['DW_AT_stmt_list'].value, CU.structs)
         else:
             return None
-        
+
     #------ PRIVATE ------#
-    
+
     def _parse_CUs(self):
         """ Parse CU entries from debug_info.
         """
@@ -146,7 +148,7 @@ class DWARFInfo(object):
             initial_length = struct_parse(
                 self.structs.Dwarf_uint32(''), self.debug_info_sec.stream, offset)
             dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32
-            
+
             # At this point we still haven't read the whole header, so we don't
             # know the address_size. Therefore, we're going to create structs
             # with a default address_size=4. If, after parsing the header, we
index 8f8485c8eaae03798d2f32d9cece52d54e238f8f..361511f65b71a76d00d878487017cc6405f916e5 100644 (file)
@@ -105,7 +105,8 @@ class LineProgram(object):
             Note that this contains more information than absolutely required
             for the line table. The line table can be easily extracted from
             the list of entries by looking only at entries with non-None
-            state.
+            state. The extra information is mainly for the purposes of display
+            with readelf and debugging.
         """
         if self._decoded_entries is None:
             self._decoded_entries = self._decode_line_program()
index 2c003258b9c66445d3969c24f08821ea31363d70..d25178c683bbb54bc8186c5c0c8d3e5dcbc8922d 100644 (file)
@@ -63,6 +63,12 @@ class DWARFStructs(object):
             Dwarf_lineprog_file_entry (+):
                 A single file entry in a line program header or instruction
         
+            Dwarf_CIE_header (+):
+                A call-frame CIE
+
+            Dwarf_FDE_header (+):
+                A call-frame FDE
+
         See also the documentation of public methods.
     """
     def __init__(self, little_endian, dwarf_format, address_size):
@@ -217,7 +223,23 @@ class DWARFStructs(object):
                 lambda obj, ctx: len(obj.name) == 0,
                 self.Dwarf_lineprog_file_entry),
             )
-        
+
+    def _create_callframe_entry_headers(self):
+        self.Dwarf_CIE_header = Struct('Dwarf_CIE_header',
+            self.Dwarf_initial_length('length'),
+            self.Dwarf_offset('CIE_id'),
+            self.Dwarf_uint8('version'),
+            CString('augmentation'),
+            self.Dwarf_uleb128('code_alignment_factor'),
+            self.Dwarf_sleb128('data_alignment_factor'),
+            self.Dwarf_uleb128('return_address_register'))
+
+        self.Dwarf_FDE_header = Struct('Dwarf_FDE_header',
+            self.Dwarf_initial_length('length'),
+            self.Dwarf_offset('CIE_pointer'),
+            self.Dwarf_target_addr('initial_location'),
+            self.Dwarf_target_addr('address_range'))
+
     def _make_block_struct(self, length_field):
         """ Create a struct for DW_FORM_block<size> 
         """
index 0e3530d8febf3e734c154e89d5b04d9def8716b5..9fd26348c5f6763c281323dc1fe4c29aca3a230c 100644 (file)
@@ -96,20 +96,20 @@ class ELFFile(object):
         """
         segment_header = self._get_segment_header(n)
         return self._make_segment(segment_header)
-    
+
     def iter_segments(self):
         """ Yield all the segments in the file
         """
         for i in range(self.num_segments()):
             yield self.get_segment(i)
-    
+
     def has_dwarf_info(self):
         """ Check whether this file appears to have debugging information. 
             We assume that if it has the debug_info section, it has all theother
             required sections as well.
         """
         return bool(self.get_section_by_name('.debug_info'))
-    
+
     def get_dwarf_info(self, relocate_dwarf_sections=True):
         """ Return a DWARFInfo object representing the debugging information in
             this file.
@@ -119,27 +119,28 @@ class ELFFile(object):
         """
         # Expect that has_dwarf_info was called, so at least .debug_info is 
         # present. Check also the presence of other must-have debug sections.
+        # Sections that aren't found will be passed as None to DWARFInfo.
         #
         debug_sections = {}
         for secname in ('.debug_info', '.debug_abbrev', '.debug_str', 
-                        '.debug_line'):
+                        '.debug_line', '.debug_frame'):
             section = self.get_section_by_name(secname)
-            elf_assert(
-                section is not None, 
-                "Expected to find DWARF section '%s' in the file" % (
-                    secname))
-            debug_sections[secname] = self._read_dwarf_section(
-                    section,
-                    relocate_dwarf_sections)
-        
+            if section is None:
+                debug_sections[secname] = None
+            else:
+                debug_sections[secname] = self._read_dwarf_section(
+                        section,
+                        relocate_dwarf_sections)
+
         return DWARFInfo(
                 config=DwarfConfig(
                     little_endian=self.little_endian,
                     machine_arch=self.get_machine_arch()),
                 debug_info_sec=debug_sections['.debug_info'],
                 debug_abbrev_sec=debug_sections['.debug_abbrev'],
+                debug_frame_sec=debug_sections['.debug_frame'],
                 debug_str_sec=debug_sections['.debug_str'],
-                debug_line_sec=debug_sections['.debug_line'])                
+                debug_line_sec=debug_sections['.debug_line'])
 
     def get_machine_arch(self):
         if self['e_machine'] == 'EM_X86_64':
@@ -148,14 +149,14 @@ class ELFFile(object):
             return 'x86'
         else:
             return '<unknown>'
-        
+
     #-------------------------------- PRIVATE --------------------------------#
-    
+
     def __getitem__(self, name):
         """ Implement dict-like access to header entries
         """
         return self.header[name]
-        
+
     def _identify_file(self):
         """ Verify the ELF file and identify its class and endianness.
         """
@@ -166,7 +167,7 @@ class ELFFile(object):
         self.stream.seek(0)
         magic = self.stream.read(4)
         elf_assert(magic == '\x7fELF', 'Magic number does not match')
-        
+
         ei_class = self.stream.read(1)
         if ei_class == '\x01':
             self.elfclass = 32
@@ -174,7 +175,7 @@ class ELFFile(object):
             self.elfclass = 64
         else:
             raise ELFError('Invalid EI_CLASS %s' % repr(ei_class))
-        
+
         ei_data = self.stream.read(1)
         if ei_data == '\x01':
             self.little_endian = True