Kernel/LibC/DynamicLoader: Implement thread local storage
For some reason this does not work on 32 bit version, so it is disabled on that platform. I'll have to look into it later to find the bug :)
This commit is contained in:
@@ -8,8 +8,15 @@ namespace Kernel::ELF
|
||||
|
||||
struct LoadResult
|
||||
{
|
||||
bool has_interpreter;
|
||||
struct TLS
|
||||
{
|
||||
vaddr_t addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
bool open_execfd;
|
||||
vaddr_t entry_point;
|
||||
BAN::Optional<TLS> master_tls;
|
||||
BAN::Vector<BAN::UniqPtr<MemoryRegion>> regions;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/Credentials.h>
|
||||
#include <kernel/ELF.h>
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
@@ -219,6 +220,13 @@ namespace Kernel
|
||||
Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp);
|
||||
static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0);
|
||||
|
||||
struct TLSResult
|
||||
{
|
||||
BAN::UniqPtr<MemoryRegion> region;
|
||||
vaddr_t addr;
|
||||
};
|
||||
static BAN::ErrorOr<TLSResult> initialize_thread_local_storage(PageTable&, ELF::LoadResult::TLS master_tls);
|
||||
|
||||
struct FileParent
|
||||
{
|
||||
VirtualFileSystem::File parent;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Kernel::ELF
|
||||
{
|
||||
@@ -108,13 +109,13 @@ namespace Kernel::ELF
|
||||
auto file_header = TRY(read_and_validate_file_header(inode));
|
||||
auto program_headers = TRY(read_program_headers(inode, file_header));
|
||||
|
||||
vaddr_t executable_end = 0;
|
||||
size_t exec_max_offset { 0 };
|
||||
BAN::String interpreter;
|
||||
|
||||
for (const auto& program_header : program_headers)
|
||||
{
|
||||
if (program_header.p_type == PT_LOAD)
|
||||
executable_end = BAN::Math::max<vaddr_t>(executable_end, program_header.p_vaddr + program_header.p_memsz);
|
||||
exec_max_offset = BAN::Math::max<vaddr_t>(exec_max_offset, program_header.p_vaddr + program_header.p_memsz);
|
||||
else if (program_header.p_type == PT_INTERP)
|
||||
{
|
||||
BAN::Vector<uint8_t> interp_buffer;
|
||||
@@ -140,9 +141,6 @@ namespace Kernel::ELF
|
||||
}
|
||||
}
|
||||
|
||||
if (file_header.e_type == ET_DYN)
|
||||
executable_end = 0x400000;
|
||||
|
||||
if (!interpreter.empty())
|
||||
{
|
||||
auto interpreter_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, interpreter, O_EXEC)).inode;
|
||||
@@ -164,15 +162,16 @@ namespace Kernel::ELF
|
||||
}
|
||||
|
||||
const vaddr_t load_base_vaddr =
|
||||
[&file_header, executable_end]() -> vaddr_t
|
||||
[&file_header, exec_max_offset]() -> vaddr_t
|
||||
{
|
||||
if (file_header.e_type == ET_EXEC)
|
||||
return 0;
|
||||
if (file_header.e_type == ET_DYN)
|
||||
return (executable_end + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
|
||||
return (exec_max_offset + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
|
||||
ASSERT_NOT_REACHED();
|
||||
}();
|
||||
|
||||
vaddr_t last_loaded_address = 0;
|
||||
BAN::Vector<BAN::UniqPtr<MemoryRegion>> memory_regions;
|
||||
for (const auto& program_header : program_headers)
|
||||
{
|
||||
@@ -241,10 +240,57 @@ namespace Kernel::ELF
|
||||
|
||||
TRY(memory_regions.emplace_back(BAN::move(region)));
|
||||
}
|
||||
|
||||
last_loaded_address = BAN::Math::max(last_loaded_address, pheader_base + program_header.p_memsz);
|
||||
}
|
||||
|
||||
LoadResult result;
|
||||
result.has_interpreter = !interpreter.empty();
|
||||
|
||||
for (const auto& program_header : program_headers)
|
||||
{
|
||||
if (program_header.p_type != PT_TLS)
|
||||
continue;
|
||||
|
||||
if (!BAN::Math::is_power_of_two(program_header.p_align))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
size_t region_size = program_header.p_memsz;
|
||||
if (auto rem = region_size % program_header.p_align)
|
||||
region_size += program_header.p_align - rem;
|
||||
|
||||
size_t offset = 0;
|
||||
if (auto rem = region_size % alignof(uthread))
|
||||
offset = alignof(uthread) - rem;
|
||||
|
||||
auto region = TRY(MemoryBackedRegion::create(
|
||||
page_table,
|
||||
offset + region_size,
|
||||
{ .start = last_loaded_address, .end = USERSPACE_END },
|
||||
MemoryRegion::Type::PRIVATE,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::Present
|
||||
));
|
||||
|
||||
for (vaddr_t vaddr = region->vaddr(); vaddr < region->vaddr() + offset + region->size(); vaddr += PAGE_SIZE)
|
||||
TRY(region->allocate_page_containing(vaddr, false));
|
||||
|
||||
if (program_header.p_filesz > 0)
|
||||
{
|
||||
BAN::Vector<uint8_t> file_data_buffer;
|
||||
TRY(file_data_buffer.resize(program_header.p_filesz));
|
||||
if (TRY(inode->read(program_header.p_offset, file_data_buffer.span())) != file_data_buffer.size())
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
TRY(region->copy_data_to_region(offset, file_data_buffer.data(), file_data_buffer.size()));
|
||||
}
|
||||
|
||||
result.master_tls = LoadResult::TLS {
|
||||
.addr = region->vaddr(),
|
||||
.size = region->size(),
|
||||
};
|
||||
|
||||
TRY(memory_regions.emplace_back(BAN::move(region)));
|
||||
}
|
||||
|
||||
result.open_execfd = !interpreter.empty();
|
||||
result.entry_point = load_base_vaddr + file_header.e_entry;
|
||||
result.regions = BAN::move(memory_regions);
|
||||
return BAN::move(result);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/sysmacros.h>
|
||||
@@ -142,7 +143,7 @@ namespace Kernel
|
||||
BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector;
|
||||
TRY(auxiliary_vector.reserve(1 + executable.open_execfd));
|
||||
|
||||
if (executable.has_interpreter)
|
||||
if (executable.open_execfd)
|
||||
{
|
||||
const int execfd = TRY(process->m_open_file_descriptors.open(BAN::move(executable_file), O_RDONLY));
|
||||
TRY(auxiliary_vector.push_back({
|
||||
@@ -156,6 +157,14 @@ namespace Kernel
|
||||
.a_un = { .a_val = 0 },
|
||||
}));
|
||||
|
||||
BAN::Optional<vaddr_t> tls_addr;
|
||||
if (executable.master_tls.has_value())
|
||||
{
|
||||
auto tls_result = TRY(process->initialize_thread_local_storage(process->page_table(), *executable.master_tls));
|
||||
TRY(process->m_mapped_regions.emplace_back(BAN::move(tls_result.region)));
|
||||
tls_addr = tls_result.addr;
|
||||
}
|
||||
|
||||
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
|
||||
MUST(thread->initialize_userspace(
|
||||
executable.entry_point,
|
||||
@@ -163,6 +172,8 @@ namespace Kernel
|
||||
process->m_environ.span(),
|
||||
auxiliary_vector.span()
|
||||
));
|
||||
if (tls_addr.has_value())
|
||||
thread->set_tls(*tls_addr);
|
||||
|
||||
process->add_thread(thread);
|
||||
process->register_to_scheduler();
|
||||
@@ -295,6 +306,63 @@ namespace Kernel
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Process::TLSResult> Process::initialize_thread_local_storage(PageTable& page_table, ELF::LoadResult::TLS master_tls)
|
||||
{
|
||||
const auto [master_addr, master_size] = master_tls;
|
||||
ASSERT(master_size % alignof(uthread) == 0);
|
||||
|
||||
const size_t tls_size = master_size + PAGE_SIZE;
|
||||
|
||||
auto region = TRY(MemoryBackedRegion::create(
|
||||
page_table,
|
||||
tls_size,
|
||||
{ .start = master_addr, .end = USERSPACE_END },
|
||||
MemoryRegion::Type::PRIVATE,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
|
||||
));
|
||||
|
||||
BAN::Vector<uint8_t> temp_buffer;
|
||||
TRY(temp_buffer.resize(BAN::Math::min<size_t>(master_size, PAGE_SIZE)));
|
||||
|
||||
size_t bytes_copied = 0;
|
||||
while (bytes_copied < master_size)
|
||||
{
|
||||
const size_t to_copy = BAN::Math::min(master_size - bytes_copied, temp_buffer.size());
|
||||
|
||||
const vaddr_t vaddr = master_addr + bytes_copied;
|
||||
const paddr_t paddr = page_table.physical_address_of(vaddr & PAGE_ADDR_MASK);
|
||||
PageTable::with_fast_page(paddr, [&] {
|
||||
memcpy(temp_buffer.data(), PageTable::fast_page_as_ptr(vaddr % PAGE_SIZE), to_copy);
|
||||
});
|
||||
|
||||
TRY(region->copy_data_to_region(bytes_copied, temp_buffer.data(), to_copy));
|
||||
bytes_copied += to_copy;
|
||||
}
|
||||
|
||||
const uthread uthread {
|
||||
.self = reinterpret_cast<struct uthread*>(region->vaddr() + master_size),
|
||||
.master_tls_addr = reinterpret_cast<void*>(master_addr),
|
||||
.master_tls_size = master_size,
|
||||
};
|
||||
const uintptr_t dtv[2] { 1, region->vaddr() };
|
||||
|
||||
TRY(region->copy_data_to_region(
|
||||
master_size,
|
||||
reinterpret_cast<const uint8_t*>(&uthread),
|
||||
sizeof(uthread)
|
||||
));
|
||||
TRY(region->copy_data_to_region(
|
||||
master_size + sizeof(uthread),
|
||||
reinterpret_cast<const uint8_t*>(&dtv),
|
||||
sizeof(dtv)
|
||||
));
|
||||
|
||||
TLSResult result;
|
||||
result.addr = region->vaddr() + master_size;;
|
||||
result.region = BAN::move(region);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Process::proc_meminfo(off_t offset, BAN::ByteSpan buffer) const
|
||||
{
|
||||
ASSERT(offset >= 0);
|
||||
@@ -572,7 +640,7 @@ namespace Kernel
|
||||
MUST(m_open_file_descriptors.close(auxiliary_vector.front().a_un.a_val));
|
||||
});
|
||||
|
||||
if (executable.has_interpreter)
|
||||
if (executable.open_execfd)
|
||||
{
|
||||
const int execfd = TRY(m_open_file_descriptors.open(BAN::move(executable_file), O_RDONLY));
|
||||
TRY(auxiliary_vector.push_back({
|
||||
@@ -594,6 +662,13 @@ namespace Kernel
|
||||
auxiliary_vector.span()
|
||||
));
|
||||
|
||||
if (executable.master_tls.has_value())
|
||||
{
|
||||
auto tls_result = TRY(initialize_thread_local_storage(*new_page_table, *executable.master_tls));
|
||||
TRY(new_mapped_regions.emplace_back(BAN::move(tls_result.region)));
|
||||
new_thread->set_tls(tls_result.addr);
|
||||
}
|
||||
|
||||
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
|
||||
Processor::set_interrupt_state(InterruptState::Disabled);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user