From 753844ec9635d4e428253fb3a788b08b6590c5b1 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Tue, 5 Jul 2022 01:18:40 +0300 Subject: [PATCH] LibELF: Take TLS segment alignment into account in DynamicLoader Previously we would just tightly pack the different libraries' TLS segments together, but that is incorrect, as they might require some kind of minimum alignment for their TLS base address. We now plumb the required TLS segment alignment down to the TLS block linear allocator and align the base address down to the appropriate alignment. --- Userland/Libraries/LibELF/DynamicLinker.cpp | 12 +++++++----- Userland/Libraries/LibELF/DynamicLoader.cpp | 15 +++++++++------ Userland/Libraries/LibELF/DynamicLoader.h | 4 +++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 84ffeef4cd..3ed092d2a0 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -96,6 +96,8 @@ static Result, DlErrorMessage> map_library(String c s_loaders.set(get_library_name(filename), *loader); s_current_tls_offset -= loader->tls_size_of_current_object(); + if (loader->tls_alignment_of_current_object()) + s_current_tls_offset = align_down_to(s_current_tls_offset, loader->tls_alignment_of_current_object()); loader->set_tls_offset(s_current_tls_offset); // This actually maps the library at the intended and final place. @@ -196,8 +198,8 @@ static void allocate_tls() { s_total_tls_size = 0; for (auto const& data : s_loaders) { - dbgln_if(DYNAMIC_LOAD_DEBUG, "{}: TLS Size: {}", data.key, data.value->tls_size_of_current_object()); - s_total_tls_size += data.value->tls_size_of_current_object(); + dbgln_if(DYNAMIC_LOAD_DEBUG, "{}: TLS Size: {}, TLS Alignment: {}", data.key, data.value->tls_size_of_current_object(), data.value->tls_alignment_of_current_object()); + s_total_tls_size += data.value->tls_size_of_current_object() + data.value->tls_alignment_of_current_object(); } if (!s_total_tls_size) @@ -393,7 +395,7 @@ static Optional verify_tls_for_dlopen(DynamicLoader const& loade if (loader.tls_size_of_current_object() == 0) return {}; - if (s_total_tls_size + loader.tls_size_of_current_object() > s_allocated_tls_block_size) + if (s_total_tls_size + loader.tls_size_of_current_object() + loader.tls_alignment_of_current_object() > s_allocated_tls_block_size) return DlErrorMessage("TLS size too large"); bool tls_data_is_all_zero = true; @@ -461,7 +463,7 @@ static Result __dlopen(char const* filename, int flags) if (result.is_error()) return result.error(); - s_total_tls_size += result1.value()->tls_size_of_current_object(); + s_total_tls_size += result1.value()->tls_size_of_current_object() + result1.value()->tls_alignment_of_current_object(); auto object = s_global_objects.get(library_name); if (!object.has_value()) @@ -586,7 +588,7 @@ void ELF::DynamicLinker::linker_main(String&& main_program_name, int main_progra dbgln_if(DYNAMIC_LOAD_DEBUG, "loaded all dependencies"); for ([[maybe_unused]] auto& lib : s_loaders) { - dbgln_if(DYNAMIC_LOAD_DEBUG, "{} - tls size: {}, tls offset: {}", lib.key, lib.value->tls_size_of_current_object(), lib.value->tls_offset()); + dbgln_if(DYNAMIC_LOAD_DEBUG, "{} - tls size: {}, tls alignment: {}, tls offset: {}", lib.key, lib.value->tls_size_of_current_object(), lib.value->tls_alignment_of_current_object(), lib.value->tls_offset()); } allocate_tls(); diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 0d815c7124..7925b0dc66 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -71,7 +71,7 @@ DynamicLoader::DynamicLoader(int fd, String filename, void* data, size_t size, S m_elf_image = adopt_own(*new ELF::Image((u8*)m_file_data, m_file_size)); m_valid = validate(); if (m_valid) - m_tls_size_of_current_object = calculate_tls_size(); + find_tls_size_and_alignment(); else dbgln("Image validation failed for file {}", m_filename); } @@ -105,15 +105,18 @@ DynamicObject const& DynamicLoader::dynamic_object() const return *m_cached_dynamic_object; } -size_t DynamicLoader::calculate_tls_size() const +void DynamicLoader::find_tls_size_and_alignment() { - size_t tls_size = 0; - image().for_each_program_header([&tls_size](auto program_header) { + image().for_each_program_header([this](auto program_header) { if (program_header.type() == PT_TLS) { - tls_size = program_header.size_in_memory(); + m_tls_size_of_current_object = program_header.size_in_memory(); + auto alignment = program_header.alignment(); + VERIFY(!alignment || is_power_of_two(alignment)); + m_tls_alignment_of_current_object = alignment > 1 ? alignment : 0; // No need to reserve extra space for single byte alignment + return IterationDecision::Break; } + return IterationDecision::Continue; }); - return tls_size; } bool DynamicLoader::validate() diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h index 5f8b72bda3..5968464554 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.h +++ b/Userland/Libraries/LibELF/DynamicLoader.h @@ -67,6 +67,7 @@ public: void set_tls_offset(size_t offset) { m_tls_offset = offset; }; size_t tls_size_of_current_object() const { return m_tls_size_of_current_object; } + size_t tls_alignment_of_current_object() const { return m_tls_alignment_of_current_object; } size_t tls_offset() const { return m_tls_offset; } const ELF::Image& image() const { return *m_elf_image; } @@ -134,7 +135,7 @@ private: }; RelocationResult do_relocation(DynamicObject::Relocation const&, ShouldInitializeWeak should_initialize_weak); void do_relr_relocations(); - size_t calculate_tls_size() const; + void find_tls_size_and_alignment(); ssize_t negative_offset_from_tls_block_end(ssize_t tls_offset, size_t value_of_symbol) const; String m_filename; @@ -157,6 +158,7 @@ private: ssize_t m_tls_offset { 0 }; size_t m_tls_size_of_current_object { 0 }; + size_t m_tls_alignment_of_current_object { 0 }; Vector m_unresolved_relocations;