From 6062bf7f7dfe4c188971c5cc6f6b54b7acaab09b Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Wed, 23 Nov 2011 06:54:40 +0200 Subject: [PATCH] nothing works because we're in the middle of a relocation revamp - BUT IT WILL!! --- elftools/dwarf/dwarfinfo.py | 35 +++++---- elftools/elf/elffile.py | 113 ++++++++++----------------- elftools/elf/relocation.py | 151 ++++++++++++++++++++++++++++++++++++ elftools/elf/sections.py | 46 ----------- scripts/readelf.py | 2 + 5 files changed, 212 insertions(+), 135 deletions(-) diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py index 1e2208b..f32c6aa 100644 --- a/elftools/dwarf/dwarfinfo.py +++ b/elftools/dwarf/dwarfinfo.py @@ -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 = {} diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 0a12d4c..1f81c31 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -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') diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py index ab53927..abb35bd 100644 --- a/elftools/elf/relocation.py +++ b/elftools/elf/relocation.py @@ -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': + } + diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index 1f4bf3d..11aa170 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -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. diff --git a/scripts/readelf.py b/scripts/readelf.py index efbcf21..85b8f0b 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -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:' % -- 2.30.2