nothing works because we're in the middle of a relocation revamp - BUT IT WILL!!
authorEli Bendersky <eliben@gmail.com>
Wed, 23 Nov 2011 04:54:40 +0000 (06:54 +0200)
committerEli Bendersky <eliben@gmail.com>
Wed, 23 Nov 2011 04:54:40 +0000 (06:54 +0200)
elftools/dwarf/dwarfinfo.py
elftools/elf/elffile.py
elftools/elf/relocation.py
elftools/elf/sections.py
scripts/readelf.py

index 1e2208b9ee826313fe9d194525dbd5764b933e90..f32c6aa1fbe6fb649c7647178b7a3a01ce9ddfb5 100644 (file)
@@ -17,9 +17,15 @@ from .abbrevtable import AbbrevTable
 from .dwarfrelocationmanager import DWARFRelocationManager
 
 
-# Describes a debug section in a stream: offset and size
+# Describes a debug section
+# 
+# stream: a stream object containing the data of this section
+# name: section name in the container file
+# global_offset: the global offset of the section in its container file
+# size: the size of the section's data, in bytes
 #
-DebugSectionLocator = namedtuple('DebugSectionLocator', 'offset size')
+DebugSectionDescriptor = namedtuple('DebugSectionLocator', 
+        'stream name global_offset size')
 
 
 class DWARFInfo(object):
@@ -27,29 +33,26 @@ class DWARFInfo(object):
         various parts of the debug infromation.
     """
     def __init__(self,
-            stream,
             elffile,
-            debug_info_loc,
-            debug_abbrev_loc,
-            debug_str_loc,
-            debug_line_loc):
+            debug_info_sec,
+            debug_abbrev_sec,
+            debug_str_sec,
+            debug_line_sec):
         """ stream: 
                 A stream (file-like object) that contains debug sections
             
             elffile:
                 ELFFile reference
 
-            debug_*_loc:
-                DebugSectionLocator for this section, specifying where it can
-                be found in the stream
+            debug_*_sec:
+                DebugSectionDescriptor for this section
         """
-        self.stream = stream
-        self.debug_info_loc = debug_info_loc
-        self.debug_abbrev_loc = debug_abbrev_loc
-        self.debug_str_loc = debug_str_loc
-        self.debug_line_loc = debug_line_loc
-        
         self.elffile = elffile
+        self.debug_info_sec = debug_info_sec
+        self.debug_abbrev_sec = debug_abbrev_sec
+        self.debug_str_sec = debug_str_sec
+        self.debug_line_sec = debug_line_sec
+        
         self.little_endian = self.elffile.little_endian
 
         self.relocation_manager = {}
index 0a12d4cef2570eaba06acf00595e99b45929884c..1f81c31e1b0c18baa9fedb69c926c4136d1b0a1b 100644 (file)
@@ -6,16 +6,17 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-from ..common.exceptions import ELFError, ELFRelocationError
+from cStringIO import StringIO
+from ..common.exceptions import ELFError
 from ..common.utils import struct_parse, elf_assert
 from ..construct import ConstructError
 from .structs import ELFStructs
 from .sections import (
-        Section, StringTableSection, SymbolTableSection, NullSection,
-        RelocationSection)
+        Section, StringTableSection, SymbolTableSection, NullSection)
 from .segments import Segment, InterpSegment
 from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
-from ..dwarf.dwarfinfo import DWARFInfo, DebugSectionLocator
+from .relocation import RelocationHandler
+from ..dwarf.dwarfinfo import DWARFInfo, DebugSectionDescriptor
 
 
 class ELFFile(object):
@@ -23,7 +24,10 @@ class ELFFile(object):
         contents of an ELF file.
     
         Accessible attributes:
-        
+
+            stream:
+                The stream holding the data of the file
+
             elfclass: 
                 32 or 64 - specifies the word size of the target machine
             
@@ -104,11 +108,14 @@ class ELFFile(object):
         """
         return bool(self.get_section_by_name('.debug_info'))
     
-    def get_dwarf_info(self):
+    def get_dwarf_info(self, relocate_dwarf_sections=True):
         """ Return a DWARFInfo object representing the debugging information in
             this file.
+
+            If relocate_dwarf_sections is True, relocations for DWARF sections
+            are looked up and applied.
         """
-        # Expect has_dwarf_info that was called, so at least .debug_info is 
+        # Expect that has_dwarf_info was called, so at least .debug_info is 
         # present. Check also the presence of other must-have debug sections.
         #
         debug_sections = {}
@@ -119,17 +126,16 @@ class ELFFile(object):
                 section is not None, 
                 "Expected to find DWARF section '%s' in the file" % (
                     secname))
