the relocation manager for dwarf works! sdf
authorEli Bendersky <eliben@gmail.com>
Fri, 18 Nov 2011 10:02:57 +0000 (12:02 +0200)
committerEli Bendersky <eliben@gmail.com>
Fri, 18 Nov 2011 10:02:57 +0000 (12:02 +0200)
elftools/common/exceptions.py
elftools/dwarf/die.py
elftools/dwarf/dwarfinfo.py
elftools/dwarf/dwarfrelocationmanager.py
elftools/elf/descriptions.py
elftools/elf/elffile.py
scripts/readelf.py
z.py

index c2da24ca6e9ac5647ecee69c8e7cfcb6103e6b8c..26f1ba09f3e460b70f7b99c9ff1e8e4c0cbbf64f 100644 (file)
@@ -9,6 +9,9 @@
 class ELFError(Exception): 
     pass
 
+class ELFRelocationError(ELFError):
+    pass
+        
 class ELFParseError(ELFError):
     pass
 
index 0836aa0945ead471932ca68116e61cd8b49ddfea..f438934edc79cc22f2e776170075f807eddbe52c 100644 (file)
@@ -29,7 +29,8 @@ from ..common.utils import struct_parse, preserve_stream_pos
 #   (e.g. for a DW_FORM_strp it's the raw string offset into the table)
 #
 # offset:
-#   Offset of this attribute's value in the stream
+#   Offset of this attribute's value in the stream (absolute offset, relative
+#   the beginning of the whole stream)
 #
 AttributeValue = namedtuple(
     'AttributeValue', 'name form value raw_value offset')
@@ -159,12 +160,34 @@ class DIE(object):
         self.tag = abbrev_decl['tag']
         self.has_children = abbrev_decl.has_children()
         
+        # The offset of the .debug_info section in the stream. Used to compute
+        # relative offset of attribute values to the beginning of the section.
+        section_offset = self.dwarfinfo.debug_info_loc.offset
+
+        # Some attribute values need relocations. These are computed with the
+        # help of a DWARFRelocationManager for .debug_info, which is held by
+        # DWARFInfo for this purpose.
+        relocation_manager = self.dwarfinfo.relocation_manager['.debug_info']
+
         # Guided by the attributes listed in the abbreviation declaration, parse
         # values from the stream.
         #
         for name, form in abbrev_decl.iter_attr_specs():
             attr_offset = self.stream.tell()
             raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
+
+            # raw_value may need to be relocated, if there's a relocation
+            # registered for this offset in the relocation manager.
+            # Relocations are listed by offset relative to the beginning of
+            # the section.
+            offset_from_section = attr_offset - section_offset
+            if relocation_manager.has_relocation(offset_from_section):
+                # Applying the relocation may change the stream, so preserve it
+                with preserve_stream_pos(self.stream):
+                    raw_value = relocation_manager.apply_relocation(
+                            offset=offset_from_section,
+                            value=raw_value)
+
             value = self._translate_attr_value(form, raw_value)            
             self.attributes[name] = AttributeValue(
                 name=name,
index 051ea9f59d8e96f75d7d5995571733a08136bf47..1e2208b9ee826313fe9d194525dbd5764b933e90 100644 (file)
@@ -14,6 +14,7 @@ from ..common.utils import struct_parse, dwarf_assert
 from .structs import DWARFStructs
 from .compileunit import CompileUnit
 from .abbrevtable import AbbrevTable
+from .dwarfrelocationmanager import DWARFRelocationManager
 
 
 # Describes a debug section in a stream: offset and size
@@ -27,7 +28,7 @@ class DWARFInfo(object):
     """
     def __init__(self,
             stream,
-            little_endian,
+            elffile,
             debug_info_loc,
             debug_abbrev_loc,
             debug_str_loc,
@@ -35,9 +36,9 @@ class DWARFInfo(object):
         """ stream: 
                 A stream (file-like object) that contains debug sections
             
-            little_endian:
-                Section contents are in little-endian data format
-            
+            elffile:
+                ELFFile reference
+
             debug_*_loc:
                 DebugSectionLocator for this section, specifying where it can
                 be found in the stream
@@ -48,7 +49,13 @@ class DWARFInfo(object):
         self.debug_str_loc = debug_str_loc
         self.debug_line_loc = debug_line_loc
         
-        self.little_endian = little_endian
+        self.elffile = elffile
+        self.little_endian = self.elffile.little_endian
+
+        self.relocation_manager = {}
+        self.relocation_manager['.debug_info'] = DWARFRelocationManager(
+                elffile=self.elffile,
+                section_name='.debug_info')
         
         # This is the DWARFStructs the context uses, so it doesn't depend on 
         # DWARF format and address_size (these are determined per CU) - set them
index d8f7831fdb6317c7a9166668f4392bc9efbb84e0..048e1c6b9331cdc7cf7e9f3446a09ca1ea8e9c8c 100644 (file)
@@ -6,7 +6,6 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-
 from ..elf.sections import RelocationSection
 
 
@@ -20,28 +19,30 @@ class DWARFRelocationManager(object):
         self.section_name = section_name
         self._section = self.elffile.get_section_by_name(section_name)
 
-        # _relocs maps an offset in the section to a Relocation object
+        # _relocs maps an offset in the section to an index in the relocation
+        # table.
         # _reloc_section is the relocation section object
         # ... both are loaded by _load_relocations
         self._relocs = {}
         self._reloc_section = None
         self._load_relocations()
 
-        # _symtable: symbol table section attached to the relocation section
-        self._symtable = self.elffile.get_section(
-                self._reloc_section['sh_link'])
-
     def has_relocation(self, offset):
         """ Does the given offset have a relocation registered for it?
             The offset is relative to its section.
         """
         return offset in self._relocs
         
-    def apply_relocation(self, offset):
-        """ Apply the relocation registered for the given offset. Return the
-            relocated value.
+    def apply_relocation(self, offset, value):
+        """ Apply the relocation registered for the given offset. value is
+            the original value at that offset. Return the relocated value.
         """
-        reloc = self._relocs[offset]
+        reloc_index = self._relocs[offset]
+        return self.elffile.apply_relocation(
+                    reloc_section=self._reloc_section,
+                    reloc_index=reloc_index,
+                    offset=offset,
+                    value=value)
 
     def _load_relocations(self):
         # Currently assume that only a single relocation section will exist
@@ -53,7 +54,8 @@ class DWARFRelocationManager(object):
             if (    isinstance(section, RelocationSection) and
                     section.name in reloc_section_names):
                 self._reloc_section = section
-                for reloc in self._reloc_section.iter_relocations():
-                    self._relocs[reloc['r_offset']] = reloc
+                for i, reloc in enumerate(
+                        self._reloc_section.iter_relocations()):
+                    self._relocs[reloc['r_offset']] = i
                 break
 
index f404898841d02351b5b73730b905d6179ba3fa16..b7e87abf0efc1445c2eb575de29f24d46fbc2699 100644 (file)
@@ -68,10 +68,10 @@ def describe_symbol_visibility(x):
 def describe_symbol_shndx(x):
     return _DESCR_ST_SHNDX.get(x, '%3s' % x)
 
-def describe_reloc_type(x, e_machine):
-    if e_machine in ('EM_386', 'EM_486'):
+def describe_reloc_type(x, elffile):
+    if elffile.architecture_is_x86():
         return _DESCR_RELOC_TYPE_i386.get(x, _unknown)
-    elif e_machine in ('EM_X86_64', 'EM_L10M'):
+    elif elffile.architecture_is_x64():
         return _DESCR_RELOC_TYPE_x64.get(x, _unknown)
     else:
         return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)
index ebd04dd72ee3d7d8db18d7ab74407774095facca..0a12d4cef2570eaba06acf00595e99b45929884c 100644 (file)
@@ -6,7 +6,7 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-from ..common.exceptions import ELFError
+from ..common.exceptions import ELFError, ELFRelocationError
 from ..common.utils import struct_parse, elf_assert
 from ..construct import ConstructError
 from .structs import ELFStructs
@@ -14,6 +14,7 @@ from .sections import (
         Section, StringTableSection, SymbolTableSection, NullSection,
         RelocationSection)
 from .segments import Segment, InterpSegment
+from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
 from ..dwarf.dwarfinfo import DWARFInfo, DebugSectionLocator
 
 
@@ -124,12 +125,34 @@ class ELFFile(object):
         
         return DWARFInfo(
                 stream=self.stream,
-                little_endian=self.little_endian,
+                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'])                
             
+    def architecture_is_x86(self):
+        return self['e_machine'] in ('EM_386', 'EM_486')
+
+    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):
@@ -242,10 +265,55 @@ class ELFFile(object):
                 header=self._get_section_header(stringtable_section_num),
                 name='',
                 stream=self.stream)
-    
+
     def _parse_elf_header(self):
         """ Parses the ELF file header and assigns the result to attributes
             of this 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']
+
+        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']))
+
+        raise ELFRelocationError('unreachable relocation code')
+
index 986b7ae5f261b956867da92384dc6b0bdfe071b5..efbcf21bf30cc071ca37220d4ff8a0dbac552296 100755 (executable)
@@ -304,7 +304,7 @@ class ReadElf(object):
                     self._format_hex(rel['r_info'], 
                         fieldsize=hexwidth, lead0x=False),
                     describe_reloc_type(
-                        rel['r_info_type'], self.elffile['e_machine'])))
+                        rel['r_info_type'], self.elffile)))
 
                 if rel['r_info_sym'] == 0:
                     self._emitline()
diff --git a/z.py b/z.py
index 7c09211ede4723a3d49a04669e02a6f6d0dc4e1c..8bf4743f32bbbf87abf7843f1a38af90f6c058a1 100644 (file)
--- a/z.py
+++ b/z.py
@@ -29,10 +29,10 @@ print cu.structs.Dwarf_dw_form['DW_FORM_strp'].parse('\x01\x00\x00\x00\x01\x00\x
 print 'CU header', cu.header
 topdie = cu.get_top_DIE()
 
-#print topdie
+print topdie
 dinfo_sec = efile.get_section_by_name('.debug_info')
 relman = DWARFRelocationManager(efile, dinfo_sec.name)
 
 print relman._reloc_section.name, relman._reloc_section['sh_offset']
-pprint.pprint(relman._relocs)
+#pprint.pprint(relman._relocs)