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

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

View File

@@ -5,9 +5,11 @@
__BEGIN_DECLS
#define __need_pid_t
#define __need_size_t
#include <sys/types.h>
#include <bits/types/pthread_attr_t.h>
#include <bits/types/pthread_key_t.h>
#include <bits/types/pthread_t.h>
#include <limits.h>
@@ -36,18 +38,24 @@ typedef struct _dynamic_tls_t
struct uthread
{
struct uthread* self;
// TLS
void* master_tls_addr;
size_t master_tls_size;
size_t master_tls_module_count;
_dynamic_tls_t* dynamic_tls;
_pthread_cleanup_t* cleanup_stack;
pthread_t id;
// LIBC
pid_t id;
pthread_attr_t attr;
int errno_;
int cancel_type;
int cancel_state;
volatile int canceled;
_pthread_cleanup_t* cleanup_funcs;
pthread_key_t specific_keys[PTHREAD_KEYS_MAX];
void* specific_values[PTHREAD_KEYS_MAX];
void* specific_vals[PTHREAD_KEYS_MAX];
// FIXME: make this dynamic
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_self() (_get_uthread()->id)
#define _pthread_self() (_get_uthread())
#define _pthread_testcancel() do { \
struct uthread* uthread = _get_uthread(); \

View File

@@ -18,7 +18,6 @@
struct pthread_trampoline_info_t
{
struct uthread* uthread;
bool detached;
void* (*start_routine)(void*);
void* arg;
};
@@ -61,8 +60,10 @@ asm(
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);
#if defined(__x86_64__)
syscall(SYS_SET_FSBASE, info.uthread);
#elif defined(__i686__)
@@ -70,10 +71,14 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
#else
#error
#endif
// NOTE: we have to get id and set TLS to for free to be able to use pthread_self
free(arg);
signal(SIGCANCEL, &_pthread_cancel_handler);
if (info.detached)
pthread_detach(info.uthread->id);
if (info.uthread->attr.detachstate == PTHREAD_CREATE_DETACHED)
pthread_detach(info.uthread);
pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED();
}
@@ -115,10 +120,10 @@ static void free_uthread(uthread* uthread)
void pthread_cleanup_pop(int execute)
{
uthread* uthread = _get_uthread();
ASSERT(uthread->cleanup_stack);
ASSERT(uthread->cleanup_funcs);
auto* cleanup = uthread->cleanup_stack;
uthread->cleanup_stack = cleanup->next;
auto* cleanup = uthread->cleanup_funcs;
uthread->cleanup_funcs = cleanup->next;
if (execute)
cleanup->routine(cleanup->arg);
@@ -135,9 +140,9 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
cleanup->routine = routine;
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;
@@ -197,9 +202,9 @@ void* pthread_getspecific(pthread_key_t key)
if (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;
}
pthread_spin_unlock(&s_pthread_key_lock);
@@ -220,7 +225,7 @@ int pthread_setspecific(pthread_key_t key, const void* value)
continue;
if (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;
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)
{
if (stacksize < PTHREAD_STACK_MIN)
return EINVAL;
if (auto rem = stacksize % getpagesize())
stacksize += getpagesize() - rem;
attr->stackaddr = nullptr;
attr->stacksize = stacksize;
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)));
if (info == nullptr)
@@ -387,12 +397,11 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
*info = {
.uthread = nullptr,
.detached = attr ? (attr->detachstate == PTHREAD_CREATE_DETACHED) : false,
.start_routine = start_routine,
.arg = arg,
};
long syscall_ret = 0;
uthread* result = nullptr;
{
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_module_count = self->master_tls_module_count,
.dynamic_tls = self->dynamic_tls,
.cleanup_stack = nullptr,
.id = -1,
.attr = attr ? *attr : s_default_pthread_attr,
.errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = 0,
.cleanup_funcs = nullptr,
.specific_keys = {},
.specific_values = {},
.specific_vals = {},
.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;
info->uthread = uthread;
result = uthread;
}
syscall_ret = syscall(SYS_THREAD_CREATE, _pthread_trampoline, info);
if (syscall_ret == -1)
if (syscall(SYS_THREAD_CREATE, _pthread_trampoline, info) == -1)
goto pthread_create_error;
if (thread_id)
*thread_id = syscall_ret;
if (thread)
*thread = result;
return 0;
pthread_create_error:
@@ -450,13 +460,17 @@ pthread_create_error:
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)
{
uthread* uthread = _get_uthread();
while (uthread->cleanup_stack)
while (uthread->cleanup_funcs)
pthread_cleanup_pop(1);
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])
{
destructor = s_pthread_key_destructors[i];
value = uthread->specific_values[i];
uthread->specific_values[i] = nullptr;
value = uthread->specific_vals[i];
uthread->specific_vals[i] = nullptr;
}
pthread_spin_unlock(&s_pthread_key_lock);
@@ -485,18 +499,26 @@ void pthread_exit(void* value_ptr)
break;
}
free_uthread(uthread);
if (uthread->attr.detachstate == PTHREAD_CREATE_DETACHED)
free_uthread(uthread);
syscall(SYS_THREAD_EXIT, value_ptr);
ASSERT_NOT_REACHED();
}
int pthread_join(pthread_t thread, void** value_ptr)
{
if (thread->attr.detachstate != PTHREAD_CREATE_JOINABLE)
return EINVAL;
do {
pthread_testcancel();
errno = 0;
} while (syscall(SYS_THREAD_JOIN, thread, value_ptr) == -1 && errno == EINTR);
return errno;
} while (syscall(SYS_THREAD_JOIN, thread->id, value_ptr) == -1 && errno == EINTR);
const int ret = errno;
if (ret == 0)
free_uthread(thread);
return ret;
}
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
@@ -536,8 +558,8 @@ void _pthread_call_atfork(int state)
switch (state)
{
case _PTHREAD_ATFORK_PREPARE: list = s_atfork_prepare; break;
case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break;
case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break;
case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break;
case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break;
default:
ASSERT_NOT_REACHED();
}
@@ -802,7 +824,7 @@ int pthread_mutex_lock(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)
{
@@ -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)
return ret;
const uint32_t tid = pthread_self();
const uint32_t tid = pthread_self()->id;
uint32_t expected = 0;
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)
{
ASSERT(mutex->futex == static_cast<uint32_t>(pthread_self()));
ASSERT(mutex->futex == static_cast<uint32_t>(pthread_self()->id));
mutex->lock_depth--;
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)
{
if (syscall(SYS_THREAD_KILL, thread, sig) == -1)
if (syscall(SYS_THREAD_KILL, thread->id, sig) == -1)
return errno;
return 0;
}

View File

@@ -65,12 +65,12 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
#error
#endif
{
self->cleanup_stack = nullptr;
self->id = syscall(SYS_THREAD_GETID);
self->errno_ = 0;
self->cancel_type = PTHREAD_CANCEL_DEFERRED;
self->cancel_state = PTHREAD_CANCEL_ENABLE;
self->canceled = false;
self->cleanup_funs = nullptr;
}
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_module_count = 1,
.dynamic_tls = nullptr,
.cleanup_stack = nullptr,
.id = static_cast<pthread_t>(syscall(SYS_THREAD_GETID)),
.errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
.cleanup_funs = nullptr,
.specific_keys = {},
.specific_values = {},
.specific_vals = {},
.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 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;
constexpr uintptr_t SYM_NOT_FOUND = -1;
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))
{
if (expected == tid)
@@ -1349,25 +1349,21 @@ static void initialize_tls(MasterTLS master_tls)
memcpy(tls_addr, master_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
// for example __cxa_guard_release calls pthread_cond_broadcast
uthread = {
.self = &uthread,
.master_tls_addr = master_tls.addr,
.master_tls_size = master_tls.size,
.master_tls_module_count = master_tls.module_count,
.dynamic_tls = s_dynamic_tls,
.cleanup_stack = nullptr,
.id = static_cast<pthread_t>(syscall(SYS_THREAD_GETID)),
.errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
.specific_keys = {},
.specific_values = {},
.dtv = {},
};
uthread.self = &uthread;
uthread.master_tls_addr = master_tls.addr,
uthread.master_tls_size = master_tls.size,
uthread.master_tls_module_count = master_tls.module_count,
uthread.dynamic_tls = s_dynamic_tls,
// these are prepared in libc init, but some other stuff may be calling pthread functions
// for example __cxa_guard_release calls pthread_cond_broadcast
uthread.id = syscall(SYS_THREAD_GETID);
uthread.errno_ = 0;
uthread.cancel_type = PTHREAD_CANCEL_DEFERRED;
uthread.cancel_state = PTHREAD_CANCEL_ENABLE;
uthread.canceled = false;
uthread.dtv[0] = master_tls.module_count;
for (size_t i = 0; i < s_loaded_file_count; i++)