1*8a16b7a1SPedro F. Giffuni /*- 2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3*8a16b7a1SPedro F. Giffuni * 4bb535300SJeff Roberson * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> 5bb535300SJeff Roberson * All rights reserved. 6bb535300SJeff Roberson * 7bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without 8bb535300SJeff Roberson * modification, are permitted provided that the following conditions 9bb535300SJeff Roberson * are met: 10bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright 11bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer. 12bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 13bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the 14bb535300SJeff Roberson * documentation and/or other materials provided with the distribution. 15fed32d75SWarner Losh * 3. Neither the name of the author nor the names of any co-contributors 16bb535300SJeff Roberson * may be used to endorse or promote products derived from this software 17bb535300SJeff Roberson * without specific prior written permission. 18bb535300SJeff Roberson * 19bb535300SJeff Roberson * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 20bb535300SJeff Roberson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21bb535300SJeff Roberson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22bb535300SJeff Roberson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23bb535300SJeff Roberson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24bb535300SJeff Roberson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25bb535300SJeff Roberson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26bb535300SJeff Roberson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27bb535300SJeff Roberson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28bb535300SJeff Roberson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29bb535300SJeff Roberson * SUCH DAMAGE. 30bb535300SJeff Roberson */ 31a091d823SDavid Xu 3232793011SKonstantin Belousov #include <sys/cdefs.h> 3332793011SKonstantin Belousov __FBSDID("$FreeBSD$"); 3432793011SKonstantin Belousov 35e03efb02SRuslan Ermilov #include "namespace.h" 36bb535300SJeff Roberson #include <errno.h> 373832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 383832fd24SDavid Xu #include <dlfcn.h> 393832fd24SDavid Xu #endif 403a7d122fSConrad Meyer #include <stdarg.h> 41bb535300SJeff Roberson #include <stdio.h> 42bb535300SJeff Roberson #include <stdlib.h> 43bb535300SJeff Roberson #include <pthread.h> 4402c3c858SDavid Xu #include <sys/types.h> 4502c3c858SDavid Xu #include <sys/signalvar.h> 46e03efb02SRuslan Ermilov #include "un-namespace.h" 47a091d823SDavid Xu 48d6742bfbSJason Evans #include "libc_private.h" 49bb535300SJeff Roberson #include "thr_private.h" 50bb535300SJeff Roberson 513832fd24SDavid Xu static void exit_thread(void) __dead2; 523832fd24SDavid Xu 53bb535300SJeff Roberson __weak_reference(_pthread_exit, pthread_exit); 54bb535300SJeff Roberson 553832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 568690b9f6SDavid Xu static int message_printed; 573832fd24SDavid Xu 583832fd24SDavid Xu static void thread_unwind(void) __dead2; 59294246bbSEd Maste #ifdef PIC 603832fd24SDavid Xu static void thread_uw_init(void); 613832fd24SDavid Xu static _Unwind_Reason_Code thread_unwind_stop(int version, 623832fd24SDavid Xu _Unwind_Action actions, 6372248801SDavid Xu int64_t exc_class, 643832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 653832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter); 663832fd24SDavid Xu /* unwind library pointers */ 673832fd24SDavid Xu static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *, 683832fd24SDavid Xu _Unwind_Stop_Fn, void *); 6972248801SDavid Xu static unsigned long (*uwl_getcfa)(struct _Unwind_Context *); 703832fd24SDavid Xu 713832fd24SDavid Xu static void 723832fd24SDavid Xu thread_uw_init(void) 733832fd24SDavid Xu { 743832fd24SDavid Xu static int inited = 0; 755a6d7b72SEric van Gyzen Dl_info dli; 763832fd24SDavid Xu void *handle; 77a5793db9SDavid Xu void *forcedunwind, *getcfa; 783832fd24SDavid Xu 793832fd24SDavid Xu if (inited) 803832fd24SDavid Xu return; 813832fd24SDavid Xu handle = RTLD_DEFAULT; 824da1da4bSDavid Xu if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { 835a6d7b72SEric van Gyzen if (dladdr(forcedunwind, &dli)) { 84a5793db9SDavid Xu /* 85a5793db9SDavid Xu * Make sure the address is always valid by holding the library, 86a5793db9SDavid Xu * also assume functions are in same library. 87a5793db9SDavid Xu */ 885a6d7b72SEric van Gyzen if ((handle = dlopen(dli.dli_fname, RTLD_LAZY)) != NULL) { 894da1da4bSDavid Xu forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); 904da1da4bSDavid Xu getcfa = dlsym(handle, "_Unwind_GetCFA"); 91a5793db9SDavid Xu if (forcedunwind != NULL && getcfa != NULL) { 924da1da4bSDavid Xu uwl_getcfa = getcfa; 93a5793db9SDavid Xu atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, 94a5793db9SDavid Xu (uintptr_t)forcedunwind); 954da1da4bSDavid Xu } else { 964da1da4bSDavid Xu dlclose(handle); 973832fd24SDavid Xu } 983832fd24SDavid Xu } 994da1da4bSDavid Xu } 1004da1da4bSDavid Xu } 1014da1da4bSDavid Xu inited = 1; 1024da1da4bSDavid Xu } 1033832fd24SDavid Xu 1043832fd24SDavid Xu _Unwind_Reason_Code 1053832fd24SDavid Xu _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, 1063832fd24SDavid Xu void *stop_arg) 1073832fd24SDavid Xu { 1083832fd24SDavid Xu return (*uwl_forcedunwind)(ex, stop_func, stop_arg); 1093832fd24SDavid Xu } 1103832fd24SDavid Xu 11172248801SDavid Xu unsigned long 1123832fd24SDavid Xu _Unwind_GetCFA(struct _Unwind_Context *context) 1133832fd24SDavid Xu { 1143832fd24SDavid Xu return (*uwl_getcfa)(context); 1153832fd24SDavid Xu } 1163832fd24SDavid Xu #else 1173832fd24SDavid Xu #pragma weak _Unwind_GetCFA 1183832fd24SDavid Xu #pragma weak _Unwind_ForcedUnwind 119294246bbSEd Maste #endif /* PIC */ 1203832fd24SDavid Xu 1213832fd24SDavid Xu static void 122d2335a57SEric van Gyzen thread_unwind_cleanup(_Unwind_Reason_Code code __unused, 123d2335a57SEric van Gyzen struct _Unwind_Exception *e __unused) 1243832fd24SDavid Xu { 1253832fd24SDavid Xu /* 1263832fd24SDavid Xu * Specification said that _Unwind_Resume should not be used here, 1273832fd24SDavid Xu * instead, user should rethrow the exception. For C++ user, they 1283832fd24SDavid Xu * should put "throw" sentence in catch(...) block. 1293832fd24SDavid Xu */ 1303832fd24SDavid Xu PANIC("exception should be rethrown"); 1313832fd24SDavid Xu } 1323832fd24SDavid Xu 1333832fd24SDavid Xu static _Unwind_Reason_Code 134d2335a57SEric van Gyzen thread_unwind_stop(int version __unused, _Unwind_Action actions, 135d2335a57SEric van Gyzen int64_t exc_class __unused, 136d2335a57SEric van Gyzen struct _Unwind_Exception *exc_obj __unused, 137d2335a57SEric van Gyzen struct _Unwind_Context *context, void *stop_parameter __unused) 1383832fd24SDavid Xu { 1393832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1403832fd24SDavid Xu struct pthread_cleanup *cur; 1413832fd24SDavid Xu uintptr_t cfa; 1423832fd24SDavid Xu int done = 0; 1433832fd24SDavid Xu 1443832fd24SDavid Xu /* XXX assume stack grows down to lower address */ 1453832fd24SDavid Xu 1463832fd24SDavid Xu cfa = _Unwind_GetCFA(context); 1476f066bb3SDavid Xu if (actions & _UA_END_OF_STACK || 1486f066bb3SDavid Xu cfa >= (uintptr_t)curthread->unwind_stackend) { 1493832fd24SDavid Xu done = 1; 1503832fd24SDavid Xu } 1513832fd24SDavid Xu 1523832fd24SDavid Xu while ((cur = curthread->cleanup) != NULL && 1536f066bb3SDavid Xu (done || (uintptr_t)cur <= cfa)) { 1543832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 1553832fd24SDavid Xu } 1563832fd24SDavid Xu 157b585cd3eSKonstantin Belousov if (done) { 158b585cd3eSKonstantin Belousov /* Tell libc that it should call non-trivial TLS dtors. */ 159b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 160b585cd3eSKonstantin Belousov 1613832fd24SDavid Xu exit_thread(); /* Never return! */ 162b585cd3eSKonstantin Belousov } 1633832fd24SDavid Xu 1643832fd24SDavid Xu return (_URC_NO_REASON); 1653832fd24SDavid Xu } 1663832fd24SDavid Xu 1673832fd24SDavid Xu static void 1683832fd24SDavid Xu thread_unwind(void) 1693832fd24SDavid Xu { 1703832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1713832fd24SDavid Xu 1723832fd24SDavid Xu curthread->ex.exception_class = 0; 1733832fd24SDavid Xu curthread->ex.exception_cleanup = thread_unwind_cleanup; 1743832fd24SDavid Xu _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); 1753832fd24SDavid Xu PANIC("_Unwind_ForcedUnwind returned"); 1763832fd24SDavid Xu } 1773832fd24SDavid Xu 1783832fd24SDavid Xu #endif 1793832fd24SDavid Xu 180bb535300SJeff Roberson void 1813a7d122fSConrad Meyer _thread_exitf(const char *fname, int lineno, const char *fmt, ...) 1823a7d122fSConrad Meyer { 1833a7d122fSConrad Meyer va_list ap; 1843a7d122fSConrad Meyer 1853a7d122fSConrad Meyer /* Write an error message to the standard error file descriptor: */ 1863a7d122fSConrad Meyer _thread_printf(STDERR_FILENO, "Fatal error '"); 1873a7d122fSConrad Meyer 1883a7d122fSConrad Meyer va_start(ap, fmt); 1893a7d122fSConrad Meyer _thread_vprintf(STDERR_FILENO, fmt, ap); 1903a7d122fSConrad Meyer va_end(ap); 1913a7d122fSConrad Meyer 1923a7d122fSConrad Meyer _thread_printf(STDERR_FILENO, "' at line %d in file %s (errno = %d)\n", 1933a7d122fSConrad Meyer lineno, fname, errno); 1943a7d122fSConrad Meyer 1953a7d122fSConrad Meyer abort(); 1963a7d122fSConrad Meyer } 1973a7d122fSConrad Meyer 1983a7d122fSConrad Meyer void 19937a6356bSDavid Xu _thread_exit(const char *fname, int lineno, const char *msg) 200bb535300SJeff Roberson { 201bb535300SJeff Roberson 2023a7d122fSConrad Meyer _thread_exitf(fname, lineno, "%s", msg); 203bb535300SJeff Roberson } 204bb535300SJeff Roberson 205bb535300SJeff Roberson void 206bb535300SJeff Roberson _pthread_exit(void *status) 207bb535300SJeff Roberson { 20802c3c858SDavid Xu _pthread_exit_mask(status, NULL); 20902c3c858SDavid Xu } 21002c3c858SDavid Xu 21102c3c858SDavid Xu void 21202c3c858SDavid Xu _pthread_exit_mask(void *status, sigset_t *mask) 21302c3c858SDavid Xu { 214a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 2154cd18a22SMike Makonnen 216bb535300SJeff Roberson /* Check if this thread is already in the process of exiting: */ 217c72ef5eaSConrad Meyer if (curthread->cancelling) 218c72ef5eaSConrad Meyer PANIC("Thread %p has called " 219a091d823SDavid Xu "pthread_exit() from a destructor. POSIX 1003.1 " 220a091d823SDavid Xu "1996 s16.2.5.2 does not allow this!", curthread); 221bb535300SJeff Roberson 222a091d823SDavid Xu /* Flag this thread as exiting. */ 223f08e1bf6SDavid Xu curthread->cancelling = 1; 2244173ebefSDavid Xu curthread->no_cancel = 1; 225cdcffc3fSDavid Xu curthread->cancel_async = 0; 22602c3c858SDavid Xu curthread->cancel_point = 0; 22702c3c858SDavid Xu if (mask != NULL) 22802c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, mask, NULL); 22902c3c858SDavid Xu if (curthread->unblock_sigcancel) { 23002c3c858SDavid Xu sigset_t set; 23102c3c858SDavid Xu 23202c3c858SDavid Xu curthread->unblock_sigcancel = 0; 23302c3c858SDavid Xu SIGEMPTYSET(set); 23402c3c858SDavid Xu SIGADDSET(set, SIGCANCEL); 23502c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); 23602c3c858SDavid Xu } 237a091d823SDavid Xu 238bb535300SJeff Roberson /* Save the return value: */ 239bb535300SJeff Roberson curthread->ret = status; 2403832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 2418690b9f6SDavid Xu 242294246bbSEd Maste #ifdef PIC 2433832fd24SDavid Xu thread_uw_init(); 2448690b9f6SDavid Xu if (uwl_forcedunwind != NULL) { 2458690b9f6SDavid Xu #else 2468690b9f6SDavid Xu if (_Unwind_ForcedUnwind != NULL) { 2478690b9f6SDavid Xu #endif 2488690b9f6SDavid Xu if (curthread->unwind_disabled) { 2498690b9f6SDavid Xu if (message_printed == 0) { 2508690b9f6SDavid Xu message_printed = 1; 2518690b9f6SDavid Xu _thread_printf(2, "Warning: old _pthread_cleanup_push was called, " 2528690b9f6SDavid Xu "stack unwinding is disabled.\n"); 2538690b9f6SDavid Xu } 2548690b9f6SDavid Xu goto cleanup; 2558690b9f6SDavid Xu } 2568690b9f6SDavid Xu thread_unwind(); 2578690b9f6SDavid Xu 2588690b9f6SDavid Xu } else { 2598690b9f6SDavid Xu cleanup: 2603832fd24SDavid Xu while (curthread->cleanup != NULL) { 2613832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2623832fd24SDavid Xu } 263b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 264b585cd3eSKonstantin Belousov 2653832fd24SDavid Xu exit_thread(); 2663832fd24SDavid Xu } 2673832fd24SDavid Xu 2683832fd24SDavid Xu #else 2693832fd24SDavid Xu while (curthread->cleanup != NULL) { 2703832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2713832fd24SDavid Xu } 272b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 2733832fd24SDavid Xu 2743832fd24SDavid Xu exit_thread(); 2753832fd24SDavid Xu #endif /* _PTHREAD_FORCED_UNWIND */ 2763832fd24SDavid Xu } 2773832fd24SDavid Xu 2783832fd24SDavid Xu static void 2793832fd24SDavid Xu exit_thread(void) 2803832fd24SDavid Xu { 2813832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 28237a6356bSDavid Xu 283bb535300SJeff Roberson /* Check if there is thread specific data: */ 284bb535300SJeff Roberson if (curthread->specific != NULL) { 285bb535300SJeff Roberson /* Run the thread-specific data destructors: */ 286bb535300SJeff Roberson _thread_cleanupspecific(); 287bb535300SJeff Roberson } 288bb535300SJeff Roberson 289a091d823SDavid Xu if (!_thr_isthreaded()) 2904e3f7b6eSMike Makonnen exit(0); 291f97591bfSMike Makonnen 292a9b764e2SDavid Xu if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { 293a091d823SDavid Xu exit(0); 294a091d823SDavid Xu /* Never reach! */ 295bb535300SJeff Roberson } 2965b3842aeSJason Evans 2975b3842aeSJason Evans /* Tell malloc that the thread is exiting. */ 2985b3842aeSJason Evans _malloc_thread_cleanup(); 2995b3842aeSJason Evans 300bc414752SDavid Xu THR_LOCK(curthread); 301bc414752SDavid Xu curthread->state = PS_DEAD; 3022ea1f90aSDavid Xu if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { 3032ea1f90aSDavid Xu curthread->cycle++; 3048d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 3052ea1f90aSDavid Xu } 3061d5b5089SDavid Xu if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) 3071d5b5089SDavid Xu _thr_report_death(curthread); 308bc414752SDavid Xu /* 309bc414752SDavid Xu * Thread was created with initial refcount 1, we drop the 310bc414752SDavid Xu * reference count to allow it to be garbage collected. 311bc414752SDavid Xu */ 312bc414752SDavid Xu curthread->refcount--; 313a9b764e2SDavid Xu _thr_try_gc(curthread, curthread); /* thread lock released */ 314a9b764e2SDavid Xu 315a9b764e2SDavid Xu #if defined(_PTHREADS_INVARIANTS) 316a9b764e2SDavid Xu if (THR_IN_CRITICAL(curthread)) 317c72ef5eaSConrad Meyer PANIC("thread %p exits with resources held!", curthread); 318a9b764e2SDavid Xu #endif 319d7f119abSDavid Xu /* 320d7f119abSDavid Xu * Kernel will do wakeup at the address, so joiner thread 321d7f119abSDavid Xu * will be resumed if it is sleeping at the address. 322d7f119abSDavid Xu */ 323d245d9e1SDavid Xu thr_exit(&curthread->tid); 324a091d823SDavid Xu PANIC("thr_exit() returned"); 325a091d823SDavid Xu /* Never reach! */ 3261c6f6301SMike Makonnen } 327