LibC: Make pthread_t pointer to uthread instead of a thread id

This will allow getting info about other threads in userspace!
This commit is contained in:
2026-07-02 19:29:19 +03:00
parent f449ca8161
commit b19b7f064a
8 changed files with 106 additions and 94 deletions

View File

@@ -431,52 +431,41 @@ namespace Kernel
O_EXEC | O_RDWR O_EXEC | O_RDWR
)); ));
BAN::Vector<uint8_t> temp_buffer;
TRY(temp_buffer.resize(BAN::Math::min<size_t>(master_size, PAGE_SIZE)));
size_t bytes_copied = 0; size_t bytes_copied = 0;
while (bytes_copied < master_size) while (bytes_copied < master_size)
{ {
const size_t to_copy = BAN::Math::min(master_size - bytes_copied, temp_buffer.size()); uint8_t buffer[PAGE_SIZE];
const size_t to_copy = BAN::Math::min(master_size - bytes_copied, sizeof(buffer));
const vaddr_t vaddr = master_addr + bytes_copied; const vaddr_t vaddr = master_addr + bytes_copied;
const paddr_t paddr = page_table.physical_address_of(vaddr & PAGE_ADDR_MASK); const paddr_t paddr = page_table.physical_address_of(vaddr & PAGE_ADDR_MASK);
PageTable::with_fast_page(paddr, [&] { PageTable::with_fast_page(paddr, [&] {
memcpy(temp_buffer.data(), PageTable::fast_page_as_ptr(vaddr % PAGE_SIZE), to_copy); memcpy(buffer, PageTable::fast_page_as_ptr(vaddr % PAGE_SIZE), to_copy);
}); });
TRY(region->copy_data_to_region(bytes_copied, temp_buffer.data(), to_copy)); TRY(region->copy_data_to_region(bytes_copied, buffer, to_copy));
bytes_copied += to_copy; bytes_copied += to_copy;
} }
auto uthread = TRY(BAN::UniqPtr<struct uthread>::create()); uthread uthread {};
*uthread = { uthread.self = reinterpret_cast<struct uthread*>(region->vaddr() + master_size);
.self = reinterpret_cast<struct uthread*>(region->vaddr() + master_size), uthread.master_tls_addr = reinterpret_cast<void*>(master_addr),
.master_tls_addr = reinterpret_cast<void*>(master_addr), uthread.master_tls_size = master_size,
.master_tls_size = master_size, uthread.master_tls_module_count = 1;
.master_tls_module_count = 1, uthread.dynamic_tls = nullptr;
.dynamic_tls = nullptr, uthread.dtv[0] = 0;
.cleanup_stack = nullptr, uthread.dtv[1] = region->vaddr();
.id = 0,
.errno_ = 0,
.cancel_type = 0,
.cancel_state = 0,
.canceled = 0,
.specific_keys = {},
.specific_values = {},
.dtv = { 0, region->vaddr() }
};
TRY(region->copy_data_to_region( TRY(region->copy_data_to_region(
master_size, master_size,
reinterpret_cast<const uint8_t*>(uthread.ptr()), reinterpret_cast<const uint8_t*>(&uthread),
sizeof(struct uthread) sizeof(struct uthread)
)); ));
TLSResult result; return TLSResult {
result.addr = region->vaddr() + master_size;; .region = BAN::move(region),
result.region = BAN::move(region); .addr = region->vaddr() + master_size,
return result; };
} }
BAN::ErrorOr<void> Process::add_mapped_region(BAN::UniqPtr<MemoryRegion>&& region) BAN::ErrorOr<void> Process::add_mapped_region(BAN::UniqPtr<MemoryRegion>&& region)

View File

@@ -7,10 +7,7 @@
__BEGIN_DECLS __BEGIN_DECLS
#define __need_pid_t typedef struct uthread* pthread_t;
#include <sys/types.h>
typedef pid_t pthread_t;
__END_DECLS __END_DECLS

View File

