From: Robert Xiao Date: Sat, 16 Mar 2019 13:48:47 +0000 (-0700) Subject: Enable parsing of relocations pointed to by DYNAMIC. (#135) X-Git-Tag: v0.26~21 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=430e3a550f9c7b64b4b3630d319442aa79e3db19;p=pyelftools.git Enable parsing of relocations pointed to by DYNAMIC. (#135) --- diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py index e75c16e..202a1f4 100644 --- a/elftools/elf/dynamic.py +++ b/elftools/elf/dynamic.py @@ -9,9 +9,11 @@ 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): @@ -160,6 +162,41 @@ class Dynamic(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. diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py index e245df4..be03165 100644 --- a/elftools/elf/relocation.py +++ b/elftools/elf/relocation.py @@ -12,7 +12,7 @@ from ..common.exceptions import ELFRelocationError 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): @@ -44,43 +44,44 @@ 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 @@ -89,6 +90,21 @@ class RelocationSection(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. """ diff --git a/test/test_relocations.py b/test/test_relocations.py new file mode 100644 index 0000000..f1c8f10 --- /dev/null +++ b/test/test_relocations.py @@ -0,0 +1,48 @@ +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() diff --git a/test/testfiles_for_unittests/x64_bad_sections.elf b/test/testfiles_for_unittests/x64_bad_sections.elf new file mode 100644 index 0000000..6a30111 Binary files /dev/null and b/test/testfiles_for_unittests/x64_bad_sections.elf differ