-            debug_sections[secname] = DebugSectionLocator(
-                offset=section['sh_offset'],
-                size=section['sh_size'])
+            debug_sections[secname] = self._read_dwarf_section(
+                    section,
+                    relocate_dwarf_sections)
         
         return DWARFInfo(
-                stream=self.stream,
                 elffile=self,
-                debug_info_loc=debug_sections['.debug_info'],
-                debug_abbrev_loc=debug_sections['.debug_abbrev'],
-                debug_str_loc=debug_sections['.debug_str'],
-                debug_line_loc=debug_sections['.debug_line'])                
+                debug_info_sec=debug_sections['.debug_info'],
+                debug_abbrev_sec=debug_sections['.debug_abbrev'],
+                debug_str_sec=debug_sections['.debug_str'],
+                debug_line_sec=debug_sections['.debug_line'])                
             
     def architecture_is_x86(self):
         return self['e_machine'] in ('EM_386', 'EM_486')
@@ -137,22 +143,6 @@ class ELFFile(object):
     def architecture_is_x64(self):
         return self['e_machine'] == 'EM_X86_64'
         
-    def apply_relocation(self, reloc_section, reloc_index, offset, value):
-        """ Apply a relocation to the offset. The original value at offset is
-            also provided. Return a relocated value that should be written
-            back into the offset.
-
-            The relocation to apply is specified by an index and a relocation
-            section where this index points.
-
-            Throw ELFRelocationError if there's a problem with the relocation.
-        """
-        # The symbol table associated with this relocation section
-        symtab = self.get_section(reloc_section['sh_link'])
-        # Relocation object
-        reloc = reloc_section.get_relocation(reloc_index)
-        return self._do_apply_relocation(reloc, symtab, offset, value)
-
     #-------------------------------- PRIVATE --------------------------------#
     
     def __getitem__(self, name):
@@ -272,48 +262,25 @@ class ELFFile(object):
         """
         return struct_parse(self.structs.Elf_Ehdr, self.stream, stream_pos=0)
 
-    def _do_apply_relocation(self, reloc, symtab, offset, value):
-        # Only basic sanity checking here
-        if reloc['r_info_sym'] >= symtab.num_symbols():
-            raise ELFRelocationError(
-                'Invalid symbol reference in relocation: index %s' % (
-                    reloc['r_info_sym']))
-        sym_value = symtab.get_symbol(reloc['r_info_sym'])['st_value']
-        reloc_type = reloc['r_info_type']
+    def _read_dwarf_section(self, section, relocate_dwarf_sections):
+        """ Read the contents of a DWARF section from the stream and return a
+            DebugSectionDescriptor. Apply relocations if asked to.
+        """
+        self.stream.seek(section['sh_offset'])
+        # The section data is read into a new stream, for processing
+        section_stream = StringIO(self.stream.read(section['sh_size']))
+
+        if relocate_dwarf_sections:
+            reloc_handler = RelocationHandler(self)
+            reloc_section = reloc_handler.find_relocations_for_section(section)
+            if reloc_section is not None:
+                reloc_handler.apply_section_relocations(
+                        section_stream, reloc_section)
 
-        if self.architecture_is_x86():
-            if reloc.is_RELA():
-                raise ELFRelocationError(
-                    'Unexpected RELA relocation for x86: %s' % reloc)
-            if reloc_type == ENUM_RELOC_TYPE_i386['R_386_NONE']:
-                # No relocation
-                return value
-            elif reloc_type == ENUM_RELOC_TYPE_i386['R_386_32']:
-                return sym_value + value
-            elif reloc_type == ENUM_RELOC_TYPE_i386['R_386_PC32']:
-                return sym_value + value - offset
-            else:
-                raise ELFRelocationError('Unsupported relocation type %s' % (
-                    reloc_type))
-        elif self.architecture_is_x64():
-            if not reloc.is_RELA():
-                raise ELFRelocationError(
-                    'Unexpected REL relocation for x64: %s' % reloc)
-            if reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_NONE']:
-                # No relocation
-                return value
-            elif reloc_type in (
-                    ENUM_RELOC_TYPE_x64['R_X86_64_64'],
-                    ENUM_RELOC_TYPE_x64['R_X86_64_32'],
-                    ENUM_RELOC_TYPE_x64['R_X86_64_32S']):
-                return sym_value + reloc['r_addend']
-            else:
-                raise ELFRelocationError('Unsupported relocation type %s' % (
-                    reloc_type))
-        else:
-            raise ELFRelocationError(
-                'Relocations not supported for architecture %s' % (
-                    self['e_machine']))
+        return DebugSectionDescriptor(
+                stream=section_stream,
+                name=section.name,
+                global_offset=section['sh_offset'],
+                size=section['sh_size'])
 
-        raise ELFRelocationError('unreachable relocation code')
 
index ab53927edd840d3f7fd6a4d2ca88ad8e9266232f..abb35bdf91565e2b6b38054ce04d416ef5017c03 100644 (file)
@@ -6,6 +6,10 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
+from collections import namedtuple
+
+from ..common.exceptions import ELFRelocationError
+
 
 class Relocation(object):
     """ Relocation object - representing a single relocation entry. Allows
