mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 14:17:36 +00:00
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.
This commit is contained in:
parent
db76702d71
commit
6bbd2ebf83
7 changed files with 63 additions and 12 deletions
|
@ -389,7 +389,7 @@ public:
|
||||||
KResultOr<int> sys$recvfd(int sockfd, int options);
|
KResultOr<int> sys$recvfd(int sockfd, int options);
|
||||||
KResultOr<long> sys$sysconf(int name);
|
KResultOr<long> sys$sysconf(int name);
|
||||||
KResultOr<int> sys$disown(ProcessID);
|
KResultOr<int> sys$disown(ProcessID);
|
||||||
KResultOr<FlatPtr> sys$allocate_tls(size_t);
|
KResultOr<FlatPtr> sys$allocate_tls(Userspace<const char*> initial_data, size_t);
|
||||||
KResultOr<int> sys$prctl(int option, FlatPtr arg1, FlatPtr arg2);
|
KResultOr<int> sys$prctl(int option, FlatPtr arg1, FlatPtr arg2);
|
||||||
KResultOr<int> sys$set_coredump_metadata(Userspace<const Syscall::SC_set_coredump_metadata_params*>);
|
KResultOr<int> sys$set_coredump_metadata(Userspace<const Syscall::SC_set_coredump_metadata_params*>);
|
||||||
KResultOr<int> sys$anon_create(size_t, int options);
|
KResultOr<int> sys$anon_create(size_t, int options);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/WeakPtr.h>
|
#include <AK/WeakPtr.h>
|
||||||
|
#include <Kernel/Arch/x86/SmapDisabler.h>
|
||||||
#include <Kernel/FileSystem/FileDescription.h>
|
#include <Kernel/FileSystem/FileDescription.h>
|
||||||
#include <Kernel/PerformanceEventBuffer.h>
|
#include <Kernel/PerformanceEventBuffer.h>
|
||||||
#include <Kernel/Process.h>
|
#include <Kernel/Process.h>
|
||||||
|
@ -595,11 +596,11 @@ KResultOr<FlatPtr> Process::sys$mremap(Userspace<const Syscall::SC_mremap_params
|
||||||
return ENOTIMPL;
|
return ENOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
KResultOr<FlatPtr> Process::sys$allocate_tls(size_t size)
|
KResultOr<FlatPtr> Process::sys$allocate_tls(Userspace<const char*> initial_data, size_t size)
|
||||||
{
|
{
|
||||||
REQUIRE_PROMISE(stdio);
|
REQUIRE_PROMISE(stdio);
|
||||||
|
|
||||||
if (!size)
|
if (!size || size % PAGE_SIZE != 0)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
if (!m_master_tls_region.is_null())
|
if (!m_master_tls_region.is_null())
|
||||||
|
@ -627,6 +628,13 @@ KResultOr<FlatPtr> Process::sys$allocate_tls(size_t size)
|
||||||
m_master_tls_size = size;
|
m_master_tls_size = size;
|
||||||
m_master_tls_alignment = PAGE_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({});
|
auto tsr_result = main_thread->make_thread_specific_region({});
|
||||||
if (tsr_result.is_error())
|
if (tsr_result.is_error())
|
||||||
return EFAULT;
|
return EFAULT;
|
||||||
|
|
|
@ -73,9 +73,9 @@ int madvise(void* address, size_t size, int advice)
|
||||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
__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) {
|
if (rc < 0 && -rc < EMAXERRNO) {
|
||||||
errno = -rc;
|
errno = -rc;
|
||||||
return MAP_FAILED;
|
return MAP_FAILED;
|
||||||
|
|
|
@ -40,6 +40,6 @@ int munmap(void*, size_t);
|
||||||
int mprotect(void*, size_t, int prot);
|
int mprotect(void*, size_t, int prot);
|
||||||
int set_mmap_name(void*, size_t, const char*);
|
int set_mmap_name(void*, size_t, const char*);
|
||||||
int madvise(void*, size_t, int advice);
|
int madvise(void*, size_t, int advice);
|
||||||
void* allocate_tls(size_t);
|
void* allocate_tls(const char* initial_data, size_t);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include <LibELF/DynamicObject.h>
|
#include <LibELF/DynamicObject.h>
|
||||||
#include <LibELF/Hashes.h>
|
#include <LibELF/Hashes.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
|
@ -154,16 +156,26 @@ static Result<void, DlErrorMessage> map_dependencies(const String& name)
|
||||||
|
|
||||||
static void allocate_tls()
|
static void allocate_tls()
|
||||||
{
|
{
|
||||||
size_t total_tls_size = 0;
|
s_total_tls_size = 0;
|
||||||
for (const auto& data : s_loaders) {
|
for (const auto& data : s_loaders) {
|
||||||
dbgln_if(DYNAMIC_LOAD_DEBUG, "{}: TLS Size: {}", data.key, data.value->tls_size_of_current_object());
|
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);
|
if (!s_total_tls_size)
|
||||||
dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, tls_address: {:p}", tls_address);
|
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)
|
static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
|
||||||
|
|
|
@ -495,6 +495,36 @@ ssize_t DynamicLoader::negative_offset_from_tls_block_end(size_t value_of_symbol
|
||||||
return negative_offset;
|
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 <arch>/plt_trampoline.S
|
// Defined in <arch>/plt_trampoline.S
|
||||||
extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden")));
|
extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden")));
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ public:
|
||||||
bool is_dynamic() const { return m_elf_image.is_dynamic(); }
|
bool is_dynamic() const { return m_elf_image.is_dynamic(); }
|
||||||
|
|
||||||
static Optional<DynamicObject::SymbolLookupResult> lookup_symbol(const ELF::DynamicObject::Symbol&);
|
static Optional<DynamicObject::SymbolLookupResult> lookup_symbol(const ELF::DynamicObject::Symbol&);
|
||||||
|
void copy_initial_tls_data_into(ByteBuffer& buffer, size_t total_tls_size) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DynamicLoader(int fd, String filename, void* file_data, size_t file_size);
|
DynamicLoader(int fd, String filename, void* file_data, size_t file_size);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue