summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibC/elf.h9
-rw-r--r--Userland/Libraries/LibELF/DynamicLoader.cpp10
-rw-r--r--Userland/Libraries/LibELF/DynamicLoader.h1
-rw-r--r--Userland/Libraries/LibELF/DynamicObject.cpp20
-rw-r--r--Userland/Libraries/LibELF/DynamicObject.h36
-rw-r--r--Userland/Libraries/LibELF/Relocation.cpp39
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;
}
}