From 7daad5e97392a9881cf4aee168270acb4347d366 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Mon, 14 Feb 2022 03:37:42 +0100 Subject: [PATCH] Backport of managarm/mlibc#311 --- abis/dripos/errno.h | 1 + options/ansi/generic/stdio-stubs.cpp | 8 +- options/ansi/include/inttypes.h | 1 + options/internal/include/mlibc/tcb.hpp | 3 + options/internal/include/mlibc/thread.hpp | 2 + options/internal/x86_64/thread.cpp | 6 + options/posix/generic/pthread-stubs.cpp | 220 +++++++++++++----- options/posix/include/mlibc/posix-sysdeps.hpp | 3 +- options/posix/include/pthread.h | 38 ++- sysdeps/dripos/generic/generic.cpp | 5 +- sysdeps/dripos/generic/thread.cpp | 26 ++- sysdeps/lemon/generic/lemon.cpp | 5 +- sysdeps/lemon/generic/thread.cpp | 26 ++- sysdeps/linux/generic/sysdeps.cpp | 4 +- sysdeps/linux/x86_64/thread.cpp | 46 ++-- sysdeps/managarm/aarch64/thread.cpp | 26 ++- sysdeps/managarm/generic/fork-exec.cpp | 7 +- sysdeps/managarm/x86_64/thread.cpp | 26 ++- tests/ansi/sscanf.c | 7 + tests/meson.build | 1 + tests/posix/pthread_attr.c | 174 ++++++++++++++ 21 files changed, 503 insertions(+), 132 deletions(-) create mode 100644 tests/posix/pthread_attr.c diff --git a/abis/dripos/errno.h b/abis/dripos/errno.h index 68cb386965..f9e7c2b127 100644 --- a/abis/dripos/errno.h +++ b/abis/dripos/errno.h @@ -109,6 +109,7 @@ #define EFTYPE 79 /* Inappropriate file type or format */ #define EOVERFLOW 80 #define EILSEQ 81 +#define ENOTSUP 82 #define EBADMSG 82 #define ECANCELED 83 diff --git a/options/ansi/generic/stdio-stubs.cpp b/options/ansi/generic/stdio-stubs.cpp index c21696c76b..c34d44720d 100644 --- a/options/ansi/generic/stdio-stubs.cpp +++ b/options/ansi/generic/stdio-stubs.cpp @@ -497,10 +497,10 @@ static int do_scanf(H &handler, const char *fmt, __gnuc_va_list args) { res = res * 16 + (c - '0'); } else if (c >= 'a' && c <= 'f') { handler.consume(); - res = res * 16 + (c - 'a'); + res = res * 16 + (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { handler.consume(); - res = res * 16 + (c - 'A'); + res = res * 16 + (c - 'A' + 10); } else { break; } @@ -549,10 +549,10 @@ static int do_scanf(H &handler, const char *fmt, __gnuc_va_list args) { res = res * 16 + (c - '0'); } else if (c >= 'a' && c <= 'f') { handler.consume(); - res = res * 16 + (c - 'a'); + res = res * 16 + (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { handler.consume(); - res = res * 16 + (c - 'A'); + res = res * 16 + (c - 'A' + 10); } else { break; } diff --git a/options/ansi/include/inttypes.h b/options/ansi/include/inttypes.h index be9b8742d3..968a849ab2 100644 --- a/options/ansi/include/inttypes.h +++ b/options/ansi/include/inttypes.h @@ -95,6 +95,7 @@ #define SCNx32 "x" #define SCNx64 "lx" #define SCNxMAX "lx" +#define SCNxPTR "lx" #define SCNd64 "ld" diff --git a/options/internal/include/mlibc/tcb.hpp b/options/internal/include/mlibc/tcb.hpp index f8a3fe8e7b..962a1c97b9 100644 --- a/options/internal/include/mlibc/tcb.hpp +++ b/options/internal/include/mlibc/tcb.hpp @@ -107,5 +107,8 @@ struct Tcb { CleanupHandler *cleanupBegin; CleanupHandler *cleanupEnd; int isJoinable; + size_t stackSize; + void *stackAddr; + size_t guardSize; }; diff --git a/options/internal/include/mlibc/thread.hpp b/options/internal/include/mlibc/thread.hpp index 0781966f11..0909e18cd3 100644 --- a/options/internal/include/mlibc/thread.hpp +++ b/options/internal/include/mlibc/thread.hpp @@ -1,9 +1,11 @@ #pragma once #include +#include namespace mlibc { Tcb *get_current_tcb(); +uintptr_t get_sp(); } // namespace mlibc diff --git a/options/internal/x86_64/thread.cpp b/options/internal/x86_64/thread.cpp index ae0b02302c..6b320cd075 100644 --- a/options/internal/x86_64/thread.cpp +++ b/options/internal/x86_64/thread.cpp @@ -10,4 +10,10 @@ Tcb *get_current_tcb() { return reinterpret_cast(ptr); } +uintptr_t get_sp() { + uintptr_t rsp; + asm ("mov %%rsp, %0" : "=r"(rsp)); + return rsp; +} + } // namespace mlibc diff --git a/options/posix/generic/pthread-stubs.cpp b/options/posix/generic/pthread-stubs.cpp index 90ba98c6a6..8a79be7269 100644 --- a/options/posix/generic/pthread-stubs.cpp +++ b/options/posix/generic/pthread-stubs.cpp @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -63,12 +65,19 @@ static constexpr unsigned int mutex_excl_bit = static_cast(1) << 30; static constexpr unsigned int rc_count_mask = (static_cast(1) << 31) - 1; static constexpr unsigned int rc_waiters_bit = static_cast(1) << 31; +static constexpr size_t default_stacksize = 0x200000; +static constexpr size_t default_guardsize = 4096; + // ---------------------------------------------------------------------------- // pthread_attr and pthread functions. // ---------------------------------------------------------------------------- // pthread_attr functions. -int pthread_attr_init(pthread_attr_t *) { +int pthread_attr_init(pthread_attr_t *attr) { + *attr = pthread_attr_t{}; + attr->__mlibc_stacksize = default_stacksize; + attr->__mlibc_guardsize = default_guardsize; + attr->__mlibc_detachstate = PTHREAD_CREATE_JOINABLE; return 0; } @@ -76,84 +85,190 @@ int pthread_attr_destroy(pthread_attr_t *) { return 0; } -int pthread_attr_getdetachstate(const pthread_attr_t *, int *) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) { + *detachstate = attr->__mlibc_detachstate; + return 0; } +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { + if (detachstate != PTHREAD_CREATE_DETACHED && + detachstate != PTHREAD_CREATE_JOINABLE) + return EINVAL; -int pthread_attr_setdetachstate(pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); + attr->__mlibc_detachstate = detachstate; + return 0; } -int pthread_attr_getstacksize(const pthread_attr_t *__restrict, size_t *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getstacksize(const pthread_attr_t *__restrict attr, size_t *__restrict stacksize) { + *stacksize = attr->__mlibc_stacksize; + return 0; } -int pthread_attr_setstacksize(pthread_attr_t *, size_t) { - mlibc::infoLogger() << "mlibc: pthread_attr_setstacksize() is not implemented correctly" << frg::endlog; +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { + if (stacksize < PTHREAD_STACK_MIN) + return EINVAL; + attr->__mlibc_stacksize = stacksize; return 0; } -int pthread_attr_getguardsize(const pthread_attr_t *__restrict, size_t *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) { + *stackaddr = attr->__mlibc_stackaddr; + return 0; } - -int pthread_attr_setguardsize(pthread_attr_t *, size_t) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) { + attr->__mlibc_stackaddr = stackaddr; + return 0; } -int pthread_attr_getscope(const pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize) { + *stackaddr = attr->__mlibc_stackaddr; + *stacksize = attr->__mlibc_stacksize; + return 0; +} +int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) { + if (stacksize < PTHREAD_STACK_MIN) + return EINVAL; + attr->__mlibc_stacksize = stacksize; + attr->__mlibc_stackaddr = stackaddr; + return 0; } -int pthread_attr_setscope(pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getguardsize(const pthread_attr_t *__restrict attr, size_t *__restrict guardsize) { + *guardsize = attr->__mlibc_guardsize; + return 0; +} +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { + attr->__mlibc_guardsize = guardsize; + return 0; } -int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict, int *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) { + *scope = attr->__mlibc_scope; + return 0; +} +int pthread_attr_setscope(pthread_attr_t *attr, int scope) { + if (scope != PTHREAD_SCOPE_SYSTEM && + scope != PTHREAD_SCOPE_PROCESS) + return EINVAL; + if (scope == PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + attr->__mlibc_scope = scope; + return 0; } -int pthread_attr_setschedpolicy(pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) { + *inheritsched = attr->__mlibc_inheritsched; + return 0; +} +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) { + if (inheritsched != PTHREAD_INHERIT_SCHED && + inheritsched != PTHREAD_EXPLICIT_SCHED) + return EINVAL; + attr->__mlibc_inheritsched = inheritsched; + return 0; } -int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getschedparam(const pthread_attr_t *__restrict attr, struct sched_param *__restrict schedparam) { + *schedparam = attr->__mlibc_schedparam; + return 0; +} +int pthread_attr_setschedparam(pthread_attr_t *__restrict attr, const struct sched_param *__restrict schedparam) { + // TODO: this is supposed to return EINVAL for when the schedparam doesn't make sense + // for the given schedpolicy. + attr->__mlibc_schedparam = *schedparam; + return 0; } -int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict attr, int *__restrict policy) { + *policy = attr->__mlibc_schedpolicy; + return 0; +} +int pthread_attr_setschedpolicy(pthread_attr_t *__restrict attr, int policy) { + if (policy != SCHED_FIFO && policy != SCHED_RR && + policy != SCHED_OTHER) + return EINVAL; + attr->__mlibc_schedpolicy = policy; + return 0; } -int pthread_attr_getinheritsched(const pthread_attr_t *__restrict, int *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +namespace { + void get_own_stackinfo(void **stack_addr, size_t *stack_size) { + auto fp = fopen("/proc/self/maps", "r"); + if (!fp) { + mlibc::infoLogger() << "mlibc pthreads: /proc/self/maps does not exist! Producing incorrect" + " stack results!" << frg::endlog; + return; + } + + char line[256]; + auto sp = mlibc::get_sp(); + while (fgets(line, 128, fp)) { + uintptr_t from, to; + if(sscanf(line, "%lx-%lx", &from, &to) != 2) + continue; + if (sp < to && sp > from) { + // We need to return the lowest byte of the stack. + *stack_addr = reinterpret_cast(from); + *stack_size = to - from; + fclose(fp); + return; + } + } + + fclose(fp); + } } -int pthread_attr_setinheritsched(pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) { + auto tcb = reinterpret_cast(thread); + *attr = pthread_attr_t{}; + + if (!tcb->stackAddr || !tcb->stackSize) { + get_own_stackinfo(&attr->__mlibc_stackaddr, &attr->__mlibc_stacksize); + } else { + attr->__mlibc_stacksize = tcb->stackSize; + attr->__mlibc_stackaddr = tcb->stackAddr; + } + + attr->__mlibc_guardsize = tcb->guardSize; + attr->__mlibc_detachstate = tcb->isJoinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED; + mlibc::infoLogger() << "pthread_getattr_np(): Implementation is incomplete!" << frg::endlog; + return 0; } extern "C" Tcb *__rtdl_allocateTcb(); // pthread functions. -int pthread_create(pthread_t *__restrict thread, const pthread_attr_t *__restrict, +int pthread_create(pthread_t *__restrict thread, const pthread_attr_t *__restrict attrp, void *(*entry) (void *), void *__restrict user_arg) { auto new_tcb = __rtdl_allocateTcb(); pid_t tid; - mlibc::sys_clone(reinterpret_cast(entry), user_arg, new_tcb, &tid); + pthread_attr_t attr = {}; + if (!attrp) + pthread_attr_init(&attr); + else + attr = *attrp; + + // TODO: due to alignment guarantees, the stackaddr and stacksize might change + // when the stack is allocated. Currently this isn't propagated to the TCB, + // but it should be. + void *stack = attr.__mlibc_stackaddr; + if (!mlibc::sys_prepare_stack) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + int ret = mlibc::sys_prepare_stack(&stack, reinterpret_cast(entry), + user_arg, new_tcb, &attr.__mlibc_stacksize, &attr.__mlibc_guardsize); + if (ret) + return ret; + + if (!mlibc::sys_clone) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + new_tcb->stackSize = attr.__mlibc_stacksize; + new_tcb->guardSize = attr.__mlibc_guardsize; + new_tcb->stackAddr = stack - attr.__mlibc_stacksize - attr.__mlibc_guardsize; + mlibc::sys_clone(new_tcb, &tid, stack); *thread = reinterpret_cast(new_tcb); __atomic_store_n(&new_tcb->tid, tid, __ATOMIC_RELAXED); @@ -312,21 +427,6 @@ int pthread_getname_np(pthread_t, char *, size_t) { __builtin_unreachable(); } -int pthread_attr_setstack(pthread_attr_t *, void *, size_t) { - __ensure(!"Not implemented"); - __builtin_unreachable(); -} - -int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *) { - __ensure(!"Not implemented"); - __builtin_unreachable(); -} - -int pthread_getattr_np(pthread_t, pthread_attr_t *) { - __ensure(!"Not implemented"); - __builtin_unreachable(); -} - int pthread_setschedparam(pthread_t, int, const struct sched_param *) { __ensure(!"Not implemented"); __builtin_unreachable(); diff --git a/options/posix/include/mlibc/posix-sysdeps.hpp b/options/posix/include/mlibc/posix-sysdeps.hpp index 4bb44a7d3c..e19fcc945b 100644 --- a/options/posix/include/mlibc/posix-sysdeps.hpp +++ b/options/posix/include/mlibc/posix-sysdeps.hpp @@ -92,7 +92,8 @@ int sys_close(int fd); [[gnu::weak]] void sys_yield(); [[gnu::weak]] int sys_sleep(time_t *secs, long *nanos); [[gnu::weak]] int sys_fork(pid_t *child); -[[gnu::weak]] int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *pid_out); +[[gnu::weak]] int sys_clone(void *tcb, pid_t *pid_out, void *stack); +[[gnu::weak]] int sys_prepare_stack(void **stack, void *entry, void *user_arg, void* tcb, size_t *stack_size, size_t *guard_size); [[gnu::weak]] int sys_execve(const char *path, char *const argv[], char *const envp[]); [[gnu::weak]] int sys_pselect(int num_fds, fd_set *read_set, fd_set *write_set, fd_set *except_set, const struct timespec *timeout, const sigset_t *sigmask, int *num_events); diff --git a/options/posix/include/pthread.h b/options/posix/include/pthread.h index 2f2e2f7ec9..eecdbcd475 100644 --- a/options/posix/include/pthread.h +++ b/options/posix/include/pthread.h @@ -25,6 +25,10 @@ extern "C" { #define PTHREAD_SCOPE_SYSTEM 0 #define PTHREAD_SCOPE_PROCESS 1 +// Values for pthread_attr_{get,set}inheritsched +#define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_EXPLICIT_SCHED 1 + // values for pthread_{get,set}canceltype(). #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_ASYNCHRONOUS 1 @@ -61,11 +65,18 @@ extern "C" { #define PTHREAD_INHERIT_SCHED 0 #define PTHREAD_EXPLICIT_SCHED 1 +#define PTHREAD_STACK_MIN 16384 + // TODO: move to own file and include in sys/types.h struct __mlibc_threadattr { - // TODO: the guardsize attribute needs to be supported here. - - int __mlibc_deatchstate; + size_t __mlibc_guardsize; + size_t __mlibc_stacksize; + void *__mlibc_stackaddr; + int __mlibc_detachstate; + int __mlibc_scope; + int __mlibc_inheritsched; + struct sched_param __mlibc_schedparam; + int __mlibc_schedpolicy; }; typedef struct __mlibc_threadattr pthread_attr_t; @@ -136,20 +147,33 @@ int pthread_attr_setdetachstate(pthread_attr_t *, int); int pthread_attr_getstacksize(const pthread_attr_t *__restrict, size_t *__restrict); int pthread_attr_setstacksize(pthread_attr_t *, size_t); +int pthread_attr_getstackaddr(const pthread_attr_t *, void **); +int pthread_attr_setstackaddr(pthread_attr_t *, void *); + +int pthread_attr_getstack(const pthread_attr_t *, void **, size_t*); +int pthread_attr_setstack(pthread_attr_t *, void *, size_t); + int pthread_attr_getguardsize(const pthread_attr_t *__restrict, size_t *__restrict); int pthread_attr_setguardsize(pthread_attr_t *, size_t); -int pthread_attr_getscope(const pthread_attr_t *, int); +int pthread_attr_getscope(const pthread_attr_t *, int*); int pthread_attr_setscope(pthread_attr_t *, int); +int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict); +int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict); + int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict, int *__restrict); -int pthread_attr_setschedpolicy(pthread_attr_t *, int); +int pthread_attr_setschedpolicy(pthread_attr_t *__restrict, int); + +int pthread_attr_getinheritsched(const pthread_attr_t *__restrict, int *__restrict); +int pthread_attr_setinheritsched(pthread_attr_t *__restrict, int); int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict); int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict); -int pthread_attr_getinheritsched(const pthread_attr_t *__restrict, int *__restrict); -int pthread_attr_setinheritsched(pthread_attr_t *, int); +int pthread_getattr_np(pthread_t, pthread_attr_t *); + +int pthread_getattr_np(pthread_t, pthread_attr_t *); // pthread functions. int pthread_create(pthread_t *__restrict, const pthread_attr_t *__restrict, diff --git a/sysdeps/dripos/generic/generic.cpp b/sysdeps/dripos/generic/generic.cpp index 0a851691c7..ba1f536928 100644 --- a/sysdeps/dripos/generic/generic.cpp +++ b/sysdeps/dripos/generic/generic.cpp @@ -214,13 +214,12 @@ int sys_futex_wake(int *pointer) { // All remaining functions are disabled in ldso. #ifndef MLIBC_BUILDING_RTDL -int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *tid_out) { - void *sp = prepare_stack(entry, user_arg, tcb); +int sys_clone(void *tcb, pid_t *tid_out, void *stack) { int tid; asm volatile ("syscall" : "=a"(tid) - : "a"(67), "D"(__mlibc_start_thread), "S"(sp), "d"(tcb) + : "a"(67), "D"(__mlibc_start_thread), "S"(stack), "d"(tcb) : "rcx", "r11"); if (tid_out) diff --git a/sysdeps/dripos/generic/thread.cpp b/sysdeps/dripos/generic/thread.cpp index 35e7a9bece..1e0cedf2ce 100644 --- a/sysdeps/dripos/generic/thread.cpp +++ b/sysdeps/dripos/generic/thread.cpp @@ -28,17 +28,29 @@ extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) { namespace mlibc { -void *prepare_stack(void *entry, void *user_arg, void *tcb) { - uintptr_t *sp = reinterpret_cast(reinterpret_cast( - mmap(nullptr, 0x200000, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - ) + 0x200000); +static constexpr size_t default_stacksize = 0x200000; + +int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size) { + uintptr_t *sp; + if (!*stack_size) + *stack_size = default_stacksize; + *guard_size = 0; + + if (*stack) { + sp = reinterpret_cast(*stack); + } else { + sp = reinterpret_cast(reinterpret_cast( + mmap(nullptr, *stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ) + *stack_size); + } *--sp = reinterpret_cast(tcb); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } //namespace mlibc diff --git a/sysdeps/lemon/generic/lemon.cpp b/sysdeps/lemon/generic/lemon.cpp index fd94b7df54..795f9b67bf 100644 --- a/sysdeps/lemon/generic/lemon.cpp +++ b/sysdeps/lemon/generic/lemon.cpp @@ -140,8 +140,7 @@ void sys_yield(){ syscall(SYS_YIELD); } -int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *tid_out){ - auto stack = prepare_stack(entry, user_arg, tcb); +int sys_clone(void *tcb, pid_t *tid_out, void *stack){ pid_t tid = syscall(SYS_SPAWN_THREAD, __mlibc_start_thread, stack); if(tid < 0){ @@ -192,4 +191,4 @@ int sys_getentropy(void *buffer, size_t length){ } #endif -} +} diff --git a/sysdeps/lemon/generic/thread.cpp b/sysdeps/lemon/generic/thread.cpp index 574b98d21e..d0d00babfa 100644 --- a/sysdeps/lemon/generic/thread.cpp +++ b/sysdeps/lemon/generic/thread.cpp @@ -28,17 +28,29 @@ extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) { namespace mlibc { -void* prepare_stack(void *entry, void *user_arg, void *tcb) { - uintptr_t *sp = reinterpret_cast(reinterpret_cast( - mmap(nullptr, 0x1000000, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - ) + 0x1000000); +static constexpr size_t default_stacksize = 0x200000; + +int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size) { + if (!*stack_size) + *stack_size = default_stacksize; + *guard_size = 0; + + uintptr_t *sp; + if (*stack) { + sp = reinterpret_cast(*stack); + } else { + sp = reinterpret_cast(reinterpret_cast( + mmap(nullptr, *stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ) + *stack_size); + } *--sp = reinterpret_cast(tcb); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } //namespace mlibc diff --git a/sysdeps/linux/generic/sysdeps.cpp b/sysdeps/linux/generic/sysdeps.cpp index 582115a4f0..5e984724ab 100644 --- a/sysdeps/linux/generic/sysdeps.cpp +++ b/sysdeps/linux/generic/sysdeps.cpp @@ -366,9 +366,7 @@ int sys_sigprocmask(int how, const sigset_t *set, sigset_t *old) { return 0; } -int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *pid_out) { - void *stack = prepare_stack(entry, user_arg); - +int sys_clone(void *tcb, pid_t *pid_out, void *stack) { unsigned long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | CLONE_SETTLS | CLONE_PARENT_SETTID; diff --git a/sysdeps/linux/x86_64/thread.cpp b/sysdeps/linux/x86_64/thread.cpp index 94d03b6efe..ed2ec5a671 100644 --- a/sysdeps/linux/x86_64/thread.cpp +++ b/sysdeps/linux/x86_64/thread.cpp @@ -5,11 +5,7 @@ #include #include #include - -namespace { - constexpr unsigned int STACK_SIZE = 0x200000; - constexpr unsigned int GUARD_SIZE = 0x2000; -} +#include extern "C" void __mlibc_enter_thread(void *entry, void *user_arg) { // The linux kernel already sets the TCB in sys_clone(). @@ -31,20 +27,34 @@ extern "C" void __mlibc_enter_thread(void *entry, void *user_arg) { namespace mlibc { -void *prepare_stack(void *entry, void *user_arg) { - uintptr_t map = reinterpret_cast( - mmap(nullptr, STACK_SIZE + GUARD_SIZE, - PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - ); - __ensure(reinterpret_cast(map) != MAP_FAILED); - int ret = mprotect(reinterpret_cast(map + GUARD_SIZE), STACK_SIZE, - PROT_READ | PROT_WRITE); - __ensure(!ret); - - auto sp = reinterpret_cast(map + STACK_SIZE + GUARD_SIZE); +static constexpr size_t default_stacksize = 0x200000; + +int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size) { + (void)tcb; + if (!*stack_size) + *stack_size = default_stacksize; + + uintptr_t map; + if (*stack) { + map = reinterpret_cast(*stack); + } else { + map = reinterpret_cast( + mmap(nullptr, *stack_size + *guard_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ); + if (reinterpret_cast(map) == MAP_FAILED) + return EAGAIN; + int ret = mprotect(reinterpret_cast(map + *guard_size), *stack_size, + PROT_READ | PROT_WRITE); + if(ret) + return EAGAIN; + } + + auto sp = reinterpret_cast(map + *stack_size + *guard_size); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } // namespace mlibc diff --git a/sysdeps/managarm/aarch64/thread.cpp b/sysdeps/managarm/aarch64/thread.cpp index 48029b54dd..d0d00babfa 100644 --- a/sysdeps/managarm/aarch64/thread.cpp +++ b/sysdeps/managarm/aarch64/thread.cpp @@ -28,17 +28,29 @@ extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) { namespace mlibc { -void *prepare_stack(void *entry, void *user_arg, void *tcb) { - uintptr_t *sp = reinterpret_cast(reinterpret_cast( - mmap(nullptr, 0x1000000, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - ) + 0x1000000); +static constexpr size_t default_stacksize = 0x200000; + +int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size) { + if (!*stack_size) + *stack_size = default_stacksize; + *guard_size = 0; + + uintptr_t *sp; + if (*stack) { + sp = reinterpret_cast(*stack); + } else { + sp = reinterpret_cast(reinterpret_cast( + mmap(nullptr, *stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ) + *stack_size); + } *--sp = reinterpret_cast(tcb); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } //namespace mlibc diff --git a/sysdeps/managarm/generic/fork-exec.cpp b/sysdeps/managarm/generic/fork-exec.cpp index 88ca04a001..80db6ce5b9 100644 --- a/sysdeps/managarm/generic/fork-exec.cpp +++ b/sysdeps/managarm/generic/fork-exec.cpp @@ -618,14 +618,11 @@ int sys_getrusage(int scope, struct rusage *usage) { return 0; } -int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *pid_out) { +int sys_clone(void *tcb, pid_t *pid_out, void *stack) { HelWord pid = 0; - - void *sp = prepare_stack(entry, user_arg, tcb); - HEL_CHECK(helSyscall2_1(kHelCallSuper + 9, reinterpret_cast(__mlibc_start_thread), - reinterpret_cast(sp), + reinterpret_cast(stack), &pid)); if (pid_out) diff --git a/sysdeps/managarm/x86_64/thread.cpp b/sysdeps/managarm/x86_64/thread.cpp index 48029b54dd..d0d00babfa 100644 --- a/sysdeps/managarm/x86_64/thread.cpp +++ b/sysdeps/managarm/x86_64/thread.cpp @@ -28,17 +28,29 @@ extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) { namespace mlibc { -void *prepare_stack(void *entry, void *user_arg, void *tcb) { - uintptr_t *sp = reinterpret_cast(reinterpret_cast( - mmap(nullptr, 0x1000000, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - ) + 0x1000000); +static constexpr size_t default_stacksize = 0x200000; + +int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size) { + if (!*stack_size) + *stack_size = default_stacksize; + *guard_size = 0; + + uintptr_t *sp; + if (*stack) { + sp = reinterpret_cast(*stack); + } else { + sp = reinterpret_cast(reinterpret_cast( + mmap(nullptr, *stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ) + *stack_size); + } *--sp = reinterpret_cast(tcb); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } //namespace mlibc diff --git a/tests/ansi/sscanf.c b/tests/ansi/sscanf.c index 8576e93b9b..0253552a07 100644 --- a/tests/ansi/sscanf.c +++ b/tests/ansi/sscanf.c @@ -17,5 +17,12 @@ int main() { assert(!strcmp(def, "process_priority")); assert(!strcmp(strparm, "0")); + char buf3[] = "fffffffff100"; + unsigned long y = 0; + sscanf(buf3, "%lx", &y); + assert(y == 0xfffffffff100); + char buf4[] = "410dc000"; + sscanf(buf4, "%lx", &y); + assert(y == 0x410dc000); return 0; } diff --git a/tests/meson.build b/tests/meson.build index 362ce3564c..6067dd4ade 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -31,6 +31,7 @@ posix_test_cases = [ 'pthread_cleanup', 'pthread_kill', 'pthread_key', + 'pthread_attr', 'pwd', 'getaddrinfo', 'getdelim', diff --git a/tests/posix/pthread_attr.c b/tests/posix/pthread_attr.c new file mode 100644 index 0000000000..c7bf327ae0 --- /dev/null +++ b/tests/posix/pthread_attr.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include + +static void test_detachstate() { + pthread_attr_t attr; + assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)); + int detachstate; + assert(!pthread_attr_getdetachstate(&attr, &detachstate)); + assert(detachstate == PTHREAD_CREATE_DETACHED); + assert(pthread_attr_setdetachstate(&attr, 2* (PTHREAD_CREATE_DETACHED + + PTHREAD_CREATE_JOINABLE)) == EINVAL); +} + +static void *stacksize_worker(void *arg) { + size_t default_stacksize = (*(size_t*)arg); + size_t alloc_size = default_stacksize + default_stacksize/2; + void *area = alloca(alloc_size); + // If the allocated stack was not enough this will crash. + *(volatile int*)(area + alloc_size) = 1; + return NULL; +} + +static void test_stacksize() { + pthread_attr_t attr; + assert(!pthread_attr_init(&attr)); + size_t stacksize; + assert(!pthread_attr_getstacksize(&attr, &stacksize)); + assert(!pthread_attr_setstacksize(&attr, stacksize * 2)); + pthread_t thread; + assert(!pthread_create(&thread, &attr, stacksize_worker, &stacksize)); + assert(!pthread_join(thread, NULL)); +} + +static void test_guardsize() { + pthread_attr_t attr; + assert(!pthread_attr_init(&attr)); + assert(!pthread_attr_setguardsize(&attr, 0)); + size_t guardsize; + assert(!pthread_attr_getguardsize(&attr, &guardsize)); + assert(!guardsize); +} + +static void test_scope() { + pthread_attr_t attr; + assert(!pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)); + int scope; + assert(!pthread_attr_getscope(&attr, &scope)); + assert(scope == PTHREAD_SCOPE_SYSTEM); + assert(pthread_attr_setscope(&attr, 2* (PTHREAD_SCOPE_SYSTEM + + PTHREAD_SCOPE_PROCESS)) == EINVAL); +} + +static void test_inheritsched() { + pthread_attr_t attr; + assert(!pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED)); + int inheritsched; + assert(!pthread_attr_getinheritsched(&attr, &inheritsched)); + assert(inheritsched == PTHREAD_INHERIT_SCHED); + assert(pthread_attr_setinheritsched(&attr, 2* (PTHREAD_INHERIT_SCHED + + PTHREAD_EXPLICIT_SCHED)) == EINVAL); +} + +static void test_schedparam() { + pthread_attr_t attr; + struct sched_param init_param = {0}; + assert(!pthread_attr_setschedparam(&attr, &init_param)); + struct sched_param param = {1}; + assert(!pthread_attr_getschedparam(&attr, ¶m)); + assert(param.sched_priority == init_param.sched_priority); +} + +static void test_schedpolicy() { + pthread_attr_t attr; + assert(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO)); + int policy; + assert(!pthread_attr_getschedpolicy(&attr, &policy)); + assert(policy == SCHED_FIFO); + assert(pthread_attr_setinheritsched(&attr, 2* (SCHED_FIFO + SCHED_RR + + SCHED_OTHER)) == EINVAL); +} + +static void *stackaddr_worker(void *arg) { + void *addr = *(void**)arg; + + void *sp; +#if defined(__x86_64__) + asm volatile ("mov %%rsp, %0" : "=r"(sp)); +#elif defined(__aarch64__) + asm volatile ("mov %0, sp" : "=r"(sp)); +#else +# error Unknown architecture +#endif + + // Check if our stack pointer is in a sane range. + assert(addr < sp); + return NULL; +} + +static void test_stackaddr() { + pthread_attr_t attr; + assert(!pthread_attr_init(&attr)); + size_t size; + assert(!pthread_attr_getstacksize(&attr, &size)); + void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(!pthread_attr_setstackaddr(&attr, addr)); + assert(!pthread_attr_setguardsize(&attr, 0)); + void *new_addr; + assert(!pthread_attr_getstackaddr(&attr, &new_addr)); + assert(new_addr == addr); + + pthread_t thread; + assert(!pthread_create(&thread, &attr, stackaddr_worker, &addr)); + assert(!pthread_join(thread, NULL)); +} + +static void test_stack() { + pthread_attr_t attr; + void *stackaddr = (void*)1; + size_t stacksize = PTHREAD_STACK_MIN; + + assert(!pthread_attr_setstack(&attr, stackaddr, stacksize)); + void *new_addr; + size_t new_size; + assert(!pthread_attr_getstack(&attr, &new_addr, &new_size)); + assert(new_addr == stackaddr); + assert(new_size == stacksize); +} + +static void *getattr_worker(void *arg) { + return NULL; +} + +static void test_getattrnp() { + pthread_attr_t attr; + size_t stacksize = PTHREAD_STACK_MIN; + assert(!pthread_attr_init(&attr)); + assert(!pthread_attr_setstacksize(&attr, stacksize)); + + pthread_t thread; + assert(!pthread_create(&thread, &attr, getattr_worker, NULL)); + assert(!pthread_getattr_np(thread, &attr)); + size_t other_stacksize; + assert(!pthread_attr_getstacksize(&attr, &other_stacksize)); + assert(other_stacksize == stacksize); + assert(!pthread_join(thread, NULL)); + + pthread_t own_thread = pthread_self(); + void *stack; + assert(!pthread_getattr_np(own_thread, &attr)); + assert(!pthread_attr_getstack(&attr, &stack, &other_stacksize)); + assert(stack); + assert(other_stacksize); + // Check that we can read from the highest byte returned. + // pthread_getattr_np() should return the lowest byte + // of the stack. + assert(!*(char*)(stack + other_stacksize - 1)); +} + +int main() { + test_detachstate(); + test_stacksize(); + test_guardsize(); + test_scope(); + test_inheritsched(); + test_schedparam(); + test_schedpolicy(); + test_stackaddr(); + test_stack(); + test_getattrnp(); +}