From afe7a231ef399c29ae86ecfda846610d8241ffcebb0870ea1547345b34ed3b60 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Mon, 18 Feb 2019 13:35:23 +0000 Subject: [PATCH] Accepting request 677136 from home:Andreas_Schwab:Factory - pthread-rwlock-trylock-stalls.patch: nptl: Fix pthread_rwlock_try*lock stalls (BZ #23844) - arm-systemtap-probe-constraint.patch: arm: Use "nr" constraint for Systemtap probes (BZ #24164) - pthread-mutex-barrier.patch: Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock (BZ #24180) - fork-handler-lock.patch: nptl: Avoid fork handler lock for async-signal-safe fork (BZ #24161) - pthread-join-probe.patch: nptl: Fix invalid Systemtap probe in pthread_join (BZ #24211) - riscv-clone-unwind.patch: RISC-V: Fix elfutils testsuite unwind failures (BZ #24040) OBS-URL: https://build.opensuse.org/request/show/677136 OBS-URL: https://build.opensuse.org/package/show/Base:System/glibc?expand=0&rev=518 --- arm-systemtap-probe-constraint.patch | 74 ++++ fork-handler-lock.patch | 91 ++++ glibc.changes | 16 + glibc.spec | 23 +- pthread-join-probe.patch | 36 ++ pthread-mutex-barrier.patch | 173 ++++++++ pthread-rwlock-trylock-stalls.patch | 616 +++++++++++++++++++++++++++ riscv-clone-unwind.patch | 119 ++++++ 8 files changed, 1146 insertions(+), 2 deletions(-) create mode 100644 arm-systemtap-probe-constraint.patch create mode 100644 fork-handler-lock.patch create mode 100644 pthread-join-probe.patch create mode 100644 pthread-mutex-barrier.patch create mode 100644 pthread-rwlock-trylock-stalls.patch create mode 100644 riscv-clone-unwind.patch diff --git a/arm-systemtap-probe-constraint.patch b/arm-systemtap-probe-constraint.patch new file mode 100644 index 0000000..0754b36 --- /dev/null +++ b/arm-systemtap-probe-constraint.patch @@ -0,0 +1,74 @@ +2019-02-05 Florian Weimer + + [BZ #24164] + arm: Use "nr" constraint for Systemtap probes, to avoid the + compiler using memory operands for constants, due to the "o" + alternative in the default "nor" constraint. + * include/stap-probe.h [USE_STAP_PROBE]: Include + + * sysdeps/generic/stap-probe-machine.h: New file. + * sysdeps/arm/stap-probe-machine.h: Likewise. + +Index: glibc-2.29/include/stap-probe.h +=================================================================== +--- glibc-2.29.orig/include/stap-probe.h ++++ glibc-2.29/include/stap-probe.h +@@ -21,6 +21,7 @@ + + #ifdef USE_STAP_PROBE + ++# include + # include + + /* Our code uses one macro LIBC_PROBE (name, n, arg1, ..., argn). +Index: glibc-2.29/sysdeps/arm/stap-probe-machine.h +=================================================================== +--- /dev/null ++++ glibc-2.29/sysdeps/arm/stap-probe-machine.h +@@ -0,0 +1,22 @@ ++/* Macros for customizing Systemtap . Arm version. ++ Copyright (C) 2019 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++/* The default "nor" constraint produces unparseable memory references ++ for constants. Omit the problematic "o" constraint. See bug 24164 ++ and GCC PR 89146. */ ++#define STAP_SDT_ARG_CONSTRAINT nr +Index: glibc-2.29/sysdeps/generic/stap-probe-machine.h +=================================================================== +--- /dev/null ++++ glibc-2.29/sysdeps/generic/stap-probe-machine.h +@@ -0,0 +1,19 @@ ++/* Macros for customizing Systemtap . Generic version. ++ Copyright (C) 2019 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++/* By default, there are no customizations. */ diff --git a/fork-handler-lock.patch b/fork-handler-lock.patch new file mode 100644 index 0000000..d387ec2 --- /dev/null +++ b/fork-handler-lock.patch @@ -0,0 +1,91 @@ +2019-02-08 Florian Weimer + + [BZ #24161] + * sysdeps/nptl/fork.h (__run_fork_handlers): Add multiple_threads + argument. + * nptl/register-atfork.c (__run_fork_handlers): Only perform + locking if the new do_locking argument is true. + * sysdeps/nptl/fork.c (__libc_fork): Pass multiple_threads to + __run_fork_handlers. + +Index: glibc-2.29/nptl/register-atfork.c +=================================================================== +--- glibc-2.29.orig/nptl/register-atfork.c ++++ glibc-2.29/nptl/register-atfork.c +@@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle) + } + + void +-__run_fork_handlers (enum __run_fork_handler_type who) ++__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking) + { + struct fork_handler *runp; + + if (who == atfork_run_prepare) + { +- lll_lock (atfork_lock, LLL_PRIVATE); ++ if (do_locking) ++ lll_lock (atfork_lock, LLL_PRIVATE); + size_t sl = fork_handler_list_size (&fork_handlers); + for (size_t i = sl; i > 0; i--) + { +@@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_han + else if (who == atfork_run_parent && runp->parent_handler) + runp->parent_handler (); + } +- lll_unlock (atfork_lock, LLL_PRIVATE); ++ if (do_locking) ++ lll_unlock (atfork_lock, LLL_PRIVATE); + } + } + +Index: glibc-2.29/sysdeps/nptl/fork.c +=================================================================== +--- glibc-2.29.orig/sysdeps/nptl/fork.c ++++ glibc-2.29/sysdeps/nptl/fork.c +@@ -55,7 +55,7 @@ __libc_fork (void) + but our current fork implementation is not. */ + bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads); + +- __run_fork_handlers (atfork_run_prepare); ++ __run_fork_handlers (atfork_run_prepare, multiple_threads); + + /* If we are not running multiple threads, we do not have to + preserve lock state. If fork runs from a signal handler, only +@@ -134,7 +134,7 @@ __libc_fork (void) + __rtld_lock_initialize (GL(dl_load_lock)); + + /* Run the handlers registered for the child. */ +- __run_fork_handlers (atfork_run_child); ++ __run_fork_handlers (atfork_run_child, multiple_threads); + } + else + { +@@ -149,7 +149,7 @@ __libc_fork (void) + } + + /* Run the handlers registered for the parent. */ +- __run_fork_handlers (atfork_run_parent); ++ __run_fork_handlers (atfork_run_parent, multiple_threads); + } + + return pid; +Index: glibc-2.29/sysdeps/nptl/fork.h +=================================================================== +--- glibc-2.29.orig/sysdeps/nptl/fork.h ++++ glibc-2.29/sysdeps/nptl/fork.h +@@ -52,9 +52,11 @@ enum __run_fork_handler_type + - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal + lock. + - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal +- lock. */ +-extern void __run_fork_handlers (enum __run_fork_handler_type who) +- attribute_hidden; ++ lock. ++ ++ Perform locking only if DO_LOCKING. */ ++extern void __run_fork_handlers (enum __run_fork_handler_type who, ++ _Bool do_locking) attribute_hidden; + + /* C library side function to register new fork handlers. */ + extern int __register_atfork (void (*__prepare) (void), diff --git a/glibc.changes b/glibc.changes index 57b7e2c..eb7b4ae 100644 --- a/glibc.changes +++ b/glibc.changes @@ -1,3 +1,19 @@ +------------------------------------------------------------------- +Mon Feb 18 09:28:08 UTC 2019 - schwab@suse.de + +- pthread-rwlock-trylock-stalls.patch: nptl: Fix pthread_rwlock_try*lock + stalls (BZ #23844) +- arm-systemtap-probe-constraint.patch: arm: Use "nr" constraint for + Systemtap probes (BZ #24164) +- pthread-mutex-barrier.patch: Add compiler barriers around modifications + of the robust mutex list for pthread_mutex_trylock (BZ #24180) +- fork-handler-lock.patch: nptl: Avoid fork handler lock for + async-signal-safe fork (BZ #24161) +- pthread-join-probe.patch: nptl: Fix invalid Systemtap probe in + pthread_join (BZ #24211) +- riscv-clone-unwind.patch: RISC-V: Fix elfutils testsuite unwind failures + (BZ #24040) + ------------------------------------------------------------------- Fri Feb 1 10:34:39 UTC 2019 - schwab@suse.de diff --git a/glibc.spec b/glibc.spec index ab88571..4d694fe 100644 --- a/glibc.spec +++ b/glibc.spec @@ -270,6 +270,18 @@ Patch306: glibc-fix-double-loopback.diff ### # Patches from upstream ### +# PATCH-FIX-UPSTREAM nptl: Fix pthread_rwlock_try*lock stalls (BZ #23844) +Patch1000: pthread-rwlock-trylock-stalls.patch +# PATCH-FIX-UPSTREAM arm: Use "nr" constraint for Systemtap probes (BZ #24164) +Patch1001: arm-systemtap-probe-constraint.patch +# PATCH-FIX-UPSTREAM Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock (BZ #24180) +Patch1002: pthread-mutex-barrier.patch +# PATCH-FIX-UPSTREAM nptl: Avoid fork handler lock for async-signal-safe fork (BZ #24161) +Patch1003: fork-handler-lock.patch +# PATCH-FIX-UPSTREAM nptl: Fix invalid Systemtap probe in pthread_join (BZ #24211) +Patch1004: pthread-join-probe.patch +# PATCH-FIX-UPSTREAM RISC-V: Fix elfutils testsuite unwind failures (BZ #24040) +Patch1005: riscv-clone-unwind.patch ### # Patches awaiting upstream approval @@ -479,6 +491,13 @@ makedb: A program to create a database for nss %patch304 -p1 %patch306 -p1 +%patch1000 -p1 +%patch1001 -p1 +%patch1002 -p1 +%patch1003 -p1 +%patch1004 -p1 +%patch1005 -p1 + %patch2000 -p1 %patch2004 -p1 %patch2005 -p1 @@ -850,9 +869,9 @@ cc-base/elf/ldconfig -vn $destdir # this will not work if we generate them in parallel. # thus we need to run fdupes on /usr/lib/locale/ # Still, on my system this is a speed advantage: - # non-parallel build for install-locale-files: 9:34mins + # non-parallel build for install-locales: 9:34mins # parallel build with fdupes: 7:08mins - make %{?_smp_mflags} install_root=%{buildroot} localedata/install-locale-files + make %{?_smp_mflags} install_root=%{buildroot} localedata/install-locales # Avoid hardlinks across subpackages mv %{buildroot}/usr/lib/locale/{en_US,C}.utf8 . %fdupes %{buildroot}/usr/lib/locale diff --git a/pthread-join-probe.patch b/pthread-join-probe.patch new file mode 100644 index 0000000..f22ad33 --- /dev/null +++ b/pthread-join-probe.patch @@ -0,0 +1,36 @@ +2019-02-15 Florian Weimer + + [BZ #24211] + * nptl/pthread_join_common.c (__pthread_timedjoin_ex): Do not read + pd->result after the thread descriptor has been freed. + +Index: glibc-2.29/nptl/pthread_join_common.c +=================================================================== +--- glibc-2.29.orig/nptl/pthread_join_common.c ++++ glibc-2.29/nptl/pthread_join_common.c +@@ -86,6 +86,7 @@ __pthread_timedjoin_ex (pthread_t thread + pthread_cleanup_pop (0); + } + ++ void *pd_result = pd->result; + if (__glibc_likely (result == 0)) + { + /* We mark the thread as terminated and as joined. */ +@@ -93,7 +94,7 @@ __pthread_timedjoin_ex (pthread_t thread + + /* Store the return value if the caller is interested. */ + if (thread_return != NULL) +- *thread_return = pd->result; ++ *thread_return = pd_result; + + /* Free the TCB. */ + __free_tcb (pd); +@@ -101,7 +102,7 @@ __pthread_timedjoin_ex (pthread_t thread + else + pd->joinid = NULL; + +- LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd->result); ++ LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd_result); + + return result; + } diff --git a/pthread-mutex-barrier.patch b/pthread-mutex-barrier.patch new file mode 100644 index 0000000..bd862d7 --- /dev/null +++ b/pthread-mutex-barrier.patch @@ -0,0 +1,173 @@ +2019-02-07 Stefan Liebler + + [BZ #24180] + * nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock): + Add compiler barriers and comments. + +Index: glibc-2.29/nptl/pthread_mutex_trylock.c +=================================================================== +--- glibc-2.29.orig/nptl/pthread_mutex_trylock.c ++++ glibc-2.29/nptl/pthread_mutex_trylock.c +@@ -94,6 +94,9 @@ __pthread_mutex_trylock (pthread_mutex_t + case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP: + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, + &mutex->__data.__list.__next); ++ /* We need to set op_pending before starting the operation. Also ++ see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); + + oldval = mutex->__data.__lock; + do +@@ -119,7 +122,12 @@ __pthread_mutex_trylock (pthread_mutex_t + /* But it is inconsistent unless marked otherwise. */ + mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT; + ++ /* We must not enqueue the mutex before we have acquired it. ++ Also see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); + ENQUEUE_MUTEX (mutex); ++ /* We need to clear op_pending after we enqueue the mutex. */ ++ __asm ("" ::: "memory"); + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + /* Note that we deliberately exist here. If we fall +@@ -135,6 +143,8 @@ __pthread_mutex_trylock (pthread_mutex_t + int kind = PTHREAD_MUTEX_TYPE (mutex); + if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP) + { ++ /* We do not need to ensure ordering wrt another memory ++ access. Also see comments at ENQUEUE_MUTEX. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, + NULL); + return EDEADLK; +@@ -142,6 +152,8 @@ __pthread_mutex_trylock (pthread_mutex_t + + if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP) + { ++ /* We do not need to ensure ordering wrt another memory ++ access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, + NULL); + +@@ -160,6 +172,9 @@ __pthread_mutex_trylock (pthread_mutex_t + id, 0); + if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0) + { ++ /* We haven't acquired the lock as it is already acquired by ++ another owner. We do not need to ensure ordering wrt another ++ memory access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + return EBUSY; +@@ -173,13 +188,20 @@ __pthread_mutex_trylock (pthread_mutex_t + if (oldval == id) + lll_unlock (mutex->__data.__lock, + PTHREAD_ROBUST_MUTEX_PSHARED (mutex)); ++ /* FIXME This violates the mutex destruction requirements. See ++ __pthread_mutex_unlock_full. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + return ENOTRECOVERABLE; + } + } + while ((oldval & FUTEX_OWNER_DIED) != 0); + ++ /* We must not enqueue the mutex before we have acquired it. ++ Also see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); + ENQUEUE_MUTEX (mutex); ++ /* We need to clear op_pending after we enqueue the mutex. */ ++ __asm ("" ::: "memory"); + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + mutex->__data.__owner = id; +@@ -211,10 +233,15 @@ __pthread_mutex_trylock (pthread_mutex_t + } + + if (robust) +- /* Note: robust PI futexes are signaled by setting bit 0. */ +- THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, +- (void *) (((uintptr_t) &mutex->__data.__list.__next) +- | 1)); ++ { ++ /* Note: robust PI futexes are signaled by setting bit 0. */ ++ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, ++ (void *) (((uintptr_t) &mutex->__data.__list.__next) ++ | 1)); ++ /* We need to set op_pending before starting the operation. Also ++ see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); ++ } + + oldval = mutex->__data.__lock; + +@@ -223,12 +250,16 @@ __pthread_mutex_trylock (pthread_mutex_t + { + if (kind == PTHREAD_MUTEX_ERRORCHECK_NP) + { ++ /* We do not need to ensure ordering wrt another memory ++ access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + return EDEADLK; + } + + if (kind == PTHREAD_MUTEX_RECURSIVE_NP) + { ++ /* We do not need to ensure ordering wrt another memory ++ access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + /* Just bump the counter. */ +@@ -250,6 +281,9 @@ __pthread_mutex_trylock (pthread_mutex_t + { + if ((oldval & FUTEX_OWNER_DIED) == 0) + { ++ /* We haven't acquired the lock as it is already acquired by ++ another owner. We do not need to ensure ordering wrt another ++ memory access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + return EBUSY; +@@ -270,6 +304,9 @@ __pthread_mutex_trylock (pthread_mutex_t + if (INTERNAL_SYSCALL_ERROR_P (e, __err) + && INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK) + { ++ /* The kernel has not yet finished the mutex owner death. ++ We do not need to ensure ordering wrt another memory ++ access. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + return EBUSY; +@@ -287,7 +324,12 @@ __pthread_mutex_trylock (pthread_mutex_t + /* But it is inconsistent unless marked otherwise. */ + mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT; + ++ /* We must not enqueue the mutex before we have acquired it. ++ Also see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); + ENQUEUE_MUTEX (mutex); ++ /* We need to clear op_pending after we enqueue the mutex. */ ++ __asm ("" ::: "memory"); + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + + /* Note that we deliberately exit here. If we fall +@@ -310,13 +352,20 @@ __pthread_mutex_trylock (pthread_mutex_t + PTHREAD_ROBUST_MUTEX_PSHARED (mutex)), + 0, 0); + ++ /* To the kernel, this will be visible after the kernel has ++ acquired the mutex in the syscall. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + return ENOTRECOVERABLE; + } + + if (robust) + { ++ /* We must not enqueue the mutex before we have acquired it. ++ Also see comments at ENQUEUE_MUTEX. */ ++ __asm ("" ::: "memory"); + ENQUEUE_MUTEX_PI (mutex); ++ /* We need to clear op_pending after we enqueue the mutex. */ ++ __asm ("" ::: "memory"); + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); + } + diff --git a/pthread-rwlock-trylock-stalls.patch b/pthread-rwlock-trylock-stalls.patch new file mode 100644 index 0000000..024a9cc --- /dev/null +++ b/pthread-rwlock-trylock-stalls.patch @@ -0,0 +1,616 @@ +2019-01-31 Carlos O'Donell + Torvald Riegel + Rik Prohaska + + [BZ# 23844] + * nptl/Makefile (tests): Add tst-rwlock-tryrdlock-stall, and + tst-rwlock-trywrlock-stall. + * nptl/pthread_rwlock_tryrdlock.c (__pthread_rwlock_tryrdlock): + Wake waiters if PTHREAD_RWLOCK_FUTEX_USED is set. + * nptl/pthread_rwlock_trywrlock.c (__pthread_rwlock_trywrlock): + Set __wrphase_fute to 1 only if we started the write phase. + * nptl/tst-rwlock-tryrdlock-stall.c: New file. + * nptl/tst-rwlock-trywrlock-stall.c: New file. + * support/Makefile (libsupport-routines): Add xpthread_rwlock_destroy. + * support/xpthread_rwlock_destroy.c: New file. + * support/xthread.h: Declare xpthread_rwlock_destroy. + +Index: glibc-2.29/nptl/Makefile +=================================================================== +--- glibc-2.29.orig/nptl/Makefile ++++ glibc-2.29/nptl/Makefile +@@ -319,7 +319,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 ts + tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ + tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \ + tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \ +- tst-rwlock-pwn ++ tst-rwlock-pwn \ ++ tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall + + tests-internal := tst-rwlock19 tst-rwlock20 \ + tst-sem11 tst-sem12 tst-sem13 \ +Index: glibc-2.29/nptl/pthread_rwlock_tryrdlock.c +=================================================================== +--- glibc-2.29.orig/nptl/pthread_rwlock_tryrdlock.c ++++ glibc-2.29/nptl/pthread_rwlock_tryrdlock.c +@@ -94,15 +94,22 @@ __pthread_rwlock_tryrdlock (pthread_rwlo + /* Same as in __pthread_rwlock_rdlock_full: + We started the read phase, so we are also responsible for + updating the write-phase futex. Relaxed MO is sufficient. +- Note that there can be no other reader that we have to wake +- because all other readers will see the read phase started by us +- (or they will try to start it themselves); if a writer started +- the read phase, we cannot have started it. Furthermore, we +- cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will +- overwrite the value set by the most recent writer (or the readers +- before it in case of explicit hand-over) and we know that there +- are no waiting readers. */ +- atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0); ++ We have to do the same steps as a writer would when handing over the ++ read phase to use because other readers cannot distinguish between ++ us and the writer. ++ Note that __pthread_rwlock_tryrdlock callers will not have to be ++ woken up because they will either see the read phase started by us ++ or they will try to start it themselves; however, callers of ++ __pthread_rwlock_rdlock_full just increase the reader count and then ++ check what state the lock is in, so they cannot distinguish between ++ us and a writer that acquired and released the lock in the ++ meantime. */ ++ if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0) ++ & PTHREAD_RWLOCK_FUTEX_USED) != 0) ++ { ++ int private = __pthread_rwlock_get_private (rwlock); ++ futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private); ++ } + } + + return 0; +Index: glibc-2.29/nptl/pthread_rwlock_trywrlock.c +=================================================================== +--- glibc-2.29.orig/nptl/pthread_rwlock_trywrlock.c ++++ glibc-2.29/nptl/pthread_rwlock_trywrlock.c +@@ -46,8 +46,15 @@ __pthread_rwlock_trywrlock (pthread_rwlo + &rwlock->__data.__readers, &r, + r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED)) + { ++ /* We have become the primary writer and we cannot have shared ++ the PTHREAD_RWLOCK_FUTEX_USED flag with someone else, so we ++ can simply enable blocking (see full wrlock code). */ + atomic_store_relaxed (&rwlock->__data.__writers_futex, 1); +- atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); ++ /* If we started a write phase, we need to enable readers to ++ wait. If we did not, we must not change it because other threads ++ may have set the PTHREAD_RWLOCK_FUTEX_USED in the meantime. */ ++ if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) ++ atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); + atomic_store_relaxed (&rwlock->__data.__cur_writer, + THREAD_GETMEM (THREAD_SELF, tid)); + return 0; +Index: glibc-2.29/nptl/tst-rwlock-tryrdlock-stall.c +=================================================================== +--- /dev/null ++++ glibc-2.29/nptl/tst-rwlock-tryrdlock-stall.c +@@ -0,0 +1,355 @@ ++/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls. ++ Copyright (C) 2019 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++/* For a full analysis see comment: ++ https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14 ++ ++ Provided here for reference: ++ ++ --- Analysis of pthread_rwlock_tryrdlock() stall --- ++ A read lock begins to execute. ++ ++ In __pthread_rwlock_rdlock_full: ++ ++ We can attempt a read lock, but find that the lock is ++ in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit ++ is set), and the lock is held by a primary writer ++ (PTHREAD_RWLOCK_WRLOCKED is set). In this case we must ++ wait for explicit hand over from the writer to us or ++ one of the other waiters. The read lock threads are ++ about to execute: ++ ++ 341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers, ++ 342 (1 << PTHREAD_RWLOCK_READER_SHIFT)) ++ 343 + (1 << PTHREAD_RWLOCK_READER_SHIFT)); ++ ++ An unlock beings to execute. ++ ++ Then in __pthread_rwlock_wrunlock: ++ ++ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers); ++ ... ++ 549 while (!atomic_compare_exchange_weak_release ++ 550 (&rwlock->__data.__readers, &r, ++ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED) ++ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0 ++ 553 : PTHREAD_RWLOCK_WRPHASE)))) ++ 554 { ++ ... ++ 556 } ++ ++ We clear PTHREAD_RWLOCK_WRLOCKED, and if there are ++ no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE. ++ ++ Back in the read lock. ++ ++ The read lock adjusts __readres as above. ++ ++ 383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0 ++ 384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0) ++ 385 { ++ ... ++ 390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r, ++ 391 r ^ PTHREAD_RWLOCK_WRPHASE)) ++ 392 { ++ ++ And then attemps to start the read phase. ++ ++ Assume there happens to be a tryrdlock at this point, noting ++ that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE ++ is 1. So the try lock attemps to start the read phase. ++ ++ In __pthread_rwlock_tryrdlock: ++ ++ 44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) ++ 45 { ++ ... ++ 49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0) ++ 50 && (rwlock->__data.__flags ++ 51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)) ++ 52 return EBUSY; ++ 53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT); ++ 54 } ++ ... ++ 89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, ++ 90 &r, rnew)); ++ ++ And succeeds. ++ ++ Back in the write unlock: ++ ++ 557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0) ++ 558 { ++ ... ++ 563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0) ++ 564 & PTHREAD_RWLOCK_FUTEX_USED) != 0) ++ 565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private); ++ 566 } ++ ++ We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero ++ and don't wake anyone. This is OK because we handed ++ over to the trylock. It will be the trylock's responsibility ++ to wake any waiters. ++ ++ Back in the read lock: ++ ++ The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because ++ the __readers value was adjusted by the trylock, and so it falls through ++ to waiting on the lock for explicit handover from either a new writer ++ or a new reader. ++ ++ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, ++ 449 1 | PTHREAD_RWLOCK_FUTEX_USED, ++ 450 abstime, private); ++ ++ We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex ++ is in use. ++ ++ At this point we have readers waiting on the read lock ++ to unlock. The wrlock is done. The trylock is finishing ++ the installation of the read phase. ++ ++ 92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0) ++ 93 { ++ ... ++ 105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0); ++ 106 } ++ ++ The trylock does note that we were the one that ++ installed the read phase, but the comments are not ++ correct, the execution ordering above shows that ++ readers might indeed be waiting, and they are. ++ ++ The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED, ++ and the waiting reader is never worken becuase as noted ++ above it is conditional on the futex being used. ++ ++ The solution is for the trylock thread to inspect ++ PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers. ++ ++ --- Analysis of pthread_rwlock_trywrlock() stall --- ++ ++ A write lock begins to execute, takes the write lock, ++ and then releases the lock... ++ ++ In pthread_rwlock_wrunlock(): ++ ++ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers); ++ ... ++ 549 while (!atomic_compare_exchange_weak_release ++ 550 (&rwlock->__data.__readers, &r, ++ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED) ++ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0 ++ 553 : PTHREAD_RWLOCK_WRPHASE)))) ++ 554 { ++ ... ++ 556 } ++ ++ ... leaving it in the write phase with zero readers ++ (the case where we leave the write phase in place ++ during a write unlock). ++ ++ A write trylock begins to execute. ++ ++ In __pthread_rwlock_trywrlock: ++ ++ 40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0) ++ 41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0) ++ 42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0)))) ++ 43 { ++ ++ The lock is not locked. ++ ++ There are no readers. ++ ++ 45 if (atomic_compare_exchange_weak_acquire ( ++ 46 &rwlock->__data.__readers, &r, ++ 47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED)) ++ ++ We atomically install the write phase and we take the ++ exclusive write lock. ++ ++ 48 { ++ 49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1); ++ ++ We get this far. ++ ++ A reader lock begins to execute. ++ ++ In pthread_rwlock_rdlock: ++ ++ 437 for (;;) ++ 438 { ++ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) ++ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) ++ 441 { ++ 442 int private = __pthread_rwlock_get_private (rwlock); ++ 443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) ++ 444 && (!atomic_compare_exchange_weak_relaxed ++ 445 (&rwlock->__data.__wrphase_futex, ++ 446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))) ++ 447 continue; ++ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, ++ 449 1 | PTHREAD_RWLOCK_FUTEX_USED, ++ 450 abstime, private); ++ ++ We are in a write phase, so the while() on line 439 is true. ++ ++ The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set ++ since this is the first reader to lock. ++ ++ The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED ++ on the expectation that this reader will be woken during ++ the handoff. ++ ++ Back in pthread_rwlock_trywrlock: ++ ++ 50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); ++ 51 atomic_store_relaxed (&rwlock->__data.__cur_writer, ++ 52 THREAD_GETMEM (THREAD_SELF, tid)); ++ 53 return 0; ++ 54 } ++ ... ++ 57 } ++ ++ We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED, ++ and so in the unlock we will not awaken the waiting reader. ++ ++ The solution to this is to realize that if we did not start the write ++ phase we need not write 1 or any other value to __wrphase_futex. ++ This ensures that any readers (which saw __wrphase_futex != 0) can ++ set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to ++ wake them. ++ ++ If we installed the write phase then all other readers are looping ++ here: ++ ++ In __pthread_rwlock_rdlock_full: ++ ++ 437 for (;;) ++ 438 { ++ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) ++ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) ++ 441 { ++ ... ++ 508 } ++ ++ waiting for the write phase to be installed or removed before they ++ can begin waiting on __wrphase_futex (part of the algorithm), or ++ taking a concurrent read lock, and thus we can safely write 1 to ++ __wrphase_futex. ++ ++ If we did not install the write phase then the readers may already ++ be waiting on the futex, the original writer wrote 1 to __wrphase_futex ++ as part of starting the write phase, and we cannot also write 1 ++ without loosing the PTHREAD_RWLOCK_FUTEX_USED bit. ++ ++ --- ++ ++ Summary for the pthread_rwlock_tryrdlock() stall: ++ ++ The stall is caused by pthread_rwlock_tryrdlock failing to check ++ that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex ++ and then waking the futex. ++ ++ The fix for bug 23844 ensures that waiters on __wrphase_futex are ++ correctly woken. Before the fix the test stalls as readers can ++ wait forever on __wrphase_futex. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* We need only one lock to reproduce the issue. We will need multiple ++ threads to get the exact case where we have a read, try, and unlock ++ all interleaving to produce the case where the readers are waiting ++ and the try fails to wake them. */ ++pthread_rwlock_t onelock; ++ ++/* The number of threads is arbitrary but empirically chosen to have ++ enough threads that we see the condition where waiting readers are ++ not woken by a successful tryrdlock. */ ++#define NTHREADS 32 ++ ++_Atomic int do_exit; ++ ++void * ++run_loop (void *arg) ++{ ++ int i = 0, ret; ++ while (!do_exit) ++ { ++ /* Arbitrarily choose if we are the writer or reader. Choose a ++ high enough ratio of readers to writers to make it likely ++ that readers block (and eventually are susceptable to ++ stalling). ++ ++ If we are a writer, take the write lock, and then unlock. ++ If we are a reader, try the lock, then lock, then unlock. */ ++ if ((i % 8) != 0) ++ xpthread_rwlock_wrlock (&onelock); ++ else ++ { ++ if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0) ++ { ++ if (ret == EBUSY) ++ xpthread_rwlock_rdlock (&onelock); ++ else ++ exit (EXIT_FAILURE); ++ } ++ } ++ /* Thread does some work and then unlocks. */ ++ xpthread_rwlock_unlock (&onelock); ++ i++; ++ } ++ return NULL; ++} ++ ++int ++do_test (void) ++{ ++ int i; ++ pthread_t tids[NTHREADS]; ++ xpthread_rwlock_init (&onelock, NULL); ++ for (i = 0; i < NTHREADS; i++) ++ tids[i] = xpthread_create (NULL, run_loop, NULL); ++ /* Run for some amount of time. Empirically speaking exercising ++ the stall via pthread_rwlock_tryrdlock is much harder, and on ++ a 3.5GHz 4 core x86_64 VM system it takes somewhere around ++ 20-200s to stall, approaching 100% stall past 200s. We can't ++ wait that long for a regression test so we just test for 20s, ++ and expect the stall to happen with a 5-10% chance (enough for ++ developers to see). */ ++ sleep (20); ++ /* Then exit. */ ++ printf ("INFO: Exiting...\n"); ++ do_exit = 1; ++ /* If any readers stalled then we will timeout waiting for them. */ ++ for (i = 0; i < NTHREADS; i++) ++ xpthread_join (tids[i]); ++ printf ("INFO: Done.\n"); ++ xpthread_rwlock_destroy (&onelock); ++ printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n"); ++ return 0; ++} ++ ++#define TIMEOUT 30 ++#include +Index: glibc-2.29/nptl/tst-rwlock-trywrlock-stall.c +=================================================================== +--- /dev/null ++++ glibc-2.29/nptl/tst-rwlock-trywrlock-stall.c +@@ -0,0 +1,108 @@ ++/* Bug 23844: Test for pthread_rwlock_trywrlock stalls. ++ Copyright (C) 2019 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c. ++ ++ Summary for the pthread_rwlock_trywrlock() stall: ++ ++ The stall is caused by pthread_rwlock_trywrlock setting ++ __wrphase_futex futex to 1 and loosing the ++ PTHREAD_RWLOCK_FUTEX_USED bit. ++ ++ The fix for bug 23844 ensures that waiters on __wrphase_futex are ++ correctly woken. Before the fix the test stalls as readers can ++ wait forever on __wrphase_futex. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* We need only one lock to reproduce the issue. We will need multiple ++ threads to get the exact case where we have a read, try, and unlock ++ all interleaving to produce the case where the readers are waiting ++ and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a ++ subsequent unlock fails to wake them. */ ++pthread_rwlock_t onelock; ++ ++/* The number of threads is arbitrary but empirically chosen to have ++ enough threads that we see the condition where waiting readers are ++ not woken by a successful unlock. */ ++#define NTHREADS 32 ++ ++_Atomic int do_exit; ++ ++void * ++run_loop (void *arg) ++{ ++ int i = 0, ret; ++ while (!do_exit) ++ { ++ /* Arbitrarily choose if we are the writer or reader. Choose a ++ high enough ratio of readers to writers to make it likely ++ that readers block (and eventually are susceptable to ++ stalling). ++ ++ If we are a writer, take the write lock, and then unlock. ++ If we are a reader, try the lock, then lock, then unlock. */ ++ if ((i % 8) != 0) ++ { ++ if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0) ++ { ++ if (ret == EBUSY) ++ xpthread_rwlock_wrlock (&onelock); ++ else ++ exit (EXIT_FAILURE); ++ } ++ } ++ else ++ xpthread_rwlock_rdlock (&onelock); ++ /* Thread does some work and then unlocks. */ ++ xpthread_rwlock_unlock (&onelock); ++ i++; ++ } ++ return NULL; ++} ++ ++int ++do_test (void) ++{ ++ int i; ++ pthread_t tids[NTHREADS]; ++ xpthread_rwlock_init (&onelock, NULL); ++ for (i = 0; i < NTHREADS; i++) ++ tids[i] = xpthread_create (NULL, run_loop, NULL); ++ /* Run for some amount of time. The pthread_rwlock_tryrwlock stall ++ is very easy to trigger and happens in seconds under the test ++ conditions. */ ++ sleep (10); ++ /* Then exit. */ ++ printf ("INFO: Exiting...\n"); ++ do_exit = 1; ++ /* If any readers stalled then we will timeout waiting for them. */ ++ for (i = 0; i < NTHREADS; i++) ++ xpthread_join (tids[i]); ++ printf ("INFO: Done.\n"); ++ xpthread_rwlock_destroy (&onelock); ++ printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n"); ++ return 0; ++} ++ ++#include +Index: glibc-2.29/support/Makefile +=================================================================== +--- glibc-2.29.orig/support/Makefile ++++ glibc-2.29/support/Makefile +@@ -129,6 +129,7 @@ libsupport-routines = \ + xpthread_mutexattr_settype \ + xpthread_once \ + xpthread_rwlock_init \ ++ xpthread_rwlock_destroy \ + xpthread_rwlock_rdlock \ + xpthread_rwlock_unlock \ + xpthread_rwlock_wrlock \ +Index: glibc-2.29/support/xpthread_rwlock_destroy.c +=================================================================== +--- /dev/null ++++ glibc-2.29/support/xpthread_rwlock_destroy.c +@@ -0,0 +1,26 @@ ++/* pthread_rwlock_destroy with error checking. ++ Copyright (C) 2019 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++ ++void ++xpthread_rwlock_destroy (pthread_rwlock_t *rwlock) ++{ ++ xpthread_check_return ("pthread_rwlock_destroy", ++ pthread_rwlock_destroy (rwlock)); ++} +Index: glibc-2.29/support/xthread.h +=================================================================== +--- glibc-2.29.orig/support/xthread.h ++++ glibc-2.29/support/xthread.h +@@ -84,6 +84,7 @@ void xpthread_rwlockattr_setkind_np (pth + void xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock); + void xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock); + void xpthread_rwlock_unlock (pthread_rwlock_t *rwlock); ++void xpthread_rwlock_destroy (pthread_rwlock_t *rwlock); + + __END_DECLS + diff --git a/riscv-clone-unwind.patch b/riscv-clone-unwind.patch new file mode 100644 index 0000000..8a0b3ac --- /dev/null +++ b/riscv-clone-unwind.patch @@ -0,0 +1,119 @@ +2019-01-13 Jim Wilson + + [BZ #24040] + * elf/Makefile (CFLAGS-tst-unwind-main.c): Add -DUSE_PTHREADS=0. + * elf/tst-unwind-main.c: If USE_PTHEADS, include pthread.h and error.h + (func): New. + (main): If USE_PTHREADS, call pthread_create to run func. Otherwise + call func directly. + * nptl/Makefile (tests): Add tst-unwind-thread. + (CFLAGS-tst-unwind-thread.c): Define. + * nptl/tst-unwind-thread.c: New file. + * sysdeps/unix/sysv/linux/riscv/clone.S (__thread_start): Mark ra + as undefined. + +Index: glibc-2.29/elf/Makefile +=================================================================== +--- glibc-2.29.orig/elf/Makefile ++++ glibc-2.29/elf/Makefile +@@ -1497,4 +1497,4 @@ $(objpfx)tst-big-note: $(objpfx)tst-big- + + $(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so + +-CFLAGS-tst-unwind-main.c += -funwind-tables ++CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0 +Index: glibc-2.29/elf/tst-unwind-main.c +=================================================================== +--- glibc-2.29.orig/elf/tst-unwind-main.c ++++ glibc-2.29/elf/tst-unwind-main.c +@@ -20,19 +20,41 @@ + #include + #include + ++#if USE_PTHREADS ++# include ++# include ++#endif ++ + static _Unwind_Reason_Code + callback (struct _Unwind_Context *ctx, void *arg) + { + return _URC_NO_REASON; + } + +-int +-main (void) ++static void * ++func (void *a) + { + /* Arrange for this test to be killed if _Unwind_Backtrace runs into an + endless loop. We cannot use the test driver because the complete + call chain needs to be compiled with -funwind-tables so that +- _Unwind_Backtrace is able to reach _start. */ ++ _Unwind_Backtrace is able to reach the start routine. */ + alarm (DEFAULT_TIMEOUT); + _Unwind_Backtrace (callback, 0); ++ return a; ++} ++ ++int ++main (void) ++{ ++#if USE_PTHREADS ++ pthread_t thr; ++ int rc = pthread_create (&thr, NULL, &func, NULL); ++ if (rc) ++ error (1, rc, "pthread_create"); ++ rc = pthread_join (thr, NULL); ++ if (rc) ++ error (1, rc, "pthread_join"); ++#else ++ func (NULL); ++#endif + } +Index: glibc-2.29/nptl/Makefile +=================================================================== +--- glibc-2.29.orig/nptl/Makefile ++++ glibc-2.29/nptl/Makefile +@@ -320,7 +320,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 ts + tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \ + tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \ + tst-rwlock-pwn \ +- tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall ++ tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \ ++ tst-unwind-thread + + tests-internal := tst-rwlock19 tst-rwlock20 \ + tst-sem11 tst-sem12 tst-sem13 \ +@@ -710,6 +711,8 @@ $(objpfx)tst-audit-threads: $(objpfx)tst + $(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so + tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so + ++CFLAGS-tst-unwind-thread.c += -funwind-tables ++ + # The tests here better do not run in parallel + ifneq ($(filter %tests,$(MAKECMDGOALS)),) + .NOTPARALLEL: +Index: glibc-2.29/nptl/tst-unwind-thread.c +=================================================================== +--- /dev/null ++++ glibc-2.29/nptl/tst-unwind-thread.c +@@ -0,0 +1,2 @@ ++#define USE_PTHREADS 1 ++#include "../elf/tst-unwind-main.c" +Index: glibc-2.29/sysdeps/unix/sysv/linux/riscv/clone.S +=================================================================== +--- glibc-2.29.orig/sysdeps/unix/sysv/linux/riscv/clone.S ++++ glibc-2.29/sysdeps/unix/sysv/linux/riscv/clone.S +@@ -69,6 +69,11 @@ L (error): + + ENTRY (__thread_start) + L (thread_start): ++ /* Terminate call stack by noting ra is undefined. Use a dummy ++ .cfi_label to force starting the FDE. */ ++ .cfi_label .Ldummy ++ cfi_undefined (ra) ++ + /* Restore the arg for user's function. */ + REG_L a1,0(sp) /* Function pointer. */ + REG_L a0,SZREG(sp) /* Argument pointer. */