summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibELF/Relocation.cpp
diff options
context:
space:
mode:
authorDaniel Bertalan <dani@danielbertalan.dev>2021-10-28 09:31:51 +0200
committerAndreas Kling <kling@serenityos.org>2022-02-11 18:07:53 +0100
commit3974cac148ff12f04fd98fa3a1c4578767988897 (patch)
treed3c262e97055e52a1aba8061402153382c7e2899 /Userland/Libraries/LibELF/Relocation.cpp
parent32b8795091681de7296074fb9a7b4f63a17e475a (diff)
downloadserenity-3974cac148ff12f04fd98fa3a1c4578767988897.zip
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
Diffstat (limited to 'Userland/Libraries/LibELF/Relocation.cpp')
-rw-r--r--Userland/Libraries/LibELF/Relocation.cpp39
1 files changed, 37 insertions, 2 deletions
diff --git a/Userland/Libraries/LibELF/Relocation.cpp b/Userland/Libraries/LibELF/Relocation.cpp
index b3c84e4cbf..30bac88c5f 100644
--- a/Userland/Libraries/LibELF/Relocation.cpp
+++ b/Userland/Libraries/LibELF/Relocation.cpp
@@ -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;
}
}