From 6bbd2ebf83c4b8c0a11462f9abd65352c592b976 Mon Sep 17 00:00:00 2001 From: Itamar Date: Sat, 24 Apr 2021 11:30:20 +0300 Subject: [PATCH] Kernel+LibELF: Support initializing values of TLS data Previously, TLS data was always zero-initialized. To support initializing the values of TLS data, sys$allocate_tls now receives a buffer with the desired initial data, and copies it to the master TLS region of the process. The DynamicLinker gathers the initial TLS image and passes it to sys$allocate_tls. We also now require the size passed to sys$allocate_tls to be page-aligned, to make things easier. Note that this doesn't waste memory as the TLS data has to be allocated in separate pages anyway. --- Kernel/Process.h | 2 +- Kernel/Syscalls/mmap.cpp | 12 +++++++-- Userland/Libraries/LibC/mman.cpp | 4 +-- Userland/Libraries/LibC/mman.h | 2 +- Userland/Libraries/LibELF/DynamicLinker.cpp | 24 ++++++++++++----- Userland/Libraries/LibELF/DynamicLoader.cpp | 30 +++++++++++++++++++++ Userland/Libraries/LibELF/DynamicLoader.h | 1 + 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/Kernel/Process.h b/Kernel/Process.h index f7bf21463d..bdbfddca41 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -389,7 +389,7 @@ public: KResultOr sys$recvfd(int sockfd, int options); KResultOr sys$sysconf(int name); KResultOr sys$disown(ProcessID); - KResultOr sys$allocate_tls(size_t); + KResultOr sys$allocate_tls(Userspace initial_data, size_t); KResultOr sys$prctl(int option, FlatPtr arg1, FlatPtr arg2); KResultOr sys$set_coredump_metadata(Userspace); KResultOr sys$anon_create(size_t, int options); diff --git a/Kernel/Syscalls/mmap.cpp b/Kernel/Syscalls/mmap.cpp index 712eb5a479..b8fee3101b 100644 --- a/Kernel/Syscalls/mmap.cpp +++ b/Kernel/Syscalls/mmap.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -595,11 +596,11 @@ KResultOr Process::sys$mremap(Userspace Process::sys$allocate_tls(size_t size) +KResultOr Process::sys$allocate_tls(Userspace initial_data, size_t size) { REQUIRE_PROMISE(stdio); - if (!size) + if (!size || size % PAGE_SIZE != 0) return EINVAL; if (!m_master_tls_region.is_null()) @@ -627,6 +628,13 @@ KResultOr Process::sys$allocate_tls(size_t size) m_master_tls_size = size; m_master_tls_alignment = PAGE_SIZE; + { + Kernel::SmapDisabler disabler; + void* fault_at; + if (!Kernel::safe_memcpy((char*)m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), (char*)initial_data.ptr(), size, fault_at)) + return EFAULT; + } + auto tsr_result = main_thread->make_thread_specific_region({}); if (tsr_result.is_error()) return EFAULT; diff --git a/Userland/Libraries/LibC/mman.cpp b/Userland/Libraries/LibC/mman.cpp index 5f06c27c79..c78f524b71 100644 --- a/Userland/Libraries/LibC/mman.cpp +++ b/Userland/Libraries/LibC/mman.cpp @@ -73,9 +73,9 @@ int madvise(void* address, size_t size, int advice) __RETURN_WITH_ERRNO(rc, rc, -1); } -void* allocate_tls(size_t size) +void* allocate_tls(const char* initial_data, size_t size) { - ptrdiff_t rc = syscall(SC_allocate_tls, size); + ptrdiff_t rc = syscall(SC_allocate_tls, initial_data, size); if (rc < 0 && -rc < EMAXERRNO) { errno = -rc; return MAP_FAILED; diff --git a/Userland/Libraries/LibC/mman.h b/Userland/Libraries/LibC/mman.h index b2f9feb59d..d2638bd03f 100644 --- a/Userland/Libraries/LibC/mman.h +++ b/Userland/Libraries/LibC/mman.h @@ -40,6 +40,6 @@ int munmap(void*, size_t); int mprotect(void*, size_t, int prot); int set_mmap_name(void*, size_t, const char*); int madvise(void*, size_t, int advice); -void* allocate_tls(size_t); +void* allocate_tls(const char* initial_data, size_t); __END_DECLS diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 5ddf812a26..5b5126b0df 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -6,6 +6,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -154,16 +156,26 @@ static Result map_dependencies(const String& name) static void allocate_tls() { - size_t total_tls_size = 0; + s_total_tls_size = 0; for (const auto& data : s_loaders) { dbgln_if(DYNAMIC_LOAD_DEBUG, "{}: TLS Size: {}", data.key, data.value->tls_size_of_current_object()); - total_tls_size += data.value->tls_size_of_current_object(); + s_total_tls_size += data.value->tls_size_of_current_object(); } - if (total_tls_size) { - [[maybe_unused]] void* tls_address = ::allocate_tls(total_tls_size); - dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, tls_address: {:p}", tls_address); + + if (!s_total_tls_size) + return; + + auto page_aligned_size = align_up_to(s_total_tls_size, PAGE_SIZE); + ByteBuffer initial_tls_data = ByteBuffer::create_zeroed(page_aligned_size); + + // Initialize TLS data + for (const auto& entry : s_loaders) { + entry.value->copy_initial_tls_data_into(initial_tls_data, s_total_tls_size); } - s_total_tls_size = total_tls_size; + + void* master_tls = ::allocate_tls((char*)initial_tls_data.data(), initial_tls_data.size()); + VERIFY(master_tls != (void*)-1); + dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, master_tls: {:p}", master_tls); } static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data) diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index ca392b1bbb..6b5225393a 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -495,6 +495,36 @@ ssize_t DynamicLoader::negative_offset_from_tls_block_end(size_t value_of_symbol return negative_offset; } +void DynamicLoader::copy_initial_tls_data_into(ByteBuffer& buffer, size_t total_tls_size) const +{ + const u8* tls_data = nullptr; + size_t tls_size_in_image = 0; + + m_elf_image.for_each_program_header([this, &tls_data, &tls_size_in_image](ELF::Image::ProgramHeader program_header) { + if (program_header.type() != PT_TLS) + return IterationDecision::Continue; + + tls_data = (const u8*)m_file_data + program_header.offset(); + tls_size_in_image = program_header.size_in_image(); + return IterationDecision::Break; + }); + + 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) { + 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); + 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()); + + return IterationDecision::Continue; + }); +} + // Defined in /plt_trampoline.S extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden"))); diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h index bad63a88e8..0cccd48791 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.h +++ b/Userland/Libraries/LibELF/DynamicLoader.h @@ -78,6 +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; private: DynamicLoader(int fd, String filename, void* file_data, size_t file_size);