SymbolTableIndexSection, SUNWSyminfoTableSection, NullSection,
NoteSection, StabSection, ARMAttributesSection)
from .dynamic import DynamicSection, DynamicSegment
-from .relocation import RelocationSection, RelocationHandler
+from .relocation import (RelocationSection, RelocationHandler,
+ RelrRelocationSection)
from .gnuversions import (
GNUVerNeedSection, GNUVerDefSection,
GNUVerSymSection)
return self._make_elf_hash_section(section_header, name)
elif sectype == 'SHT_GNU_HASH':
return self._make_gnu_hash_section(section_header, name)
+ elif sectype == 'SHT_RELR':
+ return RelrRelocationSection(section_header, name, self)
else:
return Section(section_header, name, self)
ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_MIPS,
ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_PPC64,
ENUM_D_TAG)
+from ..construct import Container
class Relocation(object):
'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
+ 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
+ if the corresponding relocation exists (if the bit is 1) or if it is
+ 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
+ self._entrysize = self._relr_struct.sizeof()
+ self._cached_relocations = None
+
+ def iter_relocations(self):
+ """ Yield all the relocations in the section
+ """
+ limit = self._offset + self._size
+ relr = self._offset
+ # The addresses of relocations in a bitmap are calculated from a base
+ # value provided in an initial 'anchor' relocation.
+ base = None
+ while relr < limit:
+ entry = struct_parse(self._relr_struct,
+ self.elffile.stream,
+ stream_pos=relr)
+ entry_offset = entry['r_offset']
+ if (entry_offset & 1) == 0:
+ # We found an anchor, take the current value as the base address
+ # for the following bitmaps and move the 'where' pointer to the
+ # beginning of the first bitmap.
+ base = entry_offset
+ base += self._entrysize
+ yield Relocation(entry, self.elffile)
+ else:
+ # We're processing a bitmap.
+ elf_assert(base is not None, 'RELR bitmap without base address')
+ i = 0
+ while True:
+ # Iterate over all bits except the least significant one.
+ entry_offset = (entry_offset >> 1)
+ if entry_offset == 0:
+ break
+ # if the current LSB is set, we have a relocation at the
+ # corresponding address so generate a Relocation with the
+ # matching offset
+ if (entry_offset & 1) != 0:
+ calc_offset = base + i * self._entrysize
+ yield Relocation(Container(r_offset = calc_offset),
+ 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()
+ # Advance to the next entry
+ relr += self._entrysize
+
+ def num_relocations(self):
+ """ Number of relocations in the section
+ """
+ if self._cached_relocations is None:
+ self._cached_relocations = list(self.iter_relocations())
+ return len(self._cached_relocations)
+
+ def get_relocation(self, n):
+ """ Get the relocation at index #n from the section (Relocation object)
+ """
+ if self._cached_relocations is None:
+ self._cached_relocations = list(self.iter_relocations())
+ return self._cached_relocations[n]
class RelocationHandler(object):
""" Handles the logic of relocations in ELF files.
--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Andreas Ziegler (andreas.ziegler@fau.de)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+# The lib_relro.so.elf file was generated as follows (on Debian 11):
+# $ cat lib_relro.c
+# int retfunc(){ return 1; }
+# int (*ptr1)() = retfunc;
+# int (*ptr2)() = retfunc;
+# <...>
+# int (*ptr100)() = retfunc;
+# $ clang-12 -c -o lib_relro.o -fPIC lib_relro.c
+# $ ld.lld-12 -o lib_relro.so.elf --pack-dyn-relocs=relr --shared -Bsymbolic-functions lib_relro.o
+
+import unittest
+import os
+
+from elftools.elf.elffile import ELFFile
+
+class TestRelr(unittest.TestCase):
+
+ def test_num_relocations(self):
+ """ Verify we can get the number of relocations in a RELR relocation
+ section.
+ """
+ path = os.path.join('test', 'testfiles_for_unittests',
+ 'lib_relro.so.elf')
+ with open(path, 'rb') as f:
+ elf = ELFFile(f)
+ relr_section = elf.get_section_by_name('.relr.dyn')
+ self.assertIsNotNone(relr_section)
+ self.assertEqual(relr_section.num_relocations(), 100)
+
+ def test_get_relocation(self):
+ """ Verify we can get a specific relocation in a RELR relocation
+ section.
+ """
+ path = os.path.join('test', 'testfiles_for_unittests',
+ 'lib_relro.so.elf')
+ with open(path, 'rb') as f:
+ elf = ELFFile(f)
+ relr_section = elf.get_section_by_name('.relr.dyn')
+ self.assertIsNotNone(relr_section)
+ reloc = relr_section.get_relocation(n=0)
+ self.assertEqual(reloc['r_offset'], 0x4540)
+ reloc = relr_section.get_relocation(n=65)
+ self.assertEqual(reloc['r_offset'], 0x4748)