diff --git a/ABI_BREAKS.md b/ABI_BREAKS.md index e33f1fa798..ebf9fa5e33 100644 --- a/ABI_BREAKS.md +++ b/ABI_BREAKS.md @@ -7,3 +7,4 @@ This document lists the ABI breaks that were made in each mlibc major version. - [#452](https://github.com/managarm/mlibc/pull/452): The functions `FD_{CLR,ISSET,SET,ZERO}` were renamed to `__FD_{CLR,ISSET,SET,ZERO}` and replaced by macros to match Wine's assumptions. - [#511](https://github.com/managarm/mlibc/pull/511): Musl's regex engine was added, implementing `regcomp` and `regexec`. This required some changes to the `regex_t` struct. - [#504](https://github.com/managarm/mlibc/pull/504), [#580](https://github.com/managarm/mlibc/pull/580): In both the mlibc and Linux ABIs, a `domainname` member was added to `struct utsname`, which is a glibc extension. +- [#311](https://github.com/managarm/mlibc/pull/311): Added all necessary fields in `pthread_attr_t` required for implementing all `pthread_attr` functions. diff --git a/abis/dripos/errno.h b/abis/dripos/errno.h index 4092449e19..03750240aa 100644 --- a/abis/dripos/errno.h +++ b/abis/dripos/errno.h @@ -144,5 +144,6 @@ #define ELNRNG 113 #define ENOTUNIQ 114 #define ERESTART 115 +#define ENOTSUP 116 #endif diff --git a/options/ansi/generic/stdio-stubs.cpp b/options/ansi/generic/stdio-stubs.cpp index d67af68a8f..9cc778c641 100644 --- a/options/ansi/generic/stdio-stubs.cpp +++ b/options/ansi/generic/stdio-stubs.cpp @@ -487,10 +487,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; } @@ -539,10 +539,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/internal/aarch64-include/mlibc/thread.hpp b/options/internal/aarch64-include/mlibc/thread.hpp index b1dc3697a0..e591113f5b 100644 --- a/options/internal/aarch64-include/mlibc/thread.hpp +++ b/options/internal/aarch64-include/mlibc/thread.hpp @@ -11,4 +11,10 @@ inline Tcb *get_current_tcb() { return reinterpret_cast(ptr); } +inline uintptr_t get_sp() { + uintptr_t sp; + asm ("mov %0, sp" : "=r"(sp)); + return sp; +} + } // namespace mlibc diff --git a/options/internal/include/mlibc/tcb.hpp b/options/internal/include/mlibc/tcb.hpp index e714ccf6a9..a83149de55 100644 --- a/options/internal/include/mlibc/tcb.hpp +++ b/options/internal/include/mlibc/tcb.hpp @@ -109,6 +109,10 @@ struct Tcb { uint64_t generation; }; frg::array *localKeys; + + size_t stackSize; + void *stackAddr; + size_t guardSize; }; // There are a few places where we assume the layout of the TCB: @@ -125,7 +129,7 @@ static_assert(offsetof(Tcb, dtvPointers) == 16); // we need to access specific fields that means that the value in // sysdeps/linux/riscv64/cp_syscall.S needs to be updated whenever // the struct is expanded. -static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) == 56); +static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) == 80); #else #error "Missing architecture specific code." #endif diff --git a/options/internal/riscv64-include/mlibc/thread.hpp b/options/internal/riscv64-include/mlibc/thread.hpp index decbb0ed39..7428b759b7 100644 --- a/options/internal/riscv64-include/mlibc/thread.hpp +++ b/options/internal/riscv64-include/mlibc/thread.hpp @@ -14,4 +14,10 @@ inline Tcb *get_current_tcb() { return tcb; } +inline uintptr_t get_sp() { + uintptr_t sp; + asm ("mv %0, sp" : "=r"(sp)); + return sp; +} + } // namespace mlibc diff --git a/options/internal/x86_64-include/mlibc/thread.hpp b/options/internal/x86_64-include/mlibc/thread.hpp index 2f65a891aa..ed02b677e9 100644 --- a/options/internal/x86_64-include/mlibc/thread.hpp +++ b/options/internal/x86_64-include/mlibc/thread.hpp @@ -11,4 +11,10 @@ inline Tcb *get_current_tcb() { return reinterpret_cast(ptr); } +inline 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 8d8ed40aa1..14e593af90 100644 --- a/options/posix/generic/pthread-stubs.cpp +++ b/options/posix/generic/pthread-stubs.cpp @@ -1,10 +1,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #include @@ -55,12 +58,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; } @@ -68,84 +78,276 @@ 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_setstackaddr(pthread_attr_t *attr, void *stackaddr) { + attr->__mlibc_stackaddr = stackaddr; + return 0; } -int pthread_attr_setguardsize(pthread_attr_t *, size_t) { - __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_getscope(const 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_setscope(pthread_attr_t *, int) { - __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_getschedpolicy(const pthread_attr_t *__restrict, int *__restrict) { - __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_setschedpolicy(pthread_attr_t *, int) { - __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_getschedparam(const pthread_attr_t *__restrict, 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_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getaffinity_np(const pthread_attr_t *__restrict attr, + size_t cpusetsize, cpu_set_t *__restrict cpusetp) { + if (!attr) + return EINVAL; + + if (!attr->__mlibc_cpuset) { + memset(cpusetp, -1, cpusetsize); + return 0; + } + + for (size_t cnt = cpusetsize; cnt < attr->__mlibc_cpusetsize; cnt++) + if (reinterpret_cast(attr->__mlibc_cpuset)[cnt] != '\0') + return ERANGE; + + auto p = memcpy(cpusetp, attr->__mlibc_cpuset, + std::min(cpusetsize, attr->__mlibc_cpusetsize)); + if (cpusetsize > attr->__mlibc_cpusetsize) + memset(p, '\0', cpusetsize - attr->__mlibc_cpusetsize); + + return 0; } -int pthread_attr_getinheritsched(const pthread_attr_t *__restrict, int *__restrict) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_setaffinity_np(pthread_attr_t *__restrict attr, + size_t cpusetsize, const cpu_set_t *__restrict cpusetp) { + if (!attr) + return EINVAL; + + if (!cpusetp || !cpusetsize) { + attr->__mlibc_cpuset = NULL; + attr->__mlibc_cpusetsize = 0; + return 0; + } + + if (attr->__mlibc_cpusetsize != cpusetsize) { + auto newp = realloc(attr->__mlibc_cpuset, cpusetsize); + if (!newp) + return ENOMEM; + + attr->__mlibc_cpuset = static_cast(newp); + attr->__mlibc_cpusetsize = cpusetsize; + } + + memcpy(attr->__mlibc_cpuset, cpusetp, cpusetsize); + return 0; } -int pthread_attr_setinheritsched(pthread_attr_t *, int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict attr, + sigset_t *__restrict sigmask) { + if (!attr) + return EINVAL; + + if (!attr->__mlibc_sigmaskset) { + sigemptyset(sigmask); + return PTHREAD_ATTR_NO_SIGMASK_NP; + } + + *sigmask = attr->__mlibc_sigmask; + + return 0; +} +int pthread_attr_setsigmask_np(pthread_attr_t *__restrict attr, + const sigset_t *__restrict sigmask) { + if (!attr) + return EINVAL; + + if (!sigmask) { + attr->__mlibc_sigmaskset = 0; + return 0; + } + + attr->__mlibc_sigmask = *sigmask; + attr->__mlibc_sigmaskset = 1; + + // Filter out internally used signals. + sigdelset(&attr->__mlibc_sigmask, SIGCANCEL); + + return 0; +} + +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, 256, 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_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; + + if (attr.__mlibc_cpuset) + mlibc::infoLogger() << "pthread_create(): cpuset is ignored!" << frg::endlog; + if (attr.__mlibc_sigmaskset) + mlibc::infoLogger() << "pthread_create(): sigmask is ignored!" << frg::endlog; + + // 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 = reinterpret_cast( + reinterpret_cast(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); @@ -297,21 +499,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 f5934ee596..7c83fdb8a9 100644 --- a/options/posix/include/mlibc/posix-sysdeps.hpp +++ b/options/posix/include/mlibc/posix-sysdeps.hpp @@ -95,7 +95,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 883fb2009b..2c8021d602 100644 --- a/options/posix/include/pthread.h +++ b/options/posix/include/pthread.h @@ -7,6 +7,7 @@ #include #include +#include #include // pthread.h is required to include sched.h and time.h @@ -25,6 +26,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 @@ -65,11 +70,24 @@ extern "C" { #define PTHREAD_INHERIT_SCHED 0 #define PTHREAD_EXPLICIT_SCHED 1 +#define PTHREAD_STACK_MIN 16384 + +#define PTHREAD_ATTR_NO_SIGMASK_NP (-1) + // 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; + cpu_set_t *__mlibc_cpuset; + size_t __mlibc_cpusetsize; + sigset_t __mlibc_sigmask; + int __mlibc_sigmaskset; }; typedef struct __mlibc_threadattr pthread_attr_t; @@ -140,20 +158,37 @@ 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_attr_getaffinity_np(const pthread_attr_t *__restrict, size_t, cpu_set_t *__restrict); +int pthread_attr_setaffinity_np(pthread_attr_t *__restrict, size_t, const cpu_set_t *__restrict); + +int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict, sigset_t *__restrict); +int pthread_attr_setsigmask_np(pthread_attr_t *__restrict, const sigset_t *__restrict); + +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/aero/generic/aero.cpp b/sysdeps/aero/generic/aero.cpp index 43ddb90659..29fb9610a9 100644 --- a/sysdeps/aero/generic/aero.cpp +++ b/sysdeps/aero/generic/aero.cpp @@ -258,8 +258,7 @@ int sys_setegid(gid_t egid) { void sys_yield() UNIMPLEMENTED("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) { auto tid = syscall(SYS_CLONE, (uintptr_t)__mlibc_start_thread, stack); if (tid < 0) { diff --git a/sysdeps/aero/generic/thread.cpp b/sysdeps/aero/generic/thread.cpp index 4eef04a5f6..18f9cacd94 100644 --- a/sysdeps/aero/generic/thread.cpp +++ b/sysdeps/aero/generic/thread.cpp @@ -30,16 +30,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) { - auto sp_bottom = reinterpret_cast( - mmap(nullptr, 0x1000000, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); - uintptr_t *sp = reinterpret_cast(sp_bottom + 0x1000000); - - *--sp = reinterpret_cast(tcb); - *--sp = reinterpret_cast(user_arg); - *--sp = reinterpret_cast(entry); - return sp; +static constexpr size_t default_stacksize = 0x1000000; + +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); + *stack = reinterpret_cast(sp); + return 0; } } // namespace mlibc diff --git a/sysdeps/aero/include/mlibc/thread-entry.hpp b/sysdeps/aero/include/mlibc/thread-entry.hpp index ed5d7d30e7..a241479d45 100644 --- a/sysdeps/aero/include/mlibc/thread-entry.hpp +++ b/sysdeps/aero/include/mlibc/thread-entry.hpp @@ -6,5 +6,6 @@ extern "C" void __mlibc_start_thread(void); 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); +int prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, + size_t *guard_size); } // namespace mlibc diff --git a/sysdeps/dripos/generic/generic.cpp b/sysdeps/dripos/generic/generic.cpp index 2a0c82880f..562c6efdc8 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 1a7eaea83e..8f15ff1a88 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){ 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 d1ded08763..39f7d53afe 100644 --- a/sysdeps/linux/generic/sysdeps.cpp +++ b/sysdeps/linux/generic/sysdeps.cpp @@ -451,9 +451,7 @@ int sys_sysinfo(struct sysinfo *info) { 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/generic/thread.cpp b/sysdeps/linux/generic/thread.cpp index 94d03b6efe..d4477440fb 100644 --- a/sysdeps/linux/generic/thread.cpp +++ b/sysdeps/linux/generic/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,36 @@ 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); + *guard_size = 0; + } 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; + map += *stack_size + *guard_size; + } + + auto sp = reinterpret_cast(map); *--sp = reinterpret_cast(user_arg); *--sp = reinterpret_cast(entry); - return sp; + *stack = reinterpret_cast(sp); + return 0; } } // namespace mlibc diff --git a/sysdeps/linux/riscv64/cp_syscall.S b/sysdeps/linux/riscv64/cp_syscall.S index d50cfbb192..b3b4c96b67 100644 --- a/sysdeps/linux/riscv64/cp_syscall.S +++ b/sysdeps/linux/riscv64/cp_syscall.S @@ -13,7 +13,7 @@ __mlibc_do_asm_cp_syscall: mv a4, a5 mv a5, a6 ld a6, -8(sp) // a7 - lw t0, -56(tp) // Tcb::cancelBits. See asserts in tcb.hpp. + lw t0, -80(tp) // Tcb::cancelBits. See asserts in tcb.hpp. __mlibc_syscall_begin: // tcbCancelEnableBit && tcbCancelTriggerBit li t1, (1 << 0) | (1 << 2) 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 3ff0321c83..14d62a0434 100644 --- a/sysdeps/managarm/generic/fork-exec.cpp +++ b/sysdeps/managarm/generic/fork-exec.cpp @@ -623,14 +623,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 e49c63dfa9..58ac3dd6ac 100644 --- a/tests/ansi/sscanf.c +++ b/tests/ansi/sscanf.c @@ -3,29 +3,48 @@ #include int main() { - int x = 0; - char buf1[] = "12345"; - sscanf(buf1, "%d", &x); - assert(x == 12345); + { + int x = 0; + char buf[] = "12345"; + sscanf(buf, "%d", &x); + assert(x == 12345); + } - char c; - int n1; - int n2; - char buf3[] = "z$ 7 5 440";; - int count = sscanf(buf3, "%*c%c %d %*d %d", &c, &n1, &n2); - assert(count == 3); - assert(c == '$'); - assert(n1 == 7); - assert(n2 == 440); + { + char c; + int n1; + int n2; + char buf[] = "z$ 7 5 440";; + int count = sscanf(buf, "%*c%c %d %*d %d", &c, &n1, &n2); + assert(count == 3); + assert(c == '$'); + assert(n1 == 7); + assert(n2 == 440); + } - // From dsda-doom - char buf2[] = "process_priority 0\n"; - char def[80], strparm[128]; - memset(def, '!', 80); - memset(strparm, '!', 128); - sscanf(buf2, "%s %[^\n]\n", def, strparm); - assert(!strcmp(def, "process_priority")); - assert(!strcmp(strparm, "0")); + { + // From dsda-doom + char buf[] = "process_priority 0\n"; + char def[80], strparm[128]; + memset(def, '!', 80); + memset(strparm, '!', 128); + sscanf(buf, "%s %[^\n]\n", def, strparm); + assert(!strcmp(def, "process_priority")); + assert(!strcmp(strparm, "0")); + } + { + char buf[] = "fffffffff100"; + unsigned long y = 0; + sscanf(buf, "%lx", &y); + assert(y == 0xfffffffff100); + } + + { + char buf[] = "410dc000"; + unsigned long y = 0; + sscanf(buf, "%lx", &y); + assert(y == 0x410dc000); + } return 0; } diff --git a/tests/meson.build b/tests/meson.build index 16ac1b2cb6..3ea1d05009 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -34,6 +34,7 @@ all_test_cases = [ 'posix/pthread_kill', 'posix/pthread_key', 'posix/pthread_thread_local', + 'posix/pthread_attr', 'posix/pwd', 'posix/fdopen', 'posix/getaddrinfo', @@ -87,6 +88,7 @@ host_libc_excluded_test_cases = [ ] host_libc_noasan_test_cases = [ 'posix/pthread_cancel', + 'posix/pthread_attr', # does some stack overflowing to check stack size 'posix/posix_memalign', 'posix/search', # requires tdelete (#351) ] diff --git a/tests/posix/pthread_attr.c b/tests/posix/pthread_attr.c new file mode 100644 index 0000000000..ca6079efd6 --- /dev/null +++ b/tests/posix/pthread_attr.c @@ -0,0 +1,221 @@ +#include +#include +#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)); +#elif defined (__riscv) + asm volatile ("mv %0, sp" : "=r"(sp)); +#else +# error Unknown architecture +#endif + + // Check if our stack pointer is in a sane range. + assert(addr >= sp); + return NULL; +} +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +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) + size; + 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)); +} +#pragma GCC diagnostic pop + +#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) +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); +} +#endif + +static void test_affinity() { + pthread_attr_t attr; + cpu_set_t set = {0}; + assert(!pthread_attr_init(&attr)); + assert(!pthread_attr_setaffinity_np(&attr, 1, &set)); + + cpu_set_t other_set = {0}; + assert(!pthread_attr_getaffinity_np(&attr, 1, &set)); + + assert(!memcmp(&set, &other_set, sizeof(cpu_set_t))); +} + +#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) +static void test_sigmask() { + pthread_attr_t attr; + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGCANCEL); + + assert(!pthread_attr_init(&attr)); + assert(!pthread_attr_setsigmask_np(&attr, &set)); + + sigset_t other_set; + sigemptyset(&other_set); + + assert(!pthread_attr_getsigmask_np(&attr, &other_set)); + + assert(sigismember(&other_set, SIGUSR1)); + // Test whether internal signals get filtered properly. + assert(!sigismember(&other_set, SIGCANCEL)); +} + +static void *getattr_worker(void *arg) { + (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)); +} +#endif + +int main() { + test_detachstate(); + test_stacksize(); + test_guardsize(); + test_scope(); + test_inheritsched(); + test_schedparam(); + test_schedpolicy(); + test_stackaddr(); + test_affinity(); +#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) + test_stack(); + test_sigmask(); + test_getattrnp(); +#endif +}