diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibC/elf.h | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibELF/DynamicLoader.cpp | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibELF/DynamicLoader.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibELF/DynamicObject.cpp | 20 | ||||
-rw-r--r-- | Userland/Libraries/LibELF/DynamicObject.h | 36 | ||||
-rw-r--r-- | Userland/Libraries/LibELF/Relocation.cpp | 39 |
6 files changed, 113 insertions, 2 deletions
diff --git a/Userland/Libraries/LibC/elf.h b/Userland/Libraries/LibC/elf.h index abb21e4536..01c497259e 100644 --- a/Userland/Libraries/LibC/elf.h +++ b/Userland/Libraries/LibC/elf.h @@ -277,6 +277,7 @@ typedef struct { #define SHT_PREINIT_ARRAY 16 /* ptrs to funcs called before init */ #define SHT_GROUP 17 /* defines a section group */ #define SHT_SYMTAB_SHNDX 18 /* Section indices (see SHN_XINDEX). */ +#define SHT_RELR 19 /* relative-only relocation section */ #define SHT_LOOS 0x60000000 /* reserved range for OS specific */ #define SHT_SUNW_dof 0x6ffffff4 /* used by dtrace */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* libraries to be prelinked */ @@ -440,6 +441,9 @@ typedef struct { # define ELF64_R_INFO(s, t) (((uint64_t)swap32(t) << 32) + (uint32_t)(s)) #endif /* __mips64__ && __MIPSEL__ */ +typedef Elf32_Word Elf32_Relr; +typedef Elf64_Xword Elf64_Relr; + /* Program Header */ typedef struct { Elf32_Word p_type; /* segment type */ @@ -545,6 +549,9 @@ typedef struct { #define DT_ENCODING 31 /* further DT_* follow encoding rules */ #define DT_PREINIT_ARRAY 32 /* address of array of preinit func */ #define DT_PREINIT_ARRAYSZ 33 /* size of array of preinit func */ +#define DT_RELRSZ 35 /* size of DT_RELR relocation table */ +#define DT_RELR 36 /* addr of DT_RELR relocation table */ +#define DT_RELRENT 37 /* size of DT_RELR relocation entry */ #define DT_LOOS 0x6000000d /* reserved range for OS */ #define DT_HIOS 0x6ffff000 /* specific dynamic array tags */ #define DT_LOPROC 0x70000000 /* reserved range for processor */ @@ -740,6 +747,7 @@ struct elf_args { # define Elf_Sym Elf32_Sym # define Elf_Rel Elf32_Rel # define Elf_RelA Elf32_Rela +# define Elf_Relr Elf32_Relr # define Elf_Dyn Elf32_Dyn # define Elf_Half Elf32_Half # define Elf_Word Elf32_Word @@ -767,6 +775,7 @@ struct elf_args { # define Elf_Sym Elf64_Sym # define Elf_Rel Elf64_Rel # define Elf_RelA Elf64_Rela +# define Elf_Relr Elf64_Relr # define Elf_Dyn Elf64_Dyn # define Elf_Half Elf64_Half # define Elf_Word Elf64_Word diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 2e678dc5a1..793ffb4e4d 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -198,8 +198,10 @@ void DynamicLoader::do_main_relocations() break; } }; + m_dynamic_object->relocation_section().for_each_relocation(do_single_relocation); m_dynamic_object->plt_relocation_section().for_each_relocation(do_single_relocation); + do_relr_relocations(); } Result<NonnullRefPtr<DynamicObject>, DlErrorMessage> DynamicLoader::load_stage_3(unsigned flags) @@ -537,6 +539,14 @@ DynamicLoader::RelocationResult DynamicLoader::do_relocation(const ELF::DynamicO return RelocationResult::Success; } +void DynamicLoader::do_relr_relocations() +{ + auto base_address = m_dynamic_object->base_address().get(); + m_dynamic_object->for_each_relr_relocation([base_address](FlatPtr address) { + *(FlatPtr*)address += base_address; + }); +} + ssize_t DynamicLoader::negative_offset_from_tls_block_end(ssize_t tls_offset, size_t value_of_symbol) const { ssize_t offset = static_cast<ssize_t>(tls_offset + value_of_symbol); diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h index 093d555c23..d7c0c71e75 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.h +++ b/Userland/Libraries/LibELF/DynamicLoader.h @@ -130,6 +130,7 @@ private: ResolveLater = 2, }; RelocationResult do_relocation(const DynamicObject::Relocation&, ShouldInitializeWeak should_initialize_weak); + void do_relr_relocations(); size_t calculate_tls_size() const; ssize_t negative_offset_from_tls_block_end(ssize_t tls_offset, size_t value_of_symbol) const; diff --git a/Userland/Libraries/LibELF/DynamicObject.cpp b/Userland/Libraries/LibELF/DynamicObject.cpp index a9e26fc0d7..f56b1b6aea 100644 --- a/Userland/Libraries/LibELF/DynamicObject.cpp +++ b/Userland/Libraries/LibELF/DynamicObject.cpp @@ -135,6 +135,15 @@ void DynamicObject::parse() case DT_RELCOUNT: m_number_of_relocations = entry.val(); break; + case DT_RELR: + m_relr_relocation_table_offset = entry.ptr() - m_elf_base_address.get(); + break; + case DT_RELRSZ: + m_size_of_relr_relocation_table = entry.val(); + break; + case DT_RELRENT: + m_size_of_relr_relocations_entry = entry.val(); + break; case DT_FLAGS: m_dt_flags = entry.val(); break; @@ -236,6 +245,11 @@ DynamicObject::RelocationSection DynamicObject::plt_relocation_section() const return RelocationSection(Section(*this, m_plt_relocation_offset_location, m_size_of_plt_relocation_entry_list, m_size_of_relocation_entry, "DT_JMPREL"sv), false); } +DynamicObject::Section DynamicObject::relr_relocation_section() const +{ + return Section(*this, m_relr_relocation_table_offset, m_size_of_relr_relocation_table, m_size_of_relr_relocations_entry, "DT_RELR"sv); +} + ElfW(Half) DynamicObject::program_header_count() const { auto* header = (const ElfW(Ehdr)*)m_base_address.as_ptr(); @@ -431,6 +445,12 @@ const char* DynamicObject::name_for_dtag(ElfW(Sword) d_tag) return "VERNEEDED"; case DT_VERNEEDEDNUM: return "VERNEEDEDNUM"; + case DT_RELR: + return "DT_RELR"; + case DT_RELRSZ: + return "DT_RELRSZ"; + case DT_RELRENT: + return "DT_RELRENT"; default: return "??"; } diff --git a/Userland/Libraries/LibELF/DynamicObject.h b/Userland/Libraries/LibELF/DynamicObject.h index bc1b3a7a57..24fb27d785 100644 --- a/Userland/Libraries/LibELF/DynamicObject.h +++ b/Userland/Libraries/LibELF/DynamicObject.h @@ -272,6 +272,7 @@ public: RelocationSection relocation_section() const; RelocationSection plt_relocation_section() const; + Section relr_relocation_section() const; bool should_process_origin() const { return m_dt_flags & DF_ORIGIN; } bool requires_symbolic_symbol_resolution() const { return m_dt_flags & DF_SYMBOLIC; } @@ -312,6 +313,9 @@ public: template<VoidFunction<Symbol&> F> void for_each_symbol(F) const; + template<typename F> + void for_each_relr_relocation(F) const; + struct SymbolLookupResult { FlatPtr value { 0 }; size_t size { 0 }; @@ -374,6 +378,9 @@ private: size_t m_size_of_relocation_table { 0 }; bool m_addend_used { false }; FlatPtr m_relocation_table_offset { 0 }; + size_t m_size_of_relr_relocations_entry { 0 }; + size_t m_size_of_relr_relocation_table { 0 }; + FlatPtr m_relr_relocation_table_offset { 0 }; bool m_is_elf_dynamic { false }; // DT_FLAGS @@ -412,6 +419,35 @@ inline void DynamicObject::RelocationSection::for_each_relocation(F func) const }); } +template<typename F> +inline void DynamicObject::for_each_relr_relocation(F f) const +{ + auto section = relr_relocation_section(); + if (section.entry_count() == 0) + return; + + VERIFY(section.entry_size() == sizeof(FlatPtr)); + VERIFY(section.size() >= section.entry_size() * section.entry_count()); + + auto* entries = reinterpret_cast<ElfW(Relr)*>(section.address().get()); + auto base = base_address().get(); + FlatPtr patch_addr = 0; + for (unsigned i = 0; i < section.entry_count(); ++i) { + if ((entries[i] & 1u) == 0) { + patch_addr = base + entries[i]; + f(patch_addr); + patch_addr += sizeof(FlatPtr); + } else { + unsigned j = 0; + for (auto bitmap = entries[i]; (bitmap >>= 1u) != 0; ++j) + if (bitmap & 1u) + f(patch_addr + j * sizeof(FlatPtr)); + + patch_addr += (8 * sizeof(FlatPtr) - 1) * sizeof(FlatPtr); + } + } +} + template<VoidFunction<DynamicObject::Symbol&> F> inline void DynamicObject::for_each_symbol(F func) const { 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; } } |