import itertools
from .sections import Section, Symbol
+from .enums import ENUM_D_TAG
from .segments import Segment
+from .relocation import RelocationTable
from ..common.exceptions import ELFError
-from ..common.utils import struct_parse, parse_cstring_from_stream
+from ..common.utils import elf_assert, struct_parse, parse_cstring_from_stream
class _DynamicStringTable(object):
self._num_tags = n + 1
return self._num_tags
+ def get_relocation_tables(self):
+ """ Load all available relocation tables from DYNAMIC tags.
+
+ Returns a dictionary mapping found table types (REL, RELA,
+ JMPREL) to RelocationTable objects.
+ """
+
+ result = {}
+
+ if list(self.iter_tags('DT_REL')):
+ result['REL'] = RelocationTable(self.elffile,
+ self.get_table_offset('DT_REL')[1],
+ next(self.iter_tags('DT_RELSZ'))['d_val'], False)
+
+ relentsz = next(self.iter_tags('DT_RELENT'))['d_val']
+ elf_assert(result['REL'].entry_size == relentsz,
+ 'Expected DT_RELENT to be %s' % relentsz)
+
+ if list(self.iter_tags('DT_RELA')):
+ result['RELA'] = RelocationTable(self.elffile,
+ self.get_table_offset('DT_RELA')[1],
+ next(self.iter_tags('DT_RELASZ'))['d_val'], True)
+
+ relentsz = next(self.iter_tags('DT_RELAENT'))['d_val']
+ elf_assert(result['RELA'].entry_size == relentsz,
+ 'Expected DT_RELAENT to be %s' % relentsz)
+
+ if list(self.iter_tags('DT_JMPREL')):
+ result['JMPREL'] = RelocationTable(self.elffile,
+ self.get_table_offset('DT_JMPREL')[1],
+ next(self.iter_tags('DT_PLTRELSZ'))['d_val'],
+ next(self.iter_tags('DT_PLTREL'))['d_val'] == ENUM_D_TAG['DT_RELA'])
+
+ return result
+
class DynamicSection(Section, Dynamic):
""" ELF dynamic table section. Knows how to process the list of tags.
from ..common.utils import elf_assert, struct_parse
from .sections import Section
from .enums import (
- ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_MIPS, ENUM_RELOC_TYPE_ARM)
+ ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_MIPS, ENUM_RELOC_TYPE_ARM, ENUM_D_TAG)
class Relocation(object):
return self.__repr__()
-class RelocationSection(Section):
- """ ELF relocation section. Serves as a collection of Relocation entries.
+class RelocationTable(object):
+ """ Shared functionality between relocation sections and relocation tables
"""
- def __init__(self, header, name, elffile):
- super(RelocationSection, self).__init__(header, name, elffile)
- if self.header['sh_type'] == 'SHT_REL':
- expected_size = self.structs.Elf_Rel.sizeof()
- self.entry_struct = self.structs.Elf_Rel
- elif self.header['sh_type'] == 'SHT_RELA':
- expected_size = self.structs.Elf_Rela.sizeof()
- self.entry_struct = self.structs.Elf_Rela
+
+ def __init__(self, elffile, offset, size, is_rela):
+ self._stream = elffile.stream
+ self._elffile = elffile
+ self._elfstructs = elffile.structs
+ self._size = size
+ self._offset = offset
+ self._is_rela = is_rela
+
+ if is_rela:
+ self.entry_struct = self._elfstructs.Elf_Rela
else:
- elf_assert(False, 'Unknown relocation type section')
+ self.entry_struct = self._elfstructs.Elf_Rel
- elf_assert(
- self.header['sh_entsize'] == expected_size,
- 'Expected sh_entsize of SHT_REL section to be %s' % expected_size)
+ self.entry_size = self.entry_struct.sizeof()
def is_RELA(self):
""" Is this a RELA relocation section? If not, it's REL.
"""
- return self.header['sh_type'] == 'SHT_RELA'
+ return self._is_rela
def num_relocations(self):
""" Number of relocations in the section
"""
- return self['sh_size'] // self['sh_entsize']
+ return self._size // self.entry_size
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_offset = self._offset + n * self.entry_size
entry = struct_parse(
self.entry_struct,
- self.stream,
+ self._stream,
stream_pos=entry_offset)
- return Relocation(entry, self.elffile)
+ return Relocation(entry, self._elffile)
def iter_relocations(self):
""" Yield all the relocations in the section
yield self.get_relocation(i)
+class RelocationSection(Section, RelocationTable):
+ """ ELF relocation section. Serves as a collection of Relocation entries.
+ """
+ def __init__(self, header, name, elffile):
+ Section.__init__(self, header, name, elffile)
+ RelocationTable.__init__(self, self.elffile,
+ self['sh_offset'], self['sh_size'], header['sh_type'] == 'SHT_RELA')
+
+ elf_assert(header['sh_type'] in ('SHT_REL', 'SHT_RELA'),
+ 'Unknown relocation type section')
+ elf_assert(header['sh_entsize'] == self.entry_size,
+ 'Expected sh_entsize of %s section to be %s' % (
+ header['sh_type'], self.entry_size))
+
+
class RelocationHandler(object):
""" Handles the logic of relocations in ELF files.
"""
--- /dev/null
+import os
+import sys
+import unittest
+
+from elftools.common.py3compat import BytesIO
+from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSegment, DynamicSection
+
+
+class TestRelocation(unittest.TestCase):
+ def test_dynamic_segment(self):
+ """Verify that we can process relocations on the PT_DYNAMIC segment without section headers"""
+
+ test_dir = os.path.join('test', 'testfiles_for_unittests')
+ with open(os.path.join(test_dir, 'x64_bad_sections.elf'), 'rb') as f:
+ elff = ELFFile(f)
+
+ for seg in elff.iter_segments():
+ if isinstance(seg, DynamicSegment):
+ relos = seg.get_relocation_tables()
+ self.assertEqual(set(relos), {'JMPREL', 'RELA'})
+
+ def test_dynamic_section(self):
+ """Verify that we can parse relocations from the .dynamic section"""
+
+ test_dir = os.path.join('test', 'testfiles_for_unittests')
+ with open(os.path.join(test_dir, 'sample_exe64.elf'), 'rb') as f:
+ elff = ELFFile(f)
+
+ for sect in elff.iter_sections():
+ if isinstance(sect, DynamicSection):
+ relos = sect.get_relocation_tables()
+ self.assertEqual(set(relos), {'JMPREL', 'RELA'})
+
+ def test_dynamic_section_solaris(self):
+ """Verify that we can parse relocations from the .dynamic section"""
+
+ test_dir = os.path.join('test', 'testfiles_for_unittests')
+ with open(os.path.join(test_dir, 'exe_solaris32_cc.elf'), 'rb') as f:
+ elff = ELFFile(f)
+
+ for sect in elff.iter_sections():
+ if isinstance(sect, DynamicSection):
+ relos = sect.get_relocation_tables()
+ self.assertEqual(set(relos), {'JMPREL', 'REL'})
+
+if __name__ == '__main__':
+ unittest.main()