From b0d7a76a6c75343aad32c3861e29c80556dbd961 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Oct 2023 15:14:33 +0200 Subject: [PATCH] Support getting RELR relocations from dynamic section (#509) --- elftools/elf/dynamic.py | 10 +++++++-- elftools/elf/relocation.py | 43 ++++++++++++++++++++++++++++---------- test/test_relr.py | 13 ++++++++++-- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py index 2f85333..d2e44ca 100644 --- a/elftools/elf/dynamic.py +++ b/elftools/elf/dynamic.py @@ -13,7 +13,7 @@ from .hash import ELFHashTable, GNUHashTable from .sections import Section, Symbol from .enums import ENUM_D_TAG from .segments import Segment -from .relocation import RelocationTable +from .relocation import RelocationTable, RelrRelocationTable from ..common.exceptions import ELFError from ..common.utils import elf_assert, struct_parse, parse_cstring_from_stream @@ -191,7 +191,7 @@ class Dynamic(object): """ Load all available relocation tables from DYNAMIC tags. Returns a dictionary mapping found table types (REL, RELA, - JMPREL) to RelocationTable objects. + RELR, JMPREL) to RelocationTable objects. """ result = {} @@ -214,6 +214,12 @@ class Dynamic(object): elf_assert(result['RELA'].entry_size == relentsz, 'Expected DT_RELAENT to be %s' % relentsz) + if list(self.iter_tags('DT_RELR')): + result['RELR'] = RelrRelocationTable(self.elffile, + self.get_table_offset('DT_RELR')[1], + next(self.iter_tags('DT_RELRSZ'))['d_val'], + next(self.iter_tags('DT_RELRENT'))['d_val']) + if list(self.iter_tags('DT_JMPREL')): result['JMPREL'] = RelocationTable(self.elffile, self.get_table_offset('DT_JMPREL')[1], diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py index 49702f0..ebd399e 100644 --- a/elftools/elf/relocation.py +++ b/elftools/elf/relocation.py @@ -107,8 +107,9 @@ class RelocationSection(Section, RelocationTable): 'Expected sh_entsize of %s section to be %s' % ( header['sh_type'], self.entry_size)) -class RelrRelocationSection(Section): - """ RELR compressed relocation section. This stores relative relocations + +class RelrRelocationTable(object): + """ RELR compressed relocation table. This stores relative relocations in a compressed format. An entry with an even value serves as an 'anchor' that defines a base address. Following this entry are one or more bitmaps for consecutive addresses after the anchor which determine @@ -116,17 +117,27 @@ class RelrRelocationSection(Section): skipped. Addends are stored at the respective addresses (as in REL relocations). """ - def __init__(self, header, name, elffile): - Section.__init__(self, header, name, elffile) - self._offset = self['sh_offset'] - self._size = self['sh_size'] - self._relr_struct = self.elffile.structs.Elf_Relr + + def __init__(self, elffile, offset, size, entrysize): + self._elffile = elffile + self._offset = offset + self._size = size + self._relr_struct = self._elffile.structs.Elf_Relr self._entrysize = self._relr_struct.sizeof() self._cached_relocations = None + elf_assert(self._entrysize == entrysize, + 'Expected RELR entry size to be %s, got %s' % ( + self._entrysize, entrysize)) + def iter_relocations(self): """ Yield all the relocations in the section """ + + # If DT_RELRSZ is zero, offset is meaningless and could be None. + if self._size == 0: + return [] + limit = self._offset + self._size relr = self._offset # The addresses of relocations in a bitmap are calculated from a base @@ -134,7 +145,7 @@ class RelrRelocationSection(Section): base = None while relr < limit: entry = struct_parse(self._relr_struct, - self.elffile.stream, + self._elffile.stream, stream_pos=relr) entry_offset = entry['r_offset'] if (entry_offset & 1) == 0: @@ -143,7 +154,7 @@ class RelrRelocationSection(Section): # beginning of the first bitmap. base = entry_offset base += self._entrysize - yield Relocation(entry, self.elffile) + yield Relocation(entry, self._elffile) else: # We're processing a bitmap. elf_assert(base is not None, 'RELR bitmap without base address') @@ -159,12 +170,12 @@ class RelrRelocationSection(Section): if (entry_offset & 1) != 0: calc_offset = base + i * self._entrysize yield Relocation(Container(r_offset = calc_offset), - self.elffile) + self._elffile) i += 1 # Advance 'base' past the current bitmap (8 == CHAR_BIT). There # are 63 (or 31 for 32-bit ELFs) entries in each bitmap, and # every bit corresponds to an ELF_addr-sized relocation. - base += (8 * self._entrysize - 1) * self.elffile.structs.Elf_addr('').sizeof() + base += (8 * self._entrysize - 1) * self._elffile.structs.Elf_addr('').sizeof() # Advance to the next entry relr += self._entrysize @@ -182,6 +193,16 @@ class RelrRelocationSection(Section): self._cached_relocations = list(self.iter_relocations()) return self._cached_relocations[n] + +class RelrRelocationSection(Section, RelrRelocationTable): + """ ELF RELR relocation section. Serves as a collection of RELR relocation entries. + """ + def __init__(self, header, name, elffile): + Section.__init__(self, header, name, elffile) + RelrRelocationTable.__init__(self, self.elffile, + self['sh_offset'], self['sh_size'], self['sh_entsize']) + + class RelocationHandler(object): """ Handles the logic of relocations in ELF files. """ diff --git a/test/test_relr.py b/test/test_relr.py index 69e39d6..78a3754 100644 --- a/test/test_relr.py +++ b/test/test_relr.py @@ -34,8 +34,7 @@ class TestRelr(unittest.TestCase): self.assertEqual(relr_section.num_relocations(), 100) def test_get_relocation(self): - """ Verify we can get a specific relocation in a RELR relocation - section. + """ Verify we can get a specific RELR relocation. """ path = os.path.join('test', 'testfiles_for_unittests', 'lib_relro.so.elf') @@ -47,3 +46,13 @@ class TestRelr(unittest.TestCase): self.assertEqual(reloc['r_offset'], 0x4540) reloc = relr_section.get_relocation(n=65) self.assertEqual(reloc['r_offset'], 0x4748) + + dynamic_section = elf.get_section_by_name('.dynamic') + self.assertIsNotNone(dynamic_section) + dynamic_relr = dynamic_section.get_relocation_tables() + self.assertIsNotNone(dynamic_relr) + self.assertIsNotNone(dynamic_relr['RELR']) + reloc = dynamic_relr['RELR'].get_relocation(n=0) + self.assertEqual(reloc['r_offset'], 0x4540) + reloc = dynamic_relr['RELR'].get_relocation(n=65) + self.assertEqual(reloc['r_offset'], 0x4748) -- 2.30.2