1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:38:11 +00:00

LibELF: Implement support for DT_RELR relative relocations

The DT_RELR relocation is a relatively new relocation encoding designed
to achieve space-efficient relative relocations in PIE programs.

The description of the format is available here:
https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Pi9aSwwABgAJ

It works by using a bitmap to store the offsets which need to be
relocated. Even entries are *address* entries: they contain an address
(relative to the base of the executable) which needs to be relocated.
Subsequent even entries are *bitmap* entries: "1" bits encode offsets
(in word size increments) relative to the last address entry which need
to be relocated.

This is in contrast to the REL/RELA format, where each entry takes up
2/3 machine words. Certain kinds of relocations store useful data in
that space (like the name of the referenced symbol), so not everything
can be encoded in this format. But as position-independent executables
and shared libraries tend to have a lot of relative relocations, a
specialized encoding for them absolutely makes sense.

The authors of the format suggest an overall 5-20% reduction in the file
size of various programs. Due to our extensive use of dynamic linking
and us not stripping debug info, relative relocations don't make up such
a large portion of the binary's size, so the measurements will tend to
skew to the lower side of the spectrum.

The following measurements were made with the x86-64 Clang toolchain:

- The kernel contains 290989 relocations. Enabling RELR decreased its
  size from 30 MiB to 23 MiB.
- LibUnicodeData contains 190262 relocations, almost all of them
  relative. Its file size changed from 17 MiB to 13 MiB.
- /bin/WebContent contains 1300 relocations, 66% of which are relative
  relocations. With RELR, its size changed from 832 KiB to 812 KiB.

This change was inspired by the following blog post:
https://maskray.me/blog/2021-10-31-relative-relocations-and-relr
This commit is contained in:
Daniel Bertalan 2021-10-28 09:31:51 +02:00 committed by Andreas Kling
parent 32b8795091
commit 3974cac148
6 changed files with 113 additions and 2 deletions

View file

@ -26,6 +26,9 @@ bool perform_relative_relocations(FlatPtr base_address)
FlatPtr relocation_section_addr = 0;
size_t relocation_table_size = 0;
size_t relocation_count = 0;
size_t relocation_entry_size = 0;
FlatPtr relr_relocation_section_addr = 0;
size_t relr_relocation_table_size = 0;
bool use_addend = false;
auto* dyns = reinterpret_cast<const ElfW(Dyn)*>(dynamic_section_addr);
for (unsigned i = 0;; ++i) {
@ -40,11 +43,19 @@ bool perform_relative_relocations(FlatPtr base_address)
relocation_count = dyn.d_un.d_val;
else if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ)
relocation_table_size = dyn.d_un.d_val;
else if (dyn.d_tag == DT_RELENT || dyn.d_tag == DT_RELAENT)
relocation_entry_size = dyn.d_un.d_val;
else if (dyn.d_tag == DT_RELR)
relr_relocation_section_addr = base_address + dyn.d_un.d_ptr;
else if (dyn.d_tag == DT_RELRSZ)
relr_relocation_table_size = dyn.d_un.d_val;
else if (dyn.d_tag == DT_RELRENT)
VERIFY(dyn.d_un.d_val == sizeof(FlatPtr));
}
if (!relocation_section_addr || !relocation_table_size || !relocation_count)
if ((!relocation_section_addr || !relocation_table_size || !relocation_count) && (!relr_relocation_section_addr || !relr_relocation_table_size))
return false;
auto relocation_entry_size = relocation_table_size / relocation_count;
for (unsigned i = 0; i < relocation_count; ++i) {
size_t offset_in_section = i * relocation_entry_size;
auto* relocation = (ElfW(Rela)*)(relocation_section_addr + offset_in_section);
@ -64,6 +75,30 @@ bool perform_relative_relocations(FlatPtr base_address)
__builtin_memcpy(patch_address, &relocated_address, sizeof(relocated_address));
}
auto patch_relr = [base_address](FlatPtr* patch_ptr) {
FlatPtr relocated_address;
__builtin_memcpy(&relocated_address, patch_ptr, sizeof(FlatPtr));
relocated_address += base_address;
__builtin_memcpy(patch_ptr, &relocated_address, sizeof(FlatPtr));
};
auto* entries = reinterpret_cast<ElfW(Relr)*>(relr_relocation_section_addr);
FlatPtr* patch_ptr = nullptr;
for (unsigned i = 0; i < relr_relocation_table_size / sizeof(FlatPtr); ++i) {
if ((entries[i] & 1u) == 0) {
patch_ptr = reinterpret_cast<FlatPtr*>(base_address + entries[i]);
patch_relr(patch_ptr);
++patch_ptr;
} else {
unsigned j = 0;
for (auto bitmap = entries[i]; (bitmap >>= 1u) != 0; ++j)
if (bitmap & 1u)
patch_relr(patch_ptr + j);
patch_ptr += 8 * sizeof(FlatPtr) - 1;
}
}
return true;
}
}