From 101ac45c1a1e0bcfe67c95c16590dbe970b24abc Mon Sep 17 00:00:00 2001 From: Itamar Date: Fri, 30 Apr 2021 13:31:42 +0300 Subject: [PATCH] LibELF: Change TLS offset calculation This changes the TLS offset calculation logic to be based on the symbol's size instead of the total size of the TLS. Because of this change, we no longer need to pipe "m_tls_size" to so many functions. Also, After this patch, the TLS data of the main program exists at the "end" of the TLS block (Highest addresses). This fixes a part of #6609. --- Userland/Libraries/LibELF/DynamicLinker.cpp | 6 +-- Userland/Libraries/LibELF/DynamicLoader.cpp | 44 +++++++++++---------- Userland/Libraries/LibELF/DynamicLoader.h | 16 ++++---- Userland/Libraries/LibELF/DynamicObject.cpp | 2 +- Userland/Libraries/LibELF/DynamicObject.h | 1 + 5 files changed, 36 insertions(+), 33 deletions(-) diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 5b5126b0df..ba6d1dbccd 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -170,7 +170,7 @@ static void allocate_tls() // Initialize TLS data for (const auto& entry : s_loaders) { - entry.value->copy_initial_tls_data_into(initial_tls_data, s_total_tls_size); + entry.value->copy_initial_tls_data_into(initial_tls_data); } void* master_tls = ::allocate_tls((char*)initial_tls_data.data(), initial_tls_data.size()); @@ -282,14 +282,14 @@ static Result, DlErrorMessage> load_main_library(co } for (auto& loader : loaders) { - bool success = loader.link(flags, s_total_tls_size); + bool success = loader.link(flags); if (!success) { return DlErrorMessage { String::formatted("Failed to link library {}", loader.filename()) }; } } for (auto& loader : loaders) { - auto result = loader.load_stage_3(flags, s_total_tls_size); + auto result = loader.load_stage_3(flags); VERIFY(!result.is_error()); auto& object = result.value(); diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 6b5225393a..5b7573d260 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -146,12 +146,12 @@ RefPtr DynamicLoader::map() return m_dynamic_object; } -bool DynamicLoader::link(unsigned flags, size_t total_tls_size) +bool DynamicLoader::link(unsigned flags) { - return load_stage_2(flags, total_tls_size); + return load_stage_2(flags); } -bool DynamicLoader::load_stage_2(unsigned flags, size_t total_tls_size) +bool DynamicLoader::load_stage_2(unsigned flags) { VERIFY(flags & RTLD_GLOBAL); @@ -173,14 +173,14 @@ bool DynamicLoader::load_stage_2(unsigned flags, size_t total_tls_size) } } } - do_main_relocations(total_tls_size); + do_main_relocations(); return true; } -void DynamicLoader::do_main_relocations(size_t total_tls_size) +void DynamicLoader::do_main_relocations() { auto do_single_relocation = [&](const ELF::DynamicObject::Relocation& relocation) { - switch (do_relocation(total_tls_size, relocation, ShouldInitializeWeak::No)) { + switch (do_relocation(relocation, ShouldInitializeWeak::No)) { case RelocationResult::Failed: dbgln("Loader.so: {} unresolved symbol '{}'", m_filename, relocation.symbol().name()); VERIFY_NOT_REACHED(); @@ -196,9 +196,9 @@ void DynamicLoader::do_main_relocations(size_t total_tls_size) m_dynamic_object->plt_relocation_section().for_each_relocation(do_single_relocation); } -Result, DlErrorMessage> DynamicLoader::load_stage_3(unsigned flags, size_t total_tls_size) +Result, DlErrorMessage> DynamicLoader::load_stage_3(unsigned flags) { - do_lazy_relocations(total_tls_size); + do_lazy_relocations(); if (flags & RTLD_LAZY) { if (m_dynamic_object->has_plt()) setup_plt_trampoline(); @@ -230,10 +230,10 @@ void DynamicLoader::load_stage_4() call_object_init_functions(); } -void DynamicLoader::do_lazy_relocations(size_t total_tls_size) +void DynamicLoader::do_lazy_relocations() { for (const auto& relocation : m_unresolved_relocations) { - if (auto res = do_relocation(total_tls_size, relocation, ShouldInitializeWeak::Yes); res != RelocationResult::Success) { + if (auto res = do_relocation(relocation, ShouldInitializeWeak::Yes); res != RelocationResult::Success) { dbgln("Loader.so: {} unresolved symbol '{}'", m_filename, relocation.symbol().name()); VERIFY_NOT_REACHED(); } @@ -389,7 +389,7 @@ void DynamicLoader::load_program_headers() // FIXME: Initialize the values in the TLS section. Currently, it is zeroed. } -DynamicLoader::RelocationResult DynamicLoader::do_relocation(size_t total_tls_size, const ELF::DynamicObject::Relocation& relocation, ShouldInitializeWeak should_initialize_weak) +DynamicLoader::RelocationResult DynamicLoader::do_relocation(const ELF::DynamicObject::Relocation& relocation, ShouldInitializeWeak should_initialize_weak) { FlatPtr* patch_ptr = nullptr; if (is_dynamic()) @@ -462,7 +462,7 @@ DynamicLoader::RelocationResult DynamicLoader::do_relocation(size_t total_tls_si break; auto* dynamic_object_of_symbol = res.value().dynamic_object; VERIFY(dynamic_object_of_symbol); - *patch_ptr = negative_offset_from_tls_block_end(res.value().value, dynamic_object_of_symbol->tls_offset().value(), total_tls_size); + *patch_ptr = negative_offset_from_tls_block_end(res.value().value, dynamic_object_of_symbol->tls_offset().value(), res.value().size); break; } case R_386_JMP_SLOT: { @@ -487,15 +487,16 @@ DynamicLoader::RelocationResult DynamicLoader::do_relocation(size_t total_tls_si return RelocationResult::Success; } -ssize_t DynamicLoader::negative_offset_from_tls_block_end(size_t value_of_symbol, size_t tls_offset, size_t total_tls_size) const +ssize_t DynamicLoader::negative_offset_from_tls_block_end(size_t value_of_symbol, size_t tls_offset, size_t symbol_size) const { - auto negative_offset = static_cast(tls_offset + value_of_symbol - total_tls_size); - // Offset has to be strictly negative. Otherwise we'd collide with the thread's ThreadSpecificData structure. - VERIFY(negative_offset < 0); - return negative_offset; + VERIFY(symbol_size > 0); + ssize_t offset = -static_cast(value_of_symbol + tls_offset + symbol_size); + // At offset 0 there's the thread's ThreadSpecificData structure, we don't want to collide with it. + VERIFY(offset < 0); + return offset; } -void DynamicLoader::copy_initial_tls_data_into(ByteBuffer& buffer, size_t total_tls_size) const +void DynamicLoader::copy_initial_tls_data_into(ByteBuffer& buffer) const { const u8* tls_data = nullptr; size_t tls_size_in_image = 0; @@ -512,11 +513,11 @@ void DynamicLoader::copy_initial_tls_data_into(ByteBuffer& buffer, size_t total_ if (!tls_data || !tls_size_in_image) return; - m_elf_image.for_each_symbol([this, &buffer, total_tls_size, tls_data](ELF::Image::Symbol symbol) { + m_elf_image.for_each_symbol([this, &buffer, tls_data](ELF::Image::Symbol symbol) { if (symbol.type() != STT_TLS) return IterationDecision::Continue; - ssize_t negative_offset = negative_offset_from_tls_block_end(symbol.value(), m_tls_offset, total_tls_size); + ssize_t negative_offset = negative_offset_from_tls_block_end(symbol.value(), m_tls_offset, symbol.size()); VERIFY(symbol.size() != 0); VERIFY(buffer.size() + negative_offset + symbol.size() <= buffer.size()); memcpy(buffer.data() + buffer.size() + negative_offset, tls_data + symbol.value(), symbol.size()); @@ -576,7 +577,8 @@ Optional DynamicLoader::lookup_symbol(const E { if (symbol.is_undefined() || symbol.bind() == STB_WEAK) return DynamicLinker::lookup_global_symbol(symbol.name()); - return DynamicObject::SymbolLookupResult { symbol.value(), symbol.address(), symbol.bind(), &symbol.object() }; + + return DynamicObject::SymbolLookupResult { symbol.value(), symbol.size(), symbol.address(), symbol.bind(), &symbol.object() }; } } // end namespace ELF diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h index 0cccd48791..bfcd2184bb 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.h +++ b/Userland/Libraries/LibELF/DynamicLoader.h @@ -54,13 +54,13 @@ public: // Note that the DynamicObject will not be linked yet. Callers are responsible for calling link() to finish it. RefPtr map(); - bool link(unsigned flags, size_t total_tls_size); + bool link(unsigned flags); // Stage 2 of loading: dynamic object loading and primary relocations - bool load_stage_2(unsigned flags, size_t total_tls_size); + bool load_stage_2(unsigned flags); // Stage 3 of loading: lazy relocations - Result, DlErrorMessage> load_stage_3(unsigned flags, size_t total_tls_size); + Result, DlErrorMessage> load_stage_3(unsigned flags); // Stage 4 of loading: initializers void load_stage_4(); @@ -78,7 +78,7 @@ public: bool is_dynamic() const { return m_elf_image.is_dynamic(); } static Optional lookup_symbol(const ELF::DynamicObject::Symbol&); - void copy_initial_tls_data_into(ByteBuffer& buffer, size_t total_tls_size) const; + void copy_initial_tls_data_into(ByteBuffer& buffer) const; private: DynamicLoader(int fd, String filename, void* file_data, size_t file_size); @@ -113,10 +113,10 @@ private: void load_program_headers(); // Stage 2 - void do_main_relocations(size_t total_tls_size); + void do_main_relocations(); // Stage 3 - void do_lazy_relocations(size_t total_tls_size); + void do_lazy_relocations(); void setup_plt_trampoline(); // Stage 4 @@ -129,9 +129,9 @@ private: Success = 1, ResolveLater = 2, }; - RelocationResult do_relocation(size_t total_tls_size, const DynamicObject::Relocation&, ShouldInitializeWeak should_initialize_weak); + RelocationResult do_relocation(const DynamicObject::Relocation&, ShouldInitializeWeak should_initialize_weak); size_t calculate_tls_size() const; - ssize_t negative_offset_from_tls_block_end(size_t value_of_symbol, size_t tls_offset, size_t total_tls_size) const; + ssize_t negative_offset_from_tls_block_end(size_t value_of_symbol, size_t tls_offset, size_t symbol_size) const; String m_filename; String m_program_interpreter; diff --git a/Userland/Libraries/LibELF/DynamicObject.cpp b/Userland/Libraries/LibELF/DynamicObject.cpp index d552357d24..d4e0b842f6 100644 --- a/Userland/Libraries/LibELF/DynamicObject.cpp +++ b/Userland/Libraries/LibELF/DynamicObject.cpp @@ -440,7 +440,7 @@ auto DynamicObject::lookup_symbol(const HashSymbol& symbol) const -> Optional DynamicObject::create(const String& filename, VirtualAddress base_address, VirtualAddress dynamic_section_address) diff --git a/Userland/Libraries/LibELF/DynamicObject.h b/Userland/Libraries/LibELF/DynamicObject.h index 98c59f713e..06d30b87b9 100644 --- a/Userland/Libraries/LibELF/DynamicObject.h +++ b/Userland/Libraries/LibELF/DynamicObject.h @@ -264,6 +264,7 @@ public: struct SymbolLookupResult { FlatPtr value { 0 }; + size_t size { 0 }; VirtualAddress address; unsigned bind { STB_LOCAL }; const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined