Skip to content

Commit

Permalink
Support getting RELR relocations from dynamic section
Browse files Browse the repository at this point in the history
  • Loading branch information
medhefgo committed Sep 27, 2023
1 parent 87c2ddd commit 0aca2c5
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 15 deletions.
10 changes: 8 additions & 2 deletions elftools/elf/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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

Expand Down Expand Up @@ -191,7 +191,7 @@ 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.
RELR, JMPREL) to RelocationTable objects.
"""

result = {}
Expand All @@ -214,6 +214,12 @@ def get_relocation_tables(self):
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],
Expand Down
43 changes: 32 additions & 11 deletions elftools/elf/relocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,34 +107,45 @@ def __init__(self, header, name, elffile):
'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
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

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
# value provided in an initial 'anchor' relocation.
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:
Expand All @@ -143,7 +154,7 @@ def iter_relocations(self):
# 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')
Expand All @@ -159,12 +170,12 @@ def iter_relocations(self):
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

Expand All @@ -182,6 +193,16 @@ def get_relocation(self, n):
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.
"""
Expand Down
13 changes: 11 additions & 2 deletions test/test_relr.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def test_num_relocations(self):
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')
Expand All @@ -47,3 +46,13 @@ def test_get_relocation(self):
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)

0 comments on commit 0aca2c5

Please sign in to comment.