mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-22 02:09:24 +00:00
Kernel+LibC+LibELF: Move TLS handling to userspace
This removes the allocate_tls syscall and adds an archctl option to set the fs_base for the current thread on x86-64, since you can't set that register from userspace. enter_thread_context loads the fs_base for the next thread on each context switch. This also moves tpidr_el0 (the thread pointer register on AArch64) to the register state, so it gets properly saved/restored on context switches. The userspace TLS allocation code is kept pretty similar to the original kernel TLS code, aside from a couple of style changes. We also have to add a new argument "tls_pointer" to SC_create_thread_params, as we otherwise can't prevent race conditions between setting the thread pointer register and signal handling code that might be triggered before the thread pointer was set, which could use TLS.
This commit is contained in:
parent
aa44fe860d
commit
243d7003a2
Notes:
sideshowbarker
2024-07-16 20:39:14 +09:00
Author: https://github.com/spholz
Commit: 243d7003a2
Pull-request: https://github.com/SerenityOS/serenity/pull/24005
Reviewed-by: https://github.com/ADKaster ✅
26 changed files with 120 additions and 249 deletions
|
@ -49,7 +49,6 @@ enum class NeedsBigProcessLock {
|
||||||
S(accept4, NeedsBigProcessLock::No) \
|
S(accept4, NeedsBigProcessLock::No) \
|
||||||
S(adjtime, NeedsBigProcessLock::No) \
|
S(adjtime, NeedsBigProcessLock::No) \
|
||||||
S(alarm, NeedsBigProcessLock::No) \
|
S(alarm, NeedsBigProcessLock::No) \
|
||||||
S(allocate_tls, NeedsBigProcessLock::No) \
|
|
||||||
S(archctl, NeedsBigProcessLock::No) \
|
S(archctl, NeedsBigProcessLock::No) \
|
||||||
S(anon_create, NeedsBigProcessLock::No) \
|
S(anon_create, NeedsBigProcessLock::No) \
|
||||||
S(annotate_mapping, NeedsBigProcessLock::No) \
|
S(annotate_mapping, NeedsBigProcessLock::No) \
|
||||||
|
@ -375,6 +374,7 @@ struct SC_create_thread_params {
|
||||||
void* stack_location; // nullptr means any, o.w. process virtual address
|
void* stack_location; // nullptr means any, o.w. process virtual address
|
||||||
void* (*entry)(void*);
|
void* (*entry)(void*);
|
||||||
void* entry_argument;
|
void* entry_argument;
|
||||||
|
void* tls_pointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SC_realpath_params {
|
struct SC_realpath_params {
|
||||||
|
|
|
@ -5,3 +5,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define ARCHCTL_X86_64_SET_FS_BASE_FOR_CURRENT_THREAD 1
|
||||||
|
|
|
@ -165,8 +165,6 @@ public:
|
||||||
|
|
||||||
static void deferred_call_queue(Function<void()> callback);
|
static void deferred_call_queue(Function<void()> callback);
|
||||||
|
|
||||||
static void set_thread_specific_data(VirtualAddress thread_specific_data);
|
|
||||||
|
|
||||||
[[noreturn]] void initialize_context_switching(Thread& initial_thread);
|
[[noreturn]] void initialize_context_switching(Thread& initial_thread);
|
||||||
NEVER_INLINE void switch_context(Thread*& from_thread, Thread*& to_thread);
|
NEVER_INLINE void switch_context(Thread*& from_thread, Thread*& to_thread);
|
||||||
[[noreturn]] static void assume_context(Thread& thread, InterruptsState new_interrupts_state);
|
[[noreturn]] static void assume_context(Thread& thread, InterruptsState new_interrupts_state);
|
||||||
|
|
|
@ -26,7 +26,6 @@ template bool ProcessorBase<Processor>::are_interrupts_enabled();
|
||||||
template void ProcessorBase<Processor>::wait_for_interrupt() const;
|
template void ProcessorBase<Processor>::wait_for_interrupt() const;
|
||||||
template Processor& ProcessorBase<Processor>::by_id(u32 id);
|
template Processor& ProcessorBase<Processor>::by_id(u32 id);
|
||||||
template StringView ProcessorBase<Processor>::platform_string();
|
template StringView ProcessorBase<Processor>::platform_string();
|
||||||
template void ProcessorBase<Processor>::set_thread_specific_data(VirtualAddress thread_specific_data);
|
|
||||||
template void ProcessorBase<Processor>::initialize_context_switching(Thread& initial_thread);
|
template void ProcessorBase<Processor>::initialize_context_switching(Thread& initial_thread);
|
||||||
template void ProcessorBase<Processor>::switch_context(Thread*& from_thread, Thread*& to_thread);
|
template void ProcessorBase<Processor>::switch_context(Thread*& from_thread, Thread*& to_thread);
|
||||||
template void ProcessorBase<Processor>::assume_context(Thread& thread, InterruptsState new_interrupts_state);
|
template void ProcessorBase<Processor>::assume_context(Thread& thread, InterruptsState new_interrupts_state);
|
||||||
|
|
|
@ -43,6 +43,7 @@ void dump_registers(RegisterState const& regs)
|
||||||
dbgln("Saved Program Status: (NZCV({:#b}) DAIF({:#b}) M({:#b})) / {:#x}", ((regs.spsr_el1 >> 28) & 0b1111), ((regs.spsr_el1 >> 6) & 0b1111), regs.spsr_el1 & 0b1111, regs.spsr_el1);
|
dbgln("Saved Program Status: (NZCV({:#b}) DAIF({:#b}) M({:#b})) / {:#x}", ((regs.spsr_el1 >> 28) & 0b1111), ((regs.spsr_el1 >> 6) & 0b1111), regs.spsr_el1 & 0b1111, regs.spsr_el1);
|
||||||
dbgln("Exception Link Register: {:#x}", regs.elr_el1);
|
dbgln("Exception Link Register: {:#x}", regs.elr_el1);
|
||||||
dbgln("Stack Pointer (EL0): {:#x}", regs.sp_el0);
|
dbgln("Stack Pointer (EL0): {:#x}", regs.sp_el0);
|
||||||
|
dbgln("Software Thread ID Register (EL0): {:#x}", regs.tpidr_el0);
|
||||||
|
|
||||||
dbgln(" x0={:p} x1={:p} x2={:p} x3={:p} x4={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[4]);
|
dbgln(" x0={:p} x1={:p} x2={:p} x3={:p} x4={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[4]);
|
||||||
dbgln(" x5={:p} x6={:p} x7={:p} x8={:p} x9={:p}", regs.x[5], regs.x[6], regs.x[7], regs.x[8], regs.x[9]);
|
dbgln(" x5={:p} x6={:p} x7={:p} x8={:p} x9={:p}", regs.x[5], regs.x[6], regs.x[7], regs.x[8], regs.x[9]);
|
||||||
|
|
|
@ -337,6 +337,7 @@ FlatPtr ProcessorBase<T>::init_context(Thread& thread, bool leave_crit)
|
||||||
}
|
}
|
||||||
eretframe.elr_el1 = thread_regs.elr_el1;
|
eretframe.elr_el1 = thread_regs.elr_el1;
|
||||||
eretframe.sp_el0 = thread_regs.sp_el0;
|
eretframe.sp_el0 = thread_regs.sp_el0;
|
||||||
|
eretframe.tpidr_el0 = thread_regs.tpidr_el0;
|
||||||
eretframe.spsr_el1 = thread_regs.spsr_el1;
|
eretframe.spsr_el1 = thread_regs.spsr_el1;
|
||||||
|
|
||||||
// Push a TrapFrame onto the stack
|
// Push a TrapFrame onto the stack
|
||||||
|
@ -472,8 +473,6 @@ extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
|
||||||
|
|
||||||
to_thread->set_cpu(Processor::current().id());
|
to_thread->set_cpu(Processor::current().id());
|
||||||
|
|
||||||
Processor::set_thread_specific_data(to_thread->thread_specific_data());
|
|
||||||
|
|
||||||
auto in_critical = to_thread->saved_critical();
|
auto in_critical = to_thread->saved_critical();
|
||||||
VERIFY(in_critical > 0);
|
VERIFY(in_critical > 0);
|
||||||
Processor::restore_critical(in_critical);
|
Processor::restore_critical(in_critical);
|
||||||
|
@ -487,12 +486,6 @@ StringView ProcessorBase<T>::platform_string()
|
||||||
return "aarch64"sv;
|
return "aarch64"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void ProcessorBase<T>::set_thread_specific_data(VirtualAddress thread_specific_data)
|
|
||||||
{
|
|
||||||
Aarch64::Asm::set_tpidr_el0(thread_specific_data.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ProcessorBase<T>::wait_for_interrupt() const
|
void ProcessorBase<T>::wait_for_interrupt() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,11 +17,12 @@ VALIDATE_IS_AARCH64()
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
struct RegisterState {
|
struct alignas(16) RegisterState {
|
||||||
u64 x[31]; // Saved general purpose registers
|
u64 x[31]; // Saved general purpose registers
|
||||||
u64 spsr_el1; // Save Processor Status Register, EL1
|
u64 spsr_el1; // Save Processor Status Register, EL1
|
||||||
u64 elr_el1; // Exception Link Register, EL1
|
u64 elr_el1; // Exception Link Register, EL1
|
||||||
u64 sp_el0; // EL0 stack pointer
|
u64 sp_el0; // EL0 stack pointer
|
||||||
|
u64 tpidr_el0; // EL0 Software Thread ID Register
|
||||||
|
|
||||||
FlatPtr userspace_sp() const { return sp_el0; }
|
FlatPtr userspace_sp() const { return sp_el0; }
|
||||||
void set_userspace_sp(FlatPtr value)
|
void set_userspace_sp(FlatPtr value)
|
||||||
|
@ -51,7 +52,7 @@ struct RegisterState {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REGISTER_STATE_SIZE (34 * 8)
|
#define REGISTER_STATE_SIZE (36 * 8)
|
||||||
static_assert(AssertSize<RegisterState, REGISTER_STATE_SIZE>());
|
static_assert(AssertSize<RegisterState, REGISTER_STATE_SIZE>());
|
||||||
|
|
||||||
inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs)
|
inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs)
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct ThreadRegisters {
|
||||||
u64 spsr_el1;
|
u64 spsr_el1;
|
||||||
u64 elr_el1;
|
u64 elr_el1;
|
||||||
u64 sp_el0;
|
u64 sp_el0;
|
||||||
|
u64 tpidr_el0;
|
||||||
u64 ttbr0_el1;
|
u64 ttbr0_el1;
|
||||||
|
|
||||||
FlatPtr ip() const { return elr_el1; }
|
FlatPtr ip() const { return elr_el1; }
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
// NOTE: This size must be a multiple of 16 bytes, to ensure that the stack pointer
|
// NOTE: This size must be a multiple of 16 bytes, to ensure that the stack pointer
|
||||||
// stays 16 byte aligned.
|
// stays 16 byte aligned.
|
||||||
#define REGISTER_STATE_SIZE 272
|
#define REGISTER_STATE_SIZE (36 * 8)
|
||||||
#if REGISTER_STATE_SIZE % 16 != 0
|
#if REGISTER_STATE_SIZE % 16 != 0
|
||||||
# error "REGISTER_STATE_SIZE is not a multiple of 16 bytes!"
|
# error "REGISTER_STATE_SIZE is not a multiple of 16 bytes!"
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
#define SPSR_EL1_SLOT (31 * 8)
|
#define SPSR_EL1_SLOT (31 * 8)
|
||||||
#define ELR_EL1_SLOT (32 * 8)
|
#define ELR_EL1_SLOT (32 * 8)
|
||||||
#define SP_EL0_SLOT (33 * 8)
|
#define SP_EL0_SLOT (33 * 8)
|
||||||
|
#define TPIDR_EL0_SLOT (34 * 8)
|
||||||
|
|
||||||
// Vector Table Entry macro. Each entry is aligned at 128 bytes, meaning we have
|
// Vector Table Entry macro. Each entry is aligned at 128 bytes, meaning we have
|
||||||
// at most that many instructions.
|
// at most that many instructions.
|
||||||
|
@ -65,6 +66,8 @@
|
||||||
str x0, [sp, #ELR_EL1_SLOT]
|
str x0, [sp, #ELR_EL1_SLOT]
|
||||||
mrs x0, sp_el0
|
mrs x0, sp_el0
|
||||||
str x0, [sp, #SP_EL0_SLOT]
|
str x0, [sp, #SP_EL0_SLOT]
|
||||||
|
mrs x0, tpidr_el0
|
||||||
|
str x0, [sp, #TPIDR_EL0_SLOT]
|
||||||
|
|
||||||
// Set up TrapFrame struct on the stack
|
// Set up TrapFrame struct on the stack
|
||||||
mov x0, sp
|
mov x0, sp
|
||||||
|
@ -88,6 +91,8 @@
|
||||||
msr elr_el1, x0
|
msr elr_el1, x0
|
||||||
ldr x0, [sp, #SP_EL0_SLOT]
|
ldr x0, [sp, #SP_EL0_SLOT]
|
||||||
msr sp_el0, x0
|
msr sp_el0, x0
|
||||||
|
ldr x0, [sp, #TPIDR_EL0_SLOT]
|
||||||
|
msr tpidr_el0, x0
|
||||||
|
|
||||||
ldp x0, x1, [sp, #(0 * 0)]
|
ldp x0, x1, [sp, #(0 * 0)]
|
||||||
ldp x2, x3, [sp, #(2 * 8)]
|
ldp x2, x3, [sp, #(2 * 8)]
|
||||||
|
|
|
@ -499,8 +499,6 @@ extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
|
||||||
|
|
||||||
to_thread->set_cpu(Processor::current().id());
|
to_thread->set_cpu(Processor::current().id());
|
||||||
|
|
||||||
Processor::set_thread_specific_data(to_thread->thread_specific_data());
|
|
||||||
|
|
||||||
auto in_critical = to_thread->saved_critical();
|
auto in_critical = to_thread->saved_critical();
|
||||||
VERIFY(in_critical > 0);
|
VERIFY(in_critical > 0);
|
||||||
Processor::restore_critical(in_critical);
|
Processor::restore_critical(in_critical);
|
||||||
|
@ -546,12 +544,6 @@ StringView ProcessorBase<T>::platform_string()
|
||||||
return "riscv64"sv;
|
return "riscv64"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void ProcessorBase<T>::set_thread_specific_data(VirtualAddress)
|
|
||||||
{
|
|
||||||
// FIXME: Add support for thread-local storage on RISC-V
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ProcessorBase<T>::wait_for_interrupt() const
|
void ProcessorBase<T>::wait_for_interrupt() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
struct ArchSpecificThreadData {
|
struct ArchSpecificThreadData {
|
||||||
|
FlatPtr fs_base { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1390,7 +1390,7 @@ extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& processor = Processor::current();
|
auto& processor = Processor::current();
|
||||||
Processor::set_thread_specific_data(to_thread->thread_specific_data());
|
Processor::set_fs_base(to_thread->arch_specific_data().fs_base);
|
||||||
|
|
||||||
if (from_regs.cr3 != to_regs.cr3)
|
if (from_regs.cr3 != to_regs.cr3)
|
||||||
write_cr3(to_regs.cr3);
|
write_cr3(to_regs.cr3);
|
||||||
|
@ -1717,11 +1717,10 @@ UNMAP_AFTER_INIT void ProcessorBase<T>::initialize_context_switching(Thread& ini
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
void Processor::set_fs_base(FlatPtr fs_base)
|
||||||
void ProcessorBase<T>::set_thread_specific_data(VirtualAddress thread_specific_data)
|
|
||||||
{
|
{
|
||||||
MSR fs_base_msr(MSR_FS_BASE);
|
MSR fs_base_msr(MSR_FS_BASE);
|
||||||
fs_base_msr.set(thread_specific_data.get());
|
fs_base_msr.set(fs_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -153,6 +153,8 @@ public:
|
||||||
|
|
||||||
static void smp_unicast(u32 cpu, Function<void()>, bool async);
|
static void smp_unicast(u32 cpu, Function<void()>, bool async);
|
||||||
static void smp_broadcast_flush_tlb(Memory::PageDirectory const*, VirtualAddress, size_t);
|
static void smp_broadcast_flush_tlb(Memory::PageDirectory const*, VirtualAddress, size_t);
|
||||||
|
|
||||||
|
static void set_fs_base(FlatPtr);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -4,17 +4,24 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/API/archctl_numbers.h>
|
||||||
#include <Kernel/Tasks/Process.h>
|
#include <Kernel/Tasks/Process.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
ErrorOr<FlatPtr> Process::sys$archctl(int option, FlatPtr arg1)
|
ErrorOr<FlatPtr> Process::sys$archctl(int option, FlatPtr arg1)
|
||||||
{
|
{
|
||||||
(void)option;
|
|
||||||
(void)arg1;
|
|
||||||
|
|
||||||
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
||||||
return ENOSYS;
|
switch (option) {
|
||||||
|
case ARCHCTL_X86_64_SET_FS_BASE_FOR_CURRENT_THREAD: {
|
||||||
|
Thread::current()->arch_specific_data().fs_base = arg1;
|
||||||
|
Processor::set_fs_base(arg1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,6 @@ struct LoadResult {
|
||||||
FlatPtr load_base { 0 };
|
FlatPtr load_base { 0 };
|
||||||
FlatPtr entry_eip { 0 };
|
FlatPtr entry_eip { 0 };
|
||||||
size_t size { 0 };
|
size_t size { 0 };
|
||||||
LockWeakPtr<Memory::Region> tls_region;
|
|
||||||
size_t tls_size { 0 };
|
|
||||||
size_t tls_alignment { 0 };
|
|
||||||
LockWeakPtr<Memory::Region> stack_region;
|
LockWeakPtr<Memory::Region> stack_region;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -258,18 +255,13 @@ static ErrorOr<FlatPtr> get_load_offset(Elf_Ehdr const& main_program_header, Ope
|
||||||
return random_load_offset_in_range(selected_range.start, selected_range.end - selected_range.start);
|
return random_load_offset_in_range(selected_range.start, selected_range.end - selected_range.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ShouldAllocateTls {
|
|
||||||
No,
|
|
||||||
Yes,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShouldAllowSyscalls {
|
enum class ShouldAllowSyscalls {
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, OpenFileDescription& object_description,
|
static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, OpenFileDescription& object_description,
|
||||||
FlatPtr load_offset, ShouldAllocateTls should_allocate_tls, ShouldAllowSyscalls should_allow_syscalls, Optional<size_t> minimum_stack_size = {})
|
FlatPtr load_offset, ShouldAllowSyscalls should_allow_syscalls, Optional<size_t> minimum_stack_size = {})
|
||||||
{
|
{
|
||||||
auto& inode = *(object_description.inode());
|
auto& inode = *(object_description.inode());
|
||||||
auto vmobject = TRY(Memory::SharedInodeVMObject::try_create_with_inode(inode));
|
auto vmobject = TRY(Memory::SharedInodeVMObject::try_create_with_inode(inode));
|
||||||
|
@ -288,9 +280,6 @@ static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, Open
|
||||||
if (!elf_image.is_valid())
|
if (!elf_image.is_valid())
|
||||||
return ENOEXEC;
|
return ENOEXEC;
|
||||||
|
|
||||||
Memory::Region* master_tls_region { nullptr };
|
|
||||||
size_t master_tls_size = 0;
|
|
||||||
size_t master_tls_alignment = 0;
|
|
||||||
FlatPtr load_base_address = 0;
|
FlatPtr load_base_address = 0;
|
||||||
size_t stack_size = Thread::default_userspace_stack_size;
|
size_t stack_size = Thread::default_userspace_stack_size;
|
||||||
|
|
||||||
|
@ -302,24 +291,6 @@ static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, Open
|
||||||
|
|
||||||
Memory::MemoryManager::enter_address_space(new_space);
|
Memory::MemoryManager::enter_address_space(new_space);
|
||||||
|
|
||||||
auto load_tls_section = [&](auto& program_header) -> ErrorOr<void> {
|
|
||||||
VERIFY(should_allocate_tls == ShouldAllocateTls::Yes);
|
|
||||||
VERIFY(program_header.size_in_memory());
|
|
||||||
|
|
||||||
if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
|
|
||||||
dbgln("Shenanigans! ELF PT_TLS header sneaks outside of executable.");
|
|
||||||
return ENOEXEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto region_name = TRY(KString::formatted("{} (master-tls)", elf_name));
|
|
||||||
master_tls_region = TRY(new_space.allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, program_header.size_in_memory(), PAGE_SIZE, region_name->view(), PROT_READ | PROT_WRITE, AllocationStrategy::Reserve));
|
|
||||||
master_tls_size = program_header.size_in_memory();
|
|
||||||
master_tls_alignment = program_header.alignment();
|
|
||||||
|
|
||||||
TRY(copy_to_user(master_tls_region->vaddr().as_ptr(), program_header.raw_data(), program_header.size_in_image()));
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
auto load_writable_section = [&](auto& program_header) -> ErrorOr<void> {
|
auto load_writable_section = [&](auto& program_header) -> ErrorOr<void> {
|
||||||
// Writable section: create a copy in memory.
|
// Writable section: create a copy in memory.
|
||||||
VERIFY(program_header.alignment() % PAGE_SIZE == 0);
|
VERIFY(program_header.alignment() % PAGE_SIZE == 0);
|
||||||
|
@ -385,9 +356,6 @@ static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, Open
|
||||||
};
|
};
|
||||||
|
|
||||||
auto load_elf_program_header = [&](auto& program_header) -> ErrorOr<void> {
|
auto load_elf_program_header = [&](auto& program_header) -> ErrorOr<void> {
|
||||||
if (program_header.type() == PT_TLS)
|
|
||||||
return load_tls_section(program_header);
|
|
||||||
|
|
||||||
if (program_header.type() == PT_LOAD)
|
if (program_header.type() == PT_LOAD)
|
||||||
return load_section(program_header);
|
return load_section(program_header);
|
||||||
|
|
||||||
|
@ -416,9 +384,6 @@ static ErrorOr<LoadResult> load_elf_object(Memory::AddressSpace& new_space, Open
|
||||||
load_base_address,
|
load_base_address,
|
||||||
elf_image.entry().offset(load_offset).get(),
|
elf_image.entry().offset(load_offset).get(),
|
||||||
executable_size,
|
executable_size,
|
||||||
TRY(AK::try_make_weak_ptr_if_nonnull(master_tls_region)),
|
|
||||||
master_tls_size,
|
|
||||||
master_tls_alignment,
|
|
||||||
TRY(stack_region->try_make_weak_ptr())
|
TRY(stack_region->try_make_weak_ptr())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -429,24 +394,10 @@ Process::load(Memory::AddressSpace& new_space, NonnullRefPtr<OpenFileDescription
|
||||||
{
|
{
|
||||||
auto load_offset = TRY(get_load_offset(main_program_header, main_program_description, interpreter_description));
|
auto load_offset = TRY(get_load_offset(main_program_header, main_program_description, interpreter_description));
|
||||||
|
|
||||||
if (interpreter_description.is_null()) {
|
if (interpreter_description.is_null())
|
||||||
auto load_result = TRY(load_elf_object(new_space, main_program_description, load_offset, ShouldAllocateTls::Yes, ShouldAllowSyscalls::No, minimum_stack_size));
|
return TRY(load_elf_object(new_space, main_program_description, load_offset, ShouldAllowSyscalls::No, minimum_stack_size));
|
||||||
m_master_tls.with([&load_result](auto& master_tls) {
|
|
||||||
master_tls.region = load_result.tls_region;
|
|
||||||
master_tls.size = load_result.tls_size;
|
|
||||||
master_tls.alignment = load_result.tls_alignment;
|
|
||||||
});
|
|
||||||
return load_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto interpreter_load_result = TRY(load_elf_object(new_space, *interpreter_description, load_offset, ShouldAllocateTls::No, ShouldAllowSyscalls::Yes, minimum_stack_size));
|
return TRY(load_elf_object(new_space, *interpreter_description, load_offset, ShouldAllowSyscalls::Yes, minimum_stack_size));
|
||||||
|
|
||||||
// TLS allocation will be done in userspace by the loader
|
|
||||||
VERIFY(!interpreter_load_result.tls_region);
|
|
||||||
VERIFY(!interpreter_load_result.tls_alignment);
|
|
||||||
VERIFY(!interpreter_load_result.tls_size);
|
|
||||||
|
|
||||||
return interpreter_load_result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::clear_signal_handlers_for_exec()
|
void Process::clear_signal_handlers_for_exec()
|
||||||
|
@ -492,13 +443,6 @@ ErrorOr<void> Process::do_exec(NonnullRefPtr<OpenFileDescription> main_program_d
|
||||||
|
|
||||||
auto allocated_space = TRY(Memory::AddressSpace::try_create(*this, nullptr));
|
auto allocated_space = TRY(Memory::AddressSpace::try_create(*this, nullptr));
|
||||||
OwnPtr<Memory::AddressSpace> old_space;
|
OwnPtr<Memory::AddressSpace> old_space;
|
||||||
auto old_master_tls = m_master_tls.with([](auto& master_tls) {
|
|
||||||
auto old = master_tls;
|
|
||||||
master_tls.region = nullptr;
|
|
||||||
master_tls.size = 0;
|
|
||||||
master_tls.alignment = 0;
|
|
||||||
return old;
|
|
||||||
});
|
|
||||||
auto& new_space = m_space.with([&](auto& space) -> Memory::AddressSpace& {
|
auto& new_space = m_space.with([&](auto& space) -> Memory::AddressSpace& {
|
||||||
old_space = move(space);
|
old_space = move(space);
|
||||||
space = move(allocated_space);
|
space = move(allocated_space);
|
||||||
|
@ -509,9 +453,6 @@ ErrorOr<void> Process::do_exec(NonnullRefPtr<OpenFileDescription> main_program_d
|
||||||
m_space.with([&](auto& space) {
|
m_space.with([&](auto& space) {
|
||||||
space = old_space.release_nonnull();
|
space = old_space.release_nonnull();
|
||||||
});
|
});
|
||||||
m_master_tls.with([&](auto& master_tls) {
|
|
||||||
master_tls = old_master_tls;
|
|
||||||
});
|
|
||||||
Memory::MemoryManager::enter_process_address_space(*this);
|
Memory::MemoryManager::enter_process_address_space(*this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -703,11 +644,6 @@ ErrorOr<void> Process::do_exec(NonnullRefPtr<OpenFileDescription> main_program_d
|
||||||
protected_data.pid = new_main_thread->tid().value();
|
protected_data.pid = new_main_thread->tid().value();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto tsr_result = new_main_thread->make_thread_specific_region({});
|
|
||||||
if (tsr_result.is_error()) {
|
|
||||||
// FIXME: We cannot fail this late. Refactor this so the allocation happens before we commit to the new executable.
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
new_main_thread->reset_fpu_state();
|
new_main_thread->reset_fpu_state();
|
||||||
|
|
||||||
auto& regs = new_main_thread->m_regs;
|
auto& regs = new_main_thread->m_regs;
|
||||||
|
|
|
@ -149,6 +149,7 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
|
||||||
child_regs.spsr_el1 = regs.spsr_el1;
|
child_regs.spsr_el1 = regs.spsr_el1;
|
||||||
child_regs.elr_el1 = regs.elr_el1;
|
child_regs.elr_el1 = regs.elr_el1;
|
||||||
child_regs.sp_el0 = regs.sp_el0;
|
child_regs.sp_el0 = regs.sp_el0;
|
||||||
|
child_regs.tpidr_el0 = regs.tpidr_el0;
|
||||||
#elif ARCH(RISCV64)
|
#elif ARCH(RISCV64)
|
||||||
for (size_t i = 0; i < array_size(child_regs.x); ++i)
|
for (size_t i = 0; i < array_size(child_regs.x); ++i)
|
||||||
child_regs.x[i] = regs.x[i];
|
child_regs.x[i] = regs.x[i];
|
||||||
|
@ -162,7 +163,6 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TRY(address_space().with([&](auto& parent_space) {
|
TRY(address_space().with([&](auto& parent_space) {
|
||||||
return m_master_tls.with([&](auto& parent_master_tls) -> ErrorOr<void> {
|
|
||||||
return child->address_space().with([&](auto& child_space) -> ErrorOr<void> {
|
return child->address_space().with([&](auto& child_space) -> ErrorOr<void> {
|
||||||
child_space->set_enforces_syscall_regions(parent_space->enforces_syscall_regions());
|
child_space->set_enforces_syscall_regions(parent_space->enforces_syscall_regions());
|
||||||
for (auto& region : parent_space->region_tree().regions()) {
|
for (auto& region : parent_space->region_tree().regions()) {
|
||||||
|
@ -170,19 +170,8 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
|
||||||
auto region_clone = TRY(region.try_clone());
|
auto region_clone = TRY(region.try_clone());
|
||||||
TRY(region_clone->map(child_space->page_directory(), Memory::ShouldFlushTLB::No));
|
TRY(region_clone->map(child_space->page_directory(), Memory::ShouldFlushTLB::No));
|
||||||
TRY(child_space->region_tree().place_specifically(*region_clone, region.range()));
|
TRY(child_space->region_tree().place_specifically(*region_clone, region.range()));
|
||||||
auto* child_region = region_clone.leak_ptr();
|
(void)region_clone.leak_ptr();
|
||||||
|
|
||||||
if (®ion == parent_master_tls.region.unsafe_ptr()) {
|
|
||||||
TRY(child->m_master_tls.with([&](auto& child_master_tls) -> ErrorOr<void> {
|
|
||||||
child_master_tls.region = TRY(child_region->try_make_weak_ptr());
|
|
||||||
child_master_tls.size = parent_master_tls.size;
|
|
||||||
child_master_tls.alignment = parent_master_tls.alignment;
|
|
||||||
return {};
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -522,57 +522,6 @@ ErrorOr<FlatPtr> Process::sys$mremap(Userspace<Syscall::SC_mremap_params const*>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<FlatPtr> Process::sys$allocate_tls(Userspace<char const*> initial_data, size_t size)
|
|
||||||
{
|
|
||||||
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
|
||||||
TRY(require_promise(Pledge::stdio));
|
|
||||||
|
|
||||||
if (!size || size % PAGE_SIZE != 0)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
return m_master_tls.with([&](auto& master_tls) -> ErrorOr<FlatPtr> {
|
|
||||||
if (!master_tls.region.is_null())
|
|
||||||
return EEXIST;
|
|
||||||
|
|
||||||
if (thread_count() != 1)
|
|
||||||
return EFAULT;
|
|
||||||
|
|
||||||
Thread* main_thread = nullptr;
|
|
||||||
bool multiple_threads = false;
|
|
||||||
for_each_thread([&main_thread, &multiple_threads](auto& thread) {
|
|
||||||
if (main_thread)
|
|
||||||
multiple_threads = true;
|
|
||||||
main_thread = &thread;
|
|
||||||
return IterationDecision::Break;
|
|
||||||
});
|
|
||||||
VERIFY(main_thread);
|
|
||||||
|
|
||||||
if (multiple_threads)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
|
|
||||||
auto* region = TRY(space->allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, size, PAGE_SIZE, "Master TLS"sv, PROT_READ | PROT_WRITE));
|
|
||||||
|
|
||||||
master_tls.region = TRY(region->try_make_weak_ptr());
|
|
||||||
master_tls.size = size;
|
|
||||||
master_tls.alignment = PAGE_SIZE;
|
|
||||||
|
|
||||||
{
|
|
||||||
Kernel::SmapDisabler disabler;
|
|
||||||
void* fault_at;
|
|
||||||
if (!Kernel::safe_memcpy((char*)master_tls.region.unsafe_ptr()->vaddr().as_ptr(), (char*)initial_data.ptr(), size, fault_at))
|
|
||||||
return EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRY(main_thread->make_thread_specific_region({}));
|
|
||||||
|
|
||||||
Processor::set_thread_specific_data(main_thread->thread_specific_data());
|
|
||||||
|
|
||||||
return master_tls.region.unsafe_ptr()->vaddr().get();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<FlatPtr> Process::sys$annotate_mapping(Userspace<void*> address, int flags)
|
ErrorOr<FlatPtr> Process::sys$annotate_mapping(Userspace<void*> address, int flags)
|
||||||
{
|
{
|
||||||
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
||||||
|
|
|
@ -68,6 +68,8 @@ ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Sys
|
||||||
regs.rsi = (FlatPtr)params.entry_argument;
|
regs.rsi = (FlatPtr)params.entry_argument;
|
||||||
regs.rdx = (FlatPtr)params.stack_location;
|
regs.rdx = (FlatPtr)params.stack_location;
|
||||||
regs.rcx = (FlatPtr)params.stack_size;
|
regs.rcx = (FlatPtr)params.stack_size;
|
||||||
|
|
||||||
|
thread->arch_specific_data().fs_base = bit_cast<FlatPtr>(params.tls_pointer);
|
||||||
#elif ARCH(AARCH64)
|
#elif ARCH(AARCH64)
|
||||||
regs.ttbr0_el1 = address_space().with([](auto& space) { return space->page_directory().ttbr0(); });
|
regs.ttbr0_el1 = address_space().with([](auto& space) { return space->page_directory().ttbr0(); });
|
||||||
|
|
||||||
|
@ -76,6 +78,8 @@ ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Sys
|
||||||
regs.x[1] = (FlatPtr)params.entry_argument;
|
regs.x[1] = (FlatPtr)params.entry_argument;
|
||||||
regs.x[2] = (FlatPtr)params.stack_location;
|
regs.x[2] = (FlatPtr)params.stack_location;
|
||||||
regs.x[3] = (FlatPtr)params.stack_size;
|
regs.x[3] = (FlatPtr)params.stack_size;
|
||||||
|
|
||||||
|
regs.tpidr_el0 = bit_cast<FlatPtr>(params.tls_pointer);
|
||||||
#elif ARCH(RISCV64)
|
#elif ARCH(RISCV64)
|
||||||
regs.satp = address_space().with([](auto& space) { return space->page_directory().satp(); });
|
regs.satp = address_space().with([](auto& space) { return space->page_directory().satp(); });
|
||||||
|
|
||||||
|
@ -84,12 +88,12 @@ ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Sys
|
||||||
regs.x[10] = (FlatPtr)params.entry_argument;
|
regs.x[10] = (FlatPtr)params.entry_argument;
|
||||||
regs.x[11] = (FlatPtr)params.stack_location;
|
regs.x[11] = (FlatPtr)params.stack_location;
|
||||||
regs.x[12] = (FlatPtr)params.stack_size;
|
regs.x[12] = (FlatPtr)params.stack_size;
|
||||||
|
|
||||||
|
regs.x[3] = bit_cast<FlatPtr>(params.tls_pointer);
|
||||||
#else
|
#else
|
||||||
# error Unknown architecture
|
# error Unknown architecture
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TRY(thread->make_thread_specific_region({}));
|
|
||||||
|
|
||||||
PerformanceManager::add_thread_created_event(*thread);
|
PerformanceManager::add_thread_created_event(*thread);
|
||||||
|
|
||||||
SpinlockLocker lock(g_scheduler_lock);
|
SpinlockLocker lock(g_scheduler_lock);
|
||||||
|
|
|
@ -471,7 +471,6 @@ public:
|
||||||
ErrorOr<FlatPtr> sys$recvfd(int sockfd, int options);
|
ErrorOr<FlatPtr> sys$recvfd(int sockfd, int options);
|
||||||
ErrorOr<FlatPtr> sys$sysconf(int name);
|
ErrorOr<FlatPtr> sys$sysconf(int name);
|
||||||
ErrorOr<FlatPtr> sys$disown(ProcessID);
|
ErrorOr<FlatPtr> sys$disown(ProcessID);
|
||||||
ErrorOr<FlatPtr> sys$allocate_tls(Userspace<char const*> initial_data, size_t);
|
|
||||||
ErrorOr<FlatPtr> sys$prctl(int option, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3);
|
ErrorOr<FlatPtr> sys$prctl(int option, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3);
|
||||||
ErrorOr<FlatPtr> sys$anon_create(size_t, int options);
|
ErrorOr<FlatPtr> sys$anon_create(size_t, int options);
|
||||||
ErrorOr<FlatPtr> sys$statvfs(Userspace<Syscall::SC_statvfs_params const*> user_params);
|
ErrorOr<FlatPtr> sys$statvfs(Userspace<Syscall::SC_statvfs_params const*> user_params);
|
||||||
|
@ -955,13 +954,6 @@ private:
|
||||||
SpinlockProtected<RefPtr<ProcessList>, LockRank::None> m_jail_process_list;
|
SpinlockProtected<RefPtr<ProcessList>, LockRank::None> m_jail_process_list;
|
||||||
SpinlockProtected<RefPtr<Jail>, LockRank::Process> m_attached_jail {};
|
SpinlockProtected<RefPtr<Jail>, LockRank::Process> m_attached_jail {};
|
||||||
|
|
||||||
struct MasterThreadLocalStorage {
|
|
||||||
LockWeakPtr<Memory::Region> region;
|
|
||||||
size_t size { 0 };
|
|
||||||
size_t alignment { 0 };
|
|
||||||
};
|
|
||||||
SpinlockProtected<MasterThreadLocalStorage, LockRank::None> m_master_tls;
|
|
||||||
|
|
||||||
Mutex m_big_lock { "Process"sv, Mutex::MutexBehavior::BigLock };
|
Mutex m_big_lock { "Process"sv, Mutex::MutexBehavior::BigLock };
|
||||||
Mutex m_ptrace_lock { "ptrace"sv };
|
Mutex m_ptrace_lock { "ptrace"sv };
|
||||||
|
|
||||||
|
|
|
@ -434,12 +434,6 @@ void Thread::exit(void* exit_value)
|
||||||
set_should_die();
|
set_should_die();
|
||||||
u32 unlock_count;
|
u32 unlock_count;
|
||||||
[[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count);
|
[[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count);
|
||||||
if (m_thread_specific_range.has_value()) {
|
|
||||||
process().address_space().with([&](auto& space) {
|
|
||||||
auto* region = space->find_region_from_range(m_thread_specific_range.value());
|
|
||||||
space->deallocate_region(*region);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
die_if_needed();
|
die_if_needed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1209,7 +1203,6 @@ ErrorOr<NonnullRefPtr<Thread>> Thread::clone(NonnullRefPtr<Process> process)
|
||||||
m_signal_action_masks.span().copy_to(clone->m_signal_action_masks);
|
m_signal_action_masks.span().copy_to(clone->m_signal_action_masks);
|
||||||
clone->m_signal_mask = m_signal_mask;
|
clone->m_signal_mask = m_signal_mask;
|
||||||
clone->m_fpu_state = m_fpu_state;
|
clone->m_fpu_state = m_fpu_state;
|
||||||
clone->m_thread_specific_data = m_thread_specific_data;
|
|
||||||
clone->m_arch_specific_data = m_arch_specific_data;
|
clone->m_arch_specific_data = m_arch_specific_data;
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
@ -1356,34 +1349,6 @@ void Thread::print_backtrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Thread::make_thread_specific_region(Badge<Process>)
|
|
||||||
{
|
|
||||||
return process().m_master_tls.with([&](auto& master_tls) -> ErrorOr<void> {
|
|
||||||
// The process may not require a TLS region, or allocate TLS later with sys$allocate_tls (which is what dynamically loaded programs do)
|
|
||||||
if (!master_tls.region)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return process().address_space().with([&](auto& space) -> ErrorOr<void> {
|
|
||||||
auto region_alignment = max(master_tls.alignment, alignof(ThreadSpecificData));
|
|
||||||
auto region_size = align_up_to(master_tls.size, region_alignment) + sizeof(ThreadSpecificData);
|
|
||||||
auto* region = TRY(space->allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, region_size, PAGE_SIZE, "Thread-specific"sv, PROT_READ | PROT_WRITE));
|
|
||||||
|
|
||||||
m_thread_specific_range = region->range();
|
|
||||||
|
|
||||||
SmapDisabler disabler;
|
|
||||||
auto* thread_specific_data = (ThreadSpecificData*)region->vaddr().offset(align_up_to(master_tls.size, region_alignment)).as_ptr();
|
|
||||||
auto* thread_local_storage = (u8*)((u8*)thread_specific_data) - align_up_to(master_tls.size, master_tls.size);
|
|
||||||
m_thread_specific_data = VirtualAddress(thread_specific_data);
|
|
||||||
thread_specific_data->self = thread_specific_data;
|
|
||||||
|
|
||||||
if (master_tls.size != 0)
|
|
||||||
memcpy(thread_local_storage, master_tls.region.unsafe_ptr()->vaddr().as_ptr(), master_tls.size);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<Thread> Thread::from_tid_in_same_jail(ThreadID tid)
|
RefPtr<Thread> Thread::from_tid_in_same_jail(ThreadID tid)
|
||||||
{
|
{
|
||||||
return Thread::all_instances().with([&](auto& list) -> RefPtr<Thread> {
|
return Thread::all_instances().with([&](auto& list) -> RefPtr<Thread> {
|
||||||
|
|
|
@ -790,7 +790,6 @@ public:
|
||||||
State state() const { return m_state; }
|
State state() const { return m_state; }
|
||||||
StringView state_string() const;
|
StringView state_string() const;
|
||||||
|
|
||||||
VirtualAddress thread_specific_data() const { return m_thread_specific_data; }
|
|
||||||
ArchSpecificThreadData& arch_specific_data() { return m_arch_specific_data; }
|
ArchSpecificThreadData& arch_specific_data() { return m_arch_specific_data; }
|
||||||
ArchSpecificThreadData const& arch_specific_data() const { return m_arch_specific_data; }
|
ArchSpecificThreadData const& arch_specific_data() const { return m_arch_specific_data; }
|
||||||
|
|
||||||
|
@ -892,8 +891,6 @@ public:
|
||||||
|
|
||||||
FPUState& fpu_state() { return m_fpu_state; }
|
FPUState& fpu_state() { return m_fpu_state; }
|
||||||
|
|
||||||
ErrorOr<void> make_thread_specific_region(Badge<Process>);
|
|
||||||
|
|
||||||
unsigned syscall_count() const { return m_syscall_count; }
|
unsigned syscall_count() const { return m_syscall_count; }
|
||||||
void did_syscall() { ++m_syscall_count; }
|
void did_syscall() { ++m_syscall_count; }
|
||||||
unsigned inode_faults() const { return m_inode_faults; }
|
unsigned inode_faults() const { return m_inode_faults; }
|
||||||
|
@ -1186,8 +1183,6 @@ private:
|
||||||
FlatPtr m_kernel_stack_base { 0 };
|
FlatPtr m_kernel_stack_base { 0 };
|
||||||
FlatPtr m_kernel_stack_top { 0 };
|
FlatPtr m_kernel_stack_top { 0 };
|
||||||
NonnullOwnPtr<Memory::Region> m_kernel_stack_region;
|
NonnullOwnPtr<Memory::Region> m_kernel_stack_region;
|
||||||
VirtualAddress m_thread_specific_data;
|
|
||||||
Optional<Memory::VirtualRange> m_thread_specific_range;
|
|
||||||
Array<Optional<u32>, NSIG> m_signal_action_masks;
|
Array<Optional<u32>, NSIG> m_signal_action_masks;
|
||||||
Array<ProcessID, NSIG> m_signal_senders;
|
Array<ProcessID, NSIG> m_signal_senders;
|
||||||
Blocker* m_blocker { nullptr };
|
Blocker* m_blocker { nullptr };
|
||||||
|
|
|
@ -55,9 +55,13 @@ static __thread bool pending_cancellation = false;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
[[gnu::weak]] ErrorOr<FlatPtr> (*__create_new_tls_region)();
|
||||||
|
[[gnu::weak]] ErrorOr<void> (*__free_tls_region)(FlatPtr thread_pointer);
|
||||||
|
|
||||||
[[noreturn]] static void exit_thread(void* code, void* stack_location, size_t stack_size)
|
[[noreturn]] static void exit_thread(void* code, void* stack_location, size_t stack_size)
|
||||||
{
|
{
|
||||||
__pthread_key_destroy_for_current_thread();
|
__pthread_key_destroy_for_current_thread();
|
||||||
|
MUST(__free_tls_region(bit_cast<FlatPtr>(__builtin_thread_pointer())));
|
||||||
syscall(SC_exit_thread, code, stack_location, stack_size);
|
syscall(SC_exit_thread, code, stack_location, stack_size);
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -94,6 +98,12 @@ static int create_thread(pthread_t* thread, void* (*entry)(void*), void* argumen
|
||||||
thread_params->entry = entry;
|
thread_params->entry = entry;
|
||||||
thread_params->entry_argument = argument;
|
thread_params->entry_argument = argument;
|
||||||
|
|
||||||
|
auto maybe_thread_pointer = __create_new_tls_region();
|
||||||
|
if (maybe_thread_pointer.is_error())
|
||||||
|
return maybe_thread_pointer.error().code();
|
||||||
|
|
||||||
|
thread_params->tls_pointer = bit_cast<void*>(maybe_thread_pointer.release_value());
|
||||||
|
|
||||||
VERIFY((uintptr_t)stack % 16 == 0);
|
VERIFY((uintptr_t)stack % 16 == 0);
|
||||||
|
|
||||||
// Push a fake return address
|
// Push a fake return address
|
||||||
|
@ -102,6 +112,9 @@ static int create_thread(pthread_t* thread, void* (*entry)(void*), void* argumen
|
||||||
int rc = syscall(SC_create_thread, pthread_create_helper, thread_params);
|
int rc = syscall(SC_create_thread, pthread_create_helper, thread_params);
|
||||||
if (rc >= 0)
|
if (rc >= 0)
|
||||||
*thread = rc;
|
*thread = rc;
|
||||||
|
else
|
||||||
|
MUST(__free_tls_region(bit_cast<FlatPtr>(thread_params->tls_pointer)));
|
||||||
|
|
||||||
__RETURN_PTHREAD_ERROR(rc);
|
__RETURN_PTHREAD_ERROR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,16 +84,6 @@ int posix_madvise(void* address, size_t len, int advice)
|
||||||
return madvise(address, len, advice);
|
return madvise(address, len, advice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* allocate_tls(char const* initial_data, size_t size)
|
|
||||||
{
|
|
||||||
ptrdiff_t rc = syscall(SC_allocate_tls, initial_data, size);
|
|
||||||
if (rc < 0 && rc > -EMAXERRNO) {
|
|
||||||
errno = -rc;
|
|
||||||
return MAP_FAILED;
|
|
||||||
}
|
|
||||||
return (void*)rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html
|
||||||
int mlock(void const*, size_t)
|
int mlock(void const*, size_t)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,6 @@ int mprotect(void*, size_t, int prot);
|
||||||
int set_mmap_name(void*, size_t, char const*);
|
int set_mmap_name(void*, size_t, char const*);
|
||||||
int madvise(void*, size_t, int advice);
|
int madvise(void*, size_t, int advice);
|
||||||
int posix_madvise(void*, size_t, int advice);
|
int posix_madvise(void*, size_t, int advice);
|
||||||
void* allocate_tls(char const* initial_data, size_t);
|
|
||||||
int mlock(void const*, size_t);
|
int mlock(void const*, size_t);
|
||||||
int munlock(void const*, size_t);
|
int munlock(void const*, size_t);
|
||||||
int msync(void*, size_t, int flags);
|
int msync(void*, size_t, int flags);
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
|
|
||||||
namespace ELF {
|
namespace ELF {
|
||||||
|
|
||||||
void set_thread_pointer_register(FlatPtr)
|
void set_thread_pointer_register(FlatPtr value)
|
||||||
{
|
{
|
||||||
TODO();
|
// TODO: Consider if we want to support the FSGSBASE extension: https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/best-practices/guidance-enabling-fsgsbase.html
|
||||||
|
VERIFY(archctl(ARCHCTL_X86_64_SET_FS_BASE_FOR_CURRENT_THREAD, value) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <Kernel/API/VirtualMemoryAnnotations.h>
|
#include <Kernel/API/VirtualMemoryAnnotations.h>
|
||||||
#include <Kernel/API/prctl_numbers.h>
|
#include <Kernel/API/prctl_numbers.h>
|
||||||
|
#include <LibELF/Arch/tls.h>
|
||||||
#include <LibELF/AuxiliaryVector.h>
|
#include <LibELF/AuxiliaryVector.h>
|
||||||
#include <LibELF/DynamicLinker.h>
|
#include <LibELF/DynamicLinker.h>
|
||||||
#include <LibELF/DynamicLoader.h>
|
#include <LibELF/DynamicLoader.h>
|
||||||
|
@ -54,7 +55,9 @@ extern "C" [[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, E
|
||||||
|
|
||||||
struct TLSData {
|
struct TLSData {
|
||||||
size_t total_tls_size { 0 };
|
size_t total_tls_size { 0 };
|
||||||
|
void* tls_template { nullptr };
|
||||||
size_t tls_template_size { 0 };
|
size_t tls_template_size { 0 };
|
||||||
|
size_t alignment { 0 };
|
||||||
};
|
};
|
||||||
static TLSData s_tls_data;
|
static TLSData s_tls_data;
|
||||||
|
|
||||||
|
@ -234,6 +237,40 @@ static Result<void, DlErrorMessage> map_dependencies(ByteString const& path)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ThreadSpecificData {
|
||||||
|
ThreadSpecificData* self;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ErrorOr<FlatPtr> __create_new_tls_region()
|
||||||
|
{
|
||||||
|
auto static_tls_region_alignment = max(s_tls_data.alignment, alignof(ThreadSpecificData));
|
||||||
|
auto static_tls_region_size = align_up_to(s_tls_data.tls_template_size, static_tls_region_alignment) + sizeof(ThreadSpecificData);
|
||||||
|
void* thread_specific_ptr = serenity_mmap(nullptr, static_tls_region_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, static_tls_region_alignment, "Static TLS Data");
|
||||||
|
if (thread_specific_ptr == MAP_FAILED)
|
||||||
|
return Error::from_syscall("mmap"sv, -errno);
|
||||||
|
|
||||||
|
auto* thread_specific_data = bit_cast<ThreadSpecificData*>(bit_cast<FlatPtr>(thread_specific_ptr) + (align_up_to(s_tls_data.tls_template_size, static_tls_region_alignment)));
|
||||||
|
thread_specific_data->self = thread_specific_data;
|
||||||
|
|
||||||
|
auto* thread_local_storage = bit_cast<u8*>(bit_cast<FlatPtr>(thread_specific_data) - align_up_to(s_tls_data.tls_template_size, s_tls_data.alignment));
|
||||||
|
|
||||||
|
if (s_tls_data.tls_template_size != 0)
|
||||||
|
memcpy(thread_local_storage, s_tls_data.tls_template, s_tls_data.tls_template_size);
|
||||||
|
|
||||||
|
return bit_cast<FlatPtr>(thread_specific_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorOr<void> __free_tls_region(FlatPtr thread_pointer)
|
||||||
|
{
|
||||||
|
auto static_tls_region_alignment = max(s_tls_data.alignment, alignof(ThreadSpecificData));
|
||||||
|
auto static_tls_region_size = align_up_to(s_tls_data.tls_template_size, static_tls_region_alignment) + sizeof(ThreadSpecificData);
|
||||||
|
|
||||||
|
if (munmap(bit_cast<void*>(bit_cast<FlatPtr>(thread_pointer) - align_up_to(s_tls_data.tls_template_size, s_tls_data.alignment)), static_tls_region_size) != 0)
|
||||||
|
return Error::from_syscall("mmap"sv, -errno);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static void allocate_tls()
|
static void allocate_tls()
|
||||||
{
|
{
|
||||||
for (auto const& data : s_loaders) {
|
for (auto const& data : s_loaders) {
|
||||||
|
@ -244,25 +281,23 @@ static void allocate_tls()
|
||||||
if (s_tls_data.total_tls_size == 0)
|
if (s_tls_data.total_tls_size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto page_aligned_size = align_up_to(s_tls_data.total_tls_size, PAGE_SIZE);
|
s_tls_data.tls_template_size = align_up_to(s_tls_data.total_tls_size, PAGE_SIZE);
|
||||||
auto initial_tls_data_result = ByteBuffer::create_zeroed(page_aligned_size);
|
s_tls_data.alignment = PAGE_SIZE;
|
||||||
if (initial_tls_data_result.is_error()) {
|
s_tls_data.tls_template = mmap_with_name(nullptr, s_tls_data.tls_template_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, "TLS Template");
|
||||||
dbgln("Failed to allocate initial TLS data");
|
|
||||||
|
if (s_tls_data.tls_template == MAP_FAILED) {
|
||||||
|
dbgln("Failed to allocate memory for the TLS template");
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& initial_tls_data = initial_tls_data_result.value();
|
auto tls_template = Bytes(s_tls_data.tls_template, s_tls_data.tls_template_size);
|
||||||
|
|
||||||
// Initialize TLS data
|
// Initialize TLS data
|
||||||
for (auto const& entry : s_loaders) {
|
for (auto const& entry : s_loaders) {
|
||||||
entry.value->copy_initial_tls_data_into(initial_tls_data);
|
entry.value->copy_initial_tls_data_into(tls_template);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* master_tls = ::allocate_tls((char*)initial_tls_data.data(), initial_tls_data.size());
|
set_thread_pointer_register(MUST(__create_new_tls_region()));
|
||||||
VERIFY(master_tls != (void*)-1);
|
|
||||||
dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, master_tls: {:p}", master_tls);
|
|
||||||
|
|
||||||
s_tls_data.tls_template_size = initial_tls_data.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
|
static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
|
||||||
|
@ -648,6 +683,8 @@ void ELF::DynamicLinker::linker_main(ByteString&& main_program_path, int main_pr
|
||||||
s_magic_weak_symbols.set("environ"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, s_envp));
|
s_magic_weak_symbols.set("environ"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, s_envp));
|
||||||
s_magic_weak_symbols.set("__stack_chk_guard"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, stack_guard));
|
s_magic_weak_symbols.set("__stack_chk_guard"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, stack_guard));
|
||||||
s_magic_weak_symbols.set("__call_fini_functions"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __call_fini_functions));
|
s_magic_weak_symbols.set("__call_fini_functions"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __call_fini_functions));
|
||||||
|
s_magic_weak_symbols.set("__create_new_tls_region"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __create_new_tls_region));
|
||||||
|
s_magic_weak_symbols.set("__free_tls_region"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __free_tls_region));
|
||||||
s_magic_weak_symbols.set("__dl_iterate_phdr"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dl_iterate_phdr));
|
s_magic_weak_symbols.set("__dl_iterate_phdr"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dl_iterate_phdr));
|
||||||
s_magic_weak_symbols.set("__dlclose"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlclose));
|
s_magic_weak_symbols.set("__dlclose"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlclose));
|
||||||
s_magic_weak_symbols.set("__dlopen"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlopen));
|
s_magic_weak_symbols.set("__dlopen"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlopen));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue