Support getting RELR relocations from dynamic section (#509)
authorJan Janssen <medhefgo@web.de>
Fri, 20 Oct 2023 13:14:33 +0000 (15:14 +0200)
committerGitHub <noreply@github.com>
Fri, 20 Oct 2023 13:14:33 +0000 (06:14 -0700)
elftools/elf/dynamic.py
elftools/elf/relocation.py
test/test_relr.py

index 2f85333e484c0ff339b6d06a69b91bb808417901..d2e44caa951f439844ace38ecce7a2dcfe62af90 100644 (file)
@@ -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],
index 49702f0662bc4a83512d6ff7476f10de2b1170be..ebd399ec3b290548e94544454fb0bf5de2e8e5fd 100644 (file)
@@ -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.
     """
index 69e39d6d1d7022960feec06680c309cceea67353..78a3754dd3c04c1aae0520b8aae0843b8c2021eb 100644 (file)
@@ -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)