@@ -35,3 +39,150 @@ class Relocation(object):
     def __str__(self):
         return self.__repr__()
 
+
+class RelocationSection(Section):
+    def __init__(self, header, name, stream, elffile):
+        super(RelocationSection, self).__init__(header, name, stream)
+        self.elffile = elffile
+        self.elfstructs = self.elffile.structs
+        if self.header['sh_type'] == 'SHT_REL':
+            expected_size = self.elfstructs.Elf_Rel.sizeof()
+            self.entry_struct = self.elfstructs.Elf_Rel
+        elif self.header['sh_type'] == 'SHT_RELA':
+            expected_size = self.elfstructs.Elf_Rela.sizeof()
+            self.entry_struct = self.elfstructs.Elf_Rela
+        else:
+            elf_assert(False, 'Unknown relocation type section')
+
+        elf_assert(
+            self.header['sh_entsize'] == expected_size,
+            'Expected sh_entsize of SHT_REL section to be %s' % expected_size)
+
+    def is_RELA(self):
+        """ Is this a RELA relocation section? If not, it's REL.
+        """
+        return self.header['sh_type'] == 'SHT_RELA'
+
+    def num_relocations(self):
+        """ Number of relocations in the section
+        """
+        return self['sh_size'] // self['sh_entsize']
+
+    def get_relocation(self, n):
+        """ Get the relocation at index #n from the section (Relocation object)
+        """
+        entry_offset = self['sh_offset'] + n * self['sh_entsize']
+        entry = struct_parse(
+            self.entry_struct,
+            self.stream,
+            stream_pos=entry_offset)
+        return Relocation(entry, self.elffile)
+
+    def iter_relocations(self):
+        """ Yield all the relocations in the section
+        """
+        for i in range(self.num_relocations()):
+            yield self.get_relocation(i)
+
+
+class RelocationHandler(object):
+    """ Handles the logic of relocations in ELF files.
+    """
+    def __init__(self, elffile):
+        self.elffile = elffile
+
+    def find_relocations_for_section(self, section):
+        """ Given a section, find the relocation section for it in the ELF
+            file. Return a RelocationSection object, or None if none was
+            found.
+        """
+        reloc_section_names = (
+                '.rel' + section.name,
+                '.rela' + section.name)
+        # Find the relocation section aimed at this one. Currently assume
+        # that either .rel or .rela section exists for this section, but
+        # not both.
+        for relsection in self.iter_sections():
+            if (    isinstance(relsection, RelocationSection) and
+                    relsection.name in reloc_section_names):
+                return relsection
+        return None
+        
+    def apply_section_relocations(self, stream, reloc_section):
+        """ Apply all relocations in reloc_section (a RelocationSection object)
+            to the given stream, that contains the data of the section that is
+            being relocated. The stream is modified as a result.
+        """
+        # The symbol table associated with this relocation section
+        symtab = self.elffile.get_section(reloc_section['sh_link'])
+        for reloc in reloc_section.iter_relocations():
+            self._do_apply_relocation(stream, reloc, symtab)
+
+    def _do_apply_relocation(self, stream, reloc, symtab):
+        # ZZZ: steps
+        # 1. Read the value from the stream (with correct size and endianness)
+        # 2. Apply the relocation to the value
+        # 3. Write the relocated value back into the stream
+        #
+        # To make it generic, have a map of "relocation recipes" per
+        # relocation.
+        #
+
+
+        # Some basic sanity checking
+        if self.architecture_is_x86() and reloc.is_RELA():
+            raise ELFRelocationError(
+                'Unexpected RELA relocation for x86: %s' % reloc)
+        elif self.architecture_is_x64() and not reloc.is_RELA():
+            raise ELFRelocationError(
+                'Unexpected REL relocation for x64: %s' % reloc)
+
+        if reloc['r_info_sym'] >= symtab.num_symbols():
+            raise ELFRelocationError(
+                'Invalid symbol reference in relocation: index %s' % (
+                    reloc['r_info_sym']))
+
+        sym_value = symtab.get_symbol(reloc['r_info_sym'])['st_value']
+        reloc_type = reloc['r_info_type']
+
+        if self.architecture_is_x86():
+            if reloc_type == ENUM_RELOC_TYPE_i386['R_386_NONE']:
+                # No relocation
+                return value
+            elif reloc_type == ENUM_RELOC_TYPE_i386['R_386_32']:
+                return sym_value + value
+            elif reloc_type == ENUM_RELOC_TYPE_i386['R_386_PC32']:
+                return sym_value + value - offset
+            else:
+                raise ELFRelocationError('Unsupported relocation type %s' % (
+                    reloc_type))
+        elif self.architecture_is_x64():
+            if reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_NONE']:
+                # No relocation
+                return value
+            elif reloc_type in (
+                    ENUM_RELOC_TYPE_x64['R_X86_64_64'],
+                    ENUM_RELOC_TYPE_x64['R_X86_64_32'],
+                    ENUM_RELOC_TYPE_x64['R_X86_64_32S']):
+                return sym_value + reloc['r_addend']
+            else:
+                raise ELFRelocationError('Unsupported relocation type %s' % (
+                    reloc_type))
+        else:
+            raise ELFRelocationError(
+                'Relocations not supported for architecture %s' % (
+                    self['e_machine']))
+
+        raise ELFRelocationError('unreachable relocation code')
+
+    # Relocations are represented by "recipes". Each recipe specifies
+    _RELOCATION_RECIPE_TYPE = namedtuple('_RELOCATION_RECIPE_TYPE',
+        'bytesize has_addend calc_func')
+
+    def _reloc_calc_identity(value, offset, addend=0):
+        return value
+        
+    _RELOCATION_RECIPES = {
+        'R_386_NONE':
+    }
+
index 1f4bf3d0c82a1382e360b7ac73b7031cf47fb1ee..11aa170eab2c99998dc77cb2d6ca12c19f83bb61 100644 (file)
@@ -8,7 +8,6 @@
 #-------------------------------------------------------------------------------
 from ..construct import CString
 from ..common.utils import struct_parse, elf_assert
-from .relocation import Relocation
 
 
 class Section(object):
@@ -109,51 +108,6 @@ class SymbolTableSection(Section):
             yield self.get_symbol(i)
 
 
-class RelocationSection(Section):
-    def __init__(self, header, name, stream, elffile):
-        super(RelocationSection, self).__init__(header, name, stream)
-        self.elffile = elffile
-        self.elfstructs = self.elffile.structs
-        if self.header['sh_type'] == 'SHT_REL':
-            expected_size = self.elfstructs.Elf_Rel.sizeof()
-            self.entry_struct = self.elfstructs.Elf_Rel
-        elif self.header['sh_type'] == 'SHT_RELA':
-            expected_size = self.elfstructs.Elf_Rela.sizeof()
-            self.entry_struct = self.elfstructs.Elf_Rela
-        else:
-            elf_assert(False, 'Unknown relocation type section')
-
-        elf_assert(
-            self.header['sh_entsize'] == expected_size,
-            'Expected sh_entsize of SHT_REL section to be %s' % expected_size)
-
-    def is_RELA(self):
-        """ Is this a RELA relocation section? If not, it's REL.
-        """
-        return self.header['sh_type'] == 'SHT_RELA'
-
-    def num_relocations(self):
-        """ Number of relocations in the section
-        """
-        return self['sh_size'] // self['sh_entsize']
-        
-    def get_relocation(self, n):
-        """ Get the relocation at index #n from the section (Relocation object)
-        """
-        entry_offset = self['sh_offset'] + n * self['sh_entsize']
-        entry = struct_parse(
-            self.entry_struct,
-            self.stream,
-            stream_pos=entry_offset)
-        return Relocation(entry, self.elffile)
-
-    def iter_relocations(self):
-        """ Yield all the relocations in the section
-        """
-        for i in range(self.num_relocations()):
-            yield self.get_relocation(i)
-
-
 class Symbol(object):
     """ Symbol object - representing a single symbol entry from a symbol table
         section.
index efbcf21bf30cc071ca37220d4ff8a0dbac552296..85b8f0b1da530e59f62be5d9746c90df5fc0676e 100755 (executable)
@@ -497,6 +497,8 @@ class ReadElf(object):
         
         # Offset of the .debug_info section in the stream
         section_offset = self._dwarfinfo.debug_info_loc.offset
+
+        print '&&& section_offset', section_offset
         
         for cu in self._dwarfinfo.iter_CUs():
             self._emitline('  Compilation Unit @ offset %s:' %