@@ -5,9 +5,11 @@
__BEGIN_DECLS __BEGIN_DECLS
#define __need_pid_t
#define __need_size_t #define __need_size_t
#include <sys/types.h> #include <sys/types.h>
#include <bits/types/pthread_attr_t.h>
#include <bits/types/pthread_key_t.h> #include <bits/types/pthread_key_t.h>
#include <bits/types/pthread_t.h> #include <bits/types/pthread_t.h>
#include <limits.h> #include <limits.h>
@@ -36,18 +38,24 @@ typedef struct _dynamic_tls_t
struct uthread struct uthread
{ {
struct uthread* self; struct uthread* self;
// TLS
void* master_tls_addr; void* master_tls_addr;
size_t master_tls_size; size_t master_tls_size;
size_t master_tls_module_count; size_t master_tls_module_count;
_dynamic_tls_t* dynamic_tls; _dynamic_tls_t* dynamic_tls;
_pthread_cleanup_t* cleanup_stack;
pthread_t id; // LIBC
pid_t id;
pthread_attr_t attr;
int errno_; int errno_;
int cancel_type; int cancel_type;
int cancel_state; int cancel_state;
volatile int canceled; volatile int canceled;
_pthread_cleanup_t* cleanup_funcs;
pthread_key_t specific_keys[PTHREAD_KEYS_MAX]; pthread_key_t specific_keys[PTHREAD_KEYS_MAX];
void* specific_values[PTHREAD_KEYS_MAX]; void* specific_vals[PTHREAD_KEYS_MAX];
// FIXME: make this dynamic // FIXME: make this dynamic
uintptr_t dtv[1 + 256]; uintptr_t dtv[1 + 256];
}; };

View File

@@ -168,7 +168,7 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg);
#define _pthread_equal(t1, t2) ((t1) == (t2)) #define _pthread_equal(t1, t2) ((t1) == (t2))
#define _pthread_self() (_get_uthread()->id) #define _pthread_self() (_get_uthread())
#define _pthread_testcancel() do { \ #define _pthread_testcancel() do { \
struct uthread* uthread = _get_uthread(); \ struct uthread* uthread = _get_uthread(); \

View File

@@ -18,7 +18,6 @@
struct pthread_trampoline_info_t struct pthread_trampoline_info_t
{ {
struct uthread* uthread; struct uthread* uthread;
bool detached;
void* (*start_routine)(void*); void* (*start_routine)(void*);
void* arg; void* arg;
}; };
@@ -61,8 +60,10 @@ asm(
extern "C" void _pthread_trampoline_cpp(void* arg) extern "C" void _pthread_trampoline_cpp(void* arg)
{ {
auto info = *reinterpret_cast<pthread_trampoline_info_t*>(arg); auto info = *static_cast<pthread_trampoline_info_t*>(arg);
info.uthread->id = syscall(SYS_THREAD_GETID); info.uthread->id = syscall(SYS_THREAD_GETID);
#if defined(__x86_64__) #if defined(__x86_64__)
syscall(SYS_SET_FSBASE, info.uthread); syscall(SYS_SET_FSBASE, info.uthread);
#elif defined(__i686__) #elif defined(__i686__)
@@ -70,10 +71,14 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
#else #else
#error #error
#endif #endif
// NOTE: we have to get id and set TLS to for free to be able to use pthread_self
free(arg); free(arg);
signal(SIGCANCEL, &_pthread_cancel_handler); signal(SIGCANCEL, &_pthread_cancel_handler);
if (info.detached) if (info.uthread->attr.detachstate == PTHREAD_CREATE_DETACHED)
pthread_detach(info.uthread->id); pthread_detach(info.uthread);
pthread_exit(info.start_routine(info.arg)); pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@@ -115,10 +120,10 @@ static void free_uthread(uthread* uthread)
void pthread_cleanup_pop(int execute) void pthread_cleanup_pop(int execute)
{ {
uthread* uthread = _get_uthread(); uthread* uthread = _get_uthread();
ASSERT(uthread->cleanup_stack); ASSERT(uthread->cleanup_funcs);
auto* cleanup = uthread->cleanup_stack; auto* cleanup = uthread->cleanup_funcs;
uthread->cleanup_stack = cleanup->next; uthread->cleanup_funcs = cleanup->next;
if (execute) if (execute)
cleanup->routine(cleanup->arg); cleanup->routine(cleanup->arg);
@@ -135,9 +140,9 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
cleanup->routine = routine; cleanup->routine = routine;
cleanup->arg = arg; cleanup->arg = arg;
cleanup->next = uthread->cleanup_stack; cleanup->next = uthread->cleanup_funcs;
uthread->cleanup_stack = cleanup; uthread->cleanup_funcs = cleanup;
} }
static pthread_key_t s_pthread_key_current = 1; static pthread_key_t s_pthread_key_current = 1;
@@ -197,9 +202,9 @@ void* pthread_getspecific(pthread_key_t key)
if (uthread->specific_keys[i] != key) if (uthread->specific_keys[i] != key)
{ {
uthread->specific_keys[i] = key; uthread->specific_keys[i] = key;
uthread->specific_values[i] = nullptr; uthread->specific_vals[i] = nullptr;
} }
ret = uthread->specific_values[i]; ret = uthread->specific_vals[i];
break; break;
} }
pthread_spin_unlock(&s_pthread_key_lock); pthread_spin_unlock(&s_pthread_key_lock);
@@ -220,7 +225,7 @@ int pthread_setspecific(pthread_key_t key, const void* value)
continue; continue;
if (uthread->specific_keys[i] != key) if (uthread->specific_keys[i] != key)
uthread->specific_keys[i] = key; uthread->specific_keys[i] = key;
uthread->specific_values[i] = const_cast<void*>(value); uthread->specific_vals[i] = const_cast<void*>(value);
ret = 0; ret = 0;
break; break;
} }
@@ -375,11 +380,16 @@ int pthread_attr_getstacksize(const pthread_attr_t* __restrict attr, size_t* __r
int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize) int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize)
{ {
if (stacksize < PTHREAD_STACK_MIN)
return EINVAL;
if (auto rem = stacksize % getpagesize())
stacksize += getpagesize() - rem;
attr->stackaddr = nullptr;
attr->stacksize = stacksize; attr->stacksize = stacksize;
return 0; return 0;
} }
int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg) int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg)
{ {
auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t))); auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t)));
if (info == nullptr) if (info == nullptr)
@@ -387,12 +397,11 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
*info = { *info = {
.uthread = nullptr, .uthread = nullptr,
.detached = attr ? (attr->detachstate == PTHREAD_CREATE_DETACHED) : false,
.start_routine = start_routine, .start_routine = start_routine,
.arg = arg, .arg = arg,
}; };
long syscall_ret = 0; uthread* result = nullptr;
{ {
uthread* self = _get_uthread(); uthread* self = _get_uthread();
@@ -413,14 +422,15 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
.master_tls_size = self->master_tls_size, .master_tls_size = self->master_tls_size,
.master_tls_module_count = self->master_tls_module_count, .master_tls_module_count = self->master_tls_module_count,
.dynamic_tls = self->dynamic_tls, .dynamic_tls = self->dynamic_tls,
.cleanup_stack = nullptr,
.id = -1, .id = -1,
.attr = attr ? *attr : s_default_pthread_attr,
.errno_ = 0, .errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED, .cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE, .cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = 0, .canceled = 0,
.cleanup_funcs = nullptr,
.specific_keys = {}, .specific_keys = {},
.specific_values = {}, .specific_vals = {},
.dtv = { self->dtv[0] } .dtv = { self->dtv[0] }
}; };
@@ -430,14 +440,14 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
uthread->dtv[i] = self->dtv[i] - self_addr + uthread_addr; uthread->dtv[i] = self->dtv[i] - self_addr + uthread_addr;
info->uthread = uthread; info->uthread = uthread;
result = uthread;
} }
syscall_ret = syscall(SYS_THREAD_CREATE, _pthread_trampoline, info); if (syscall(SYS_THREAD_CREATE, _pthread_trampoline, info) == -1)
if (syscall_ret == -1)
goto pthread_create_error; goto pthread_create_error;
if (thread_id) if (thread)
*thread_id = syscall_ret; *thread = result;
return 0; return 0;
pthread_create_error: pthread_create_error:
@@ -450,13 +460,17 @@ pthread_create_error:
int pthread_detach(pthread_t thread) int pthread_detach(pthread_t thread)
{ {
return syscall(SYS_THREAD_DETACH, thread); // FIXME: race if detached thread exits before assigning detachstate
if (syscall(SYS_THREAD_DETACH, thread->id) == -1)
return errno;
thread->attr.detachstate = PTHREAD_CREATE_DETACHED;
return 0;
} }
void pthread_exit(void* value_ptr) void pthread_exit(void* value_ptr)
{ {
uthread* uthread = _get_uthread(); uthread* uthread = _get_uthread();
while (uthread->cleanup_stack) while (uthread->cleanup_funcs)
pthread_cleanup_pop(1); pthread_cleanup_pop(1);
for (size_t iteration = 0; iteration < PTHREAD_DESTRUCTOR_ITERATIONS; iteration++) for (size_t iteration = 0; iteration < PTHREAD_DESTRUCTOR_ITERATIONS; iteration++)
@@ -471,8 +485,8 @@ void pthread_exit(void* value_ptr)
if (s_pthread_key_map[i] && uthread->specific_keys[i] == s_pthread_key_map[i]) if (s_pthread_key_map[i] && uthread->specific_keys[i] == s_pthread_key_map[i])
{ {
destructor = s_pthread_key_destructors[i]; destructor = s_pthread_key_destructors[i];
value = uthread->specific_values[i]; value = uthread->specific_vals[i];
uthread->specific_values[i] = nullptr; uthread->specific_vals[i] = nullptr;
} }
pthread_spin_unlock(&s_pthread_key_lock); pthread_spin_unlock(&s_pthread_key_lock);
@@ -485,18 +499,26 @@ void pthread_exit(void* value_ptr)
break; break;
} }
free_uthread(uthread); if (uthread->attr.detachstate == PTHREAD_CREATE_DETACHED)
free_uthread(uthread);
syscall(SYS_THREAD_EXIT, value_ptr); syscall(SYS_THREAD_EXIT, value_ptr);
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
int pthread_join(pthread_t thread, void** value_ptr) int pthread_join(pthread_t thread, void** value_ptr)
{ {
if (thread->attr.detachstate != PTHREAD_CREATE_JOINABLE)
return EINVAL;
do { do {
pthread_testcancel(); pthread_testcancel();
errno = 0; errno = 0;
} while (syscall(SYS_THREAD_JOIN, thread, value_ptr) == -1 && errno == EINTR); } while (syscall(SYS_THREAD_JOIN, thread->id, value_ptr) == -1 && errno == EINTR);
return errno;
const int ret = errno;
if (ret == 0)
free_uthread(thread);
return ret;
} }
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
@@ -536,8 +558,8 @@ void _pthread_call_atfork(int state)
switch (state) switch (state)
{ {
case _PTHREAD_ATFORK_PREPARE: list = s_atfork_prepare; break; case _PTHREAD_ATFORK_PREPARE: list = s_atfork_prepare; break;
case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break; case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break;
case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break; case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break;
default: default:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@@ -802,7 +824,7 @@ int pthread_mutex_lock(pthread_mutex_t* mutex)
int pthread_mutex_trylock(pthread_mutex_t* mutex) int pthread_mutex_trylock(pthread_mutex_t* mutex)
{ {
const uint32_t tid = pthread_self(); const uint32_t tid = pthread_self()->id;
switch (mutex->attr.type) switch (mutex->attr.type)
{ {
@@ -831,7 +853,7 @@ int pthread_mutex_timedlock(pthread_mutex_t* __restrict mutex, const struct time
if (const int ret = pthread_mutex_trylock(mutex); ret != EBUSY) if (const int ret = pthread_mutex_trylock(mutex); ret != EBUSY)
return ret; return ret;
const uint32_t tid = pthread_self(); const uint32_t tid = pthread_self()->id;
uint32_t expected = 0; uint32_t expected = 0;
while (!BAN::atomic_compare_exchange(mutex->futex, expected, tid, BAN::memory_order_acquire)) while (!BAN::atomic_compare_exchange(mutex->futex, expected, tid, BAN::memory_order_acquire))
@@ -854,7 +876,7 @@ int pthread_mutex_timedlock(pthread_mutex_t* __restrict mutex, const struct time
int pthread_mutex_unlock(pthread_mutex_t* mutex) int pthread_mutex_unlock(pthread_mutex_t* mutex)
{ {
ASSERT(mutex->futex == static_cast<uint32_t>(pthread_self())); ASSERT(mutex->futex == static_cast<uint32_t>(pthread_self()->id));
mutex->lock_depth--; mutex->lock_depth--;
if (mutex->lock_depth == 0) if (mutex->lock_depth == 0)

View File

@@ -41,7 +41,7 @@ void psignal(int signum, const char* message)
int pthread_kill(pthread_t thread, int sig) int pthread_kill(pthread_t thread, int sig)
{ {
if (syscall(SYS_THREAD_KILL, thread, sig) == -1) if (syscall(SYS_THREAD_KILL, thread->id, sig) == -1)
return errno; return errno;
return 0; return 0;
} }

View File

@@ -65,12 +65,12 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
#error #error
#endif #endif
{ {
self->cleanup_stack = nullptr;
self->id = syscall(SYS_THREAD_GETID); self->id = syscall(SYS_THREAD_GETID);
self->errno_ = 0; self->errno_ = 0;
self->cancel_type = PTHREAD_CANCEL_DEFERRED; self->cancel_type = PTHREAD_CANCEL_DEFERRED;
self->cancel_state = PTHREAD_CANCEL_ENABLE; self->cancel_state = PTHREAD_CANCEL_ENABLE;
self->canceled = false; self->canceled = false;
self->cleanup_funs = nullptr;
} }
else else
{ {
@@ -83,14 +83,14 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
.master_tls_size = 0, .master_tls_size = 0,
.master_tls_module_count = 1, .master_tls_module_count = 1,
.dynamic_tls = nullptr, .dynamic_tls = nullptr,
.cleanup_stack = nullptr,
.id = static_cast<pthread_t>(syscall(SYS_THREAD_GETID)), .id = static_cast<pthread_t>(syscall(SYS_THREAD_GETID)),
.errno_ = 0, .errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED, .cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE, .cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false, .canceled = false,
.cleanup_funs = nullptr,
.specific_keys = {}, .specific_keys = {},
.specific_values = {}, .specific_vals = {},
.dtv = { 0 }, .dtv = { 0 },
}; };

View File

@@ -216,16 +216,16 @@ static _dynamic_tls_t* s_dynamic_tls = nullptr;
static const char* s_ld_library_path = nullptr; static const char* s_ld_library_path = nullptr;
static BAN::Atomic<pthread_t> s_global_locker = 0; static BAN::Atomic<pid_t> s_global_locker = 0;
static uint32_t s_global_lock_depth = 0; static uint32_t s_global_lock_depth = 0;
constexpr uintptr_t SYM_NOT_FOUND = -1; constexpr uintptr_t SYM_NOT_FOUND = -1;
static void lock_global_lock() static void lock_global_lock()
{ {
const pthread_t tid = syscall(SYS_THREAD_GETID); const pid_t tid = syscall(SYS_THREAD_GETID);
pthread_t expected = 0; pid_t expected = 0;
while (!s_global_locker.compare_exchange(expected, tid)) while (!s_global_locker.compare_exchange(expected, tid))
{ {
if (expected == tid) if (expected == tid)
@@ -1349,25 +1349,21 @@ static void initialize_tls(MasterTLS master_tls)
memcpy(tls_addr, master_tls.addr, master_tls.size); memcpy(tls_addr, master_tls.addr, master_tls.size);
uthread& uthread = *reinterpret_cast<struct uthread*>(tls_addr + master_tls.size); uthread& uthread = *reinterpret_cast<struct uthread*>(tls_addr + master_tls.size);
memset(&uthread, 0, sizeof(uthread));
// uthread is prepared in libc init, but some other stuff may be calling pthread functions uthread.self = &uthread;
// for example __cxa_guard_release calls pthread_cond_broadcast uthread.master_tls_addr = master_tls.addr,
uthread = { uthread.master_tls_size = master_tls.size,
.self = &uthread, uthread.master_tls_module_count = master_tls.module_count,
.master_tls_addr = master_tls.addr, uthread.dynamic_tls = s_dynamic_tls,
.master_tls_size = master_tls.size,
.master_tls_module_count = master_tls.module_count, // these are prepared in libc init, but some other stuff may be calling pthread functions
.dynamic_tls = s_dynamic_tls, // for example __cxa_guard_release calls pthread_cond_broadcast
.cleanup_stack = nullptr, uthread.id = syscall(SYS_THREAD_GETID);
.id = static_cast<pthread_t>(syscall(SYS_THREAD_GETID)), uthread.errno_ = 0;
.errno_ = 0, uthread.cancel_type = PTHREAD_CANCEL_DEFERRED;
.cancel_type = PTHREAD_CANCEL_DEFERRED, uthread.cancel_state = PTHREAD_CANCEL_ENABLE;
.cancel_state = PTHREAD_CANCEL_ENABLE, uthread.canceled = false;
.canceled = false,
.specific_keys = {},
.specific_values = {},
.dtv = {},
};
uthread.dtv[0] = master_tls.module_count; uthread.dtv[0] = master_tls.module_count;
for (size_t i = 0; i < s_loaded_file_count; i++) for (size_t i = 0; i < s_loaded_file_count; i++)