diff options
Diffstat (limited to 'Userland/Libraries/LibELF/Relocation.cpp')
-rw-r--r-- | Userland/Libraries/LibELF/Relocation.cpp | 39 |
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; } } |