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):
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 = {}
# 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):
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
"""
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 = {}
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')
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):
"""
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')
# 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
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':
+ }
+
#-------------------------------------------------------------------------------
from ..construct import CString
from ..common.utils import struct_parse, elf_assert
-from .relocation import Relocation
class Section(object):
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.
# 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:' %