1bb535300SJeff Roberson /* 2bb535300SJeff Roberson * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> 3bb535300SJeff Roberson * All rights reserved. 4bb535300SJeff Roberson * 5bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without 6bb535300SJeff Roberson * modification, are permitted provided that the following conditions 7bb535300SJeff Roberson * are met: 8bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright 9bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer. 10bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 11bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the 12bb535300SJeff Roberson * documentation and/or other materials provided with the distribution. 13fed32d75SWarner Losh * 3. Neither the name of the author nor the names of any co-contributors 14bb535300SJeff Roberson * may be used to endorse or promote products derived from this software 15bb535300SJeff Roberson * without specific prior written permission. 16bb535300SJeff Roberson * 17bb535300SJeff Roberson * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 18bb535300SJeff Roberson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19bb535300SJeff Roberson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20bb535300SJeff Roberson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21bb535300SJeff Roberson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22bb535300SJeff Roberson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23bb535300SJeff Roberson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24bb535300SJeff Roberson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25bb535300SJeff Roberson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26bb535300SJeff Roberson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27bb535300SJeff Roberson * SUCH DAMAGE. 28bb535300SJeff Roberson */ 29a091d823SDavid Xu 3032793011SKonstantin Belousov #include <sys/cdefs.h> 3132793011SKonstantin Belousov __FBSDID("$FreeBSD$"); 3232793011SKonstantin Belousov 33e03efb02SRuslan Ermilov #include "namespace.h" 34bb535300SJeff Roberson #include <errno.h> 353832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 363832fd24SDavid Xu #include <dlfcn.h> 373832fd24SDavid Xu #endif 383a7d122fSConrad Meyer #include <stdarg.h> 39bb535300SJeff Roberson #include <stdio.h> 40bb535300SJeff Roberson #include <stdlib.h> 41bb535300SJeff Roberson #include <pthread.h> 4202c3c858SDavid Xu #include <sys/types.h> 4302c3c858SDavid Xu #include <sys/signalvar.h> 44e03efb02SRuslan Ermilov #include "un-namespace.h" 45a091d823SDavid Xu 46d6742bfbSJason Evans #include "libc_private.h" 47bb535300SJeff Roberson #include "thr_private.h" 48bb535300SJeff Roberson 493832fd24SDavid Xu static void exit_thread(void) __dead2; 503832fd24SDavid Xu 51bb535300SJeff Roberson __weak_reference(_pthread_exit, pthread_exit); 52bb535300SJeff Roberson 533832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 548690b9f6SDavid Xu static int message_printed; 553832fd24SDavid Xu 563832fd24SDavid Xu static void thread_unwind(void) __dead2; 57294246bbSEd Maste #ifdef PIC 583832fd24SDavid Xu static void thread_uw_init(void); 593832fd24SDavid Xu static _Unwind_Reason_Code thread_unwind_stop(int version, 603832fd24SDavid Xu _Unwind_Action actions, 6172248801SDavid Xu int64_t exc_class, 623832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 633832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter); 643832fd24SDavid Xu /* unwind library pointers */ 653832fd24SDavid Xu static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *, 663832fd24SDavid Xu _Unwind_Stop_Fn, void *); 6772248801SDavid Xu static unsigned long (*uwl_getcfa)(struct _Unwind_Context *); 683832fd24SDavid Xu 693832fd24SDavid Xu static void 703832fd24SDavid Xu thread_uw_init(void) 713832fd24SDavid Xu { 723832fd24SDavid Xu static int inited = 0; 73*5a6d7b72SEric van Gyzen Dl_info dli; 743832fd24SDavid Xu void *handle; 75a5793db9SDavid Xu void *forcedunwind, *getcfa; 763832fd24SDavid Xu 773832fd24SDavid Xu if (inited) 783832fd24SDavid Xu return; 793832fd24SDavid Xu handle = RTLD_DEFAULT; 804da1da4bSDavid Xu if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { 81*5a6d7b72SEric van Gyzen if (dladdr(forcedunwind, &dli)) { 82a5793db9SDavid Xu /* 83a5793db9SDavid Xu * Make sure the address is always valid by holding the library, 84a5793db9SDavid Xu * also assume functions are in same library. 85a5793db9SDavid Xu */ 86*5a6d7b72SEric van Gyzen if ((handle = dlopen(dli.dli_fname, RTLD_LAZY)) != NULL) { 874da1da4bSDavid Xu forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); 884da1da4bSDavid Xu getcfa = dlsym(handle, "_Unwind_GetCFA"); 89a5793db9SDavid Xu if (forcedunwind != NULL && getcfa != NULL) { 904da1da4bSDavid Xu uwl_getcfa = getcfa; 91a5793db9SDavid Xu atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, 92a5793db9SDavid Xu (uintptr_t)forcedunwind); 934da1da4bSDavid Xu } else { 944da1da4bSDavid Xu dlclose(handle); 953832fd24SDavid Xu } 963832fd24SDavid Xu } 974da1da4bSDavid Xu } 984da1da4bSDavid Xu } 994da1da4bSDavid Xu inited = 1; 1004da1da4bSDavid Xu } 1013832fd24SDavid Xu 1023832fd24SDavid Xu _Unwind_Reason_Code 1033832fd24SDavid Xu _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, 1043832fd24SDavid Xu void *stop_arg) 1053832fd24SDavid Xu { 1063832fd24SDavid Xu return (*uwl_forcedunwind)(ex, stop_func, stop_arg); 1073832fd24SDavid Xu } 1083832fd24SDavid Xu 10972248801SDavid Xu unsigned long 1103832fd24SDavid Xu _Unwind_GetCFA(struct _Unwind_Context *context) 1113832fd24SDavid Xu { 1123832fd24SDavid Xu return (*uwl_getcfa)(context); 1133832fd24SDavid Xu } 1143832fd24SDavid Xu #else 1153832fd24SDavid Xu #pragma weak _Unwind_GetCFA 1163832fd24SDavid Xu #pragma weak _Unwind_ForcedUnwind 117294246bbSEd Maste #endif /* PIC */ 1183832fd24SDavid Xu 1193832fd24SDavid Xu static void 120d2335a57SEric van Gyzen thread_unwind_cleanup(_Unwind_Reason_Code code __unused, 121d2335a57SEric van Gyzen struct _Unwind_Exception *e __unused) 1223832fd24SDavid Xu { 1233832fd24SDavid Xu /* 1243832fd24SDavid Xu * Specification said that _Unwind_Resume should not be used here, 1253832fd24SDavid Xu * instead, user should rethrow the exception. For C++ user, they 1263832fd24SDavid Xu * should put "throw" sentence in catch(...) block. 1273832fd24SDavid Xu */ 1283832fd24SDavid Xu PANIC("exception should be rethrown"); 1293832fd24SDavid Xu } 1303832fd24SDavid Xu 1313832fd24SDavid Xu static _Unwind_Reason_Code 132d2335a57SEric van Gyzen thread_unwind_stop(int version __unused, _Unwind_Action actions, 133d2335a57SEric van Gyzen int64_t exc_class __unused, 134d2335a57SEric van Gyzen struct _Unwind_Exception *exc_obj __unused, 135d2335a57SEric van Gyzen struct _Unwind_Context *context, void *stop_parameter __unused) 1363832fd24SDavid Xu { 1373832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1383832fd24SDavid Xu struct pthread_cleanup *cur; 1393832fd24SDavid Xu uintptr_t cfa; 1403832fd24SDavid Xu int done = 0; 1413832fd24SDavid Xu 1423832fd24SDavid Xu /* XXX assume stack grows down to lower address */ 1433832fd24SDavid Xu 1443832fd24SDavid Xu cfa = _Unwind_GetCFA(context); 1456f066bb3SDavid Xu if (actions & _UA_END_OF_STACK || 1466f066bb3SDavid Xu cfa >= (uintptr_t)curthread->unwind_stackend) { 1473832fd24SDavid Xu done = 1; 1483832fd24SDavid Xu } 1493832fd24SDavid Xu 1503832fd24SDavid Xu while ((cur = curthread->cleanup) != NULL && 1516f066bb3SDavid Xu (done || (uintptr_t)cur <= cfa)) { 1523832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 1533832fd24SDavid Xu } 1543832fd24SDavid Xu 155b585cd3eSKonstantin Belousov if (done) { 156b585cd3eSKonstantin Belousov /* Tell libc that it should call non-trivial TLS dtors. */ 157b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 158b585cd3eSKonstantin Belousov 1593832fd24SDavid Xu exit_thread(); /* Never return! */ 160b585cd3eSKonstantin Belousov } 1613832fd24SDavid Xu 1623832fd24SDavid Xu return (_URC_NO_REASON); 1633832fd24SDavid Xu } 1643832fd24SDavid Xu 1653832fd24SDavid Xu static void 1663832fd24SDavid Xu thread_unwind(void) 1673832fd24SDavid Xu { 1683832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1693832fd24SDavid Xu 1703832fd24SDavid Xu curthread->ex.exception_class = 0; 1713832fd24SDavid Xu curthread->ex.exception_cleanup = thread_unwind_cleanup; 1723832fd24SDavid Xu _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); 1733832fd24SDavid Xu PANIC("_Unwind_ForcedUnwind returned"); 1743832fd24SDavid Xu } 1753832fd24SDavid Xu 1763832fd24SDavid Xu #endif 1773832fd24SDavid Xu 178bb535300SJeff Roberson void 1793a7d122fSConrad Meyer _thread_exitf(const char *fname, int lineno, const char *fmt, ...) 1803a7d122fSConrad Meyer { 1813a7d122fSConrad Meyer va_list ap; 1823a7d122fSConrad Meyer 1833a7d122fSConrad Meyer /* Write an error message to the standard error file descriptor: */ 1843a7d122fSConrad Meyer _thread_printf(STDERR_FILENO, "Fatal error '"); 1853a7d122fSConrad Meyer 1863a7d122fSConrad Meyer va_start(ap, fmt); 1873a7d122fSConrad Meyer _thread_vprintf(STDERR_FILENO, fmt, ap); 1883a7d122fSConrad Meyer va_end(ap); 1893a7d122fSConrad Meyer 1903a7d122fSConrad Meyer _thread_printf(STDERR_FILENO, "' at line %d in file %s (errno = %d)\n", 1913a7d122fSConrad Meyer lineno, fname, errno); 1923a7d122fSConrad Meyer 1933a7d122fSConrad Meyer abort(); 1943a7d122fSConrad Meyer } 1953a7d122fSConrad Meyer 1963a7d122fSConrad Meyer void 19737a6356bSDavid Xu _thread_exit(const char *fname, int lineno, const char *msg) 198bb535300SJeff Roberson { 199bb535300SJeff Roberson 2003a7d122fSConrad Meyer _thread_exitf(fname, lineno, "%s", msg); 201bb535300SJeff Roberson } 202bb535300SJeff Roberson 203bb535300SJeff Roberson void 204bb535300SJeff Roberson _pthread_exit(void *status) 205bb535300SJeff Roberson { 20602c3c858SDavid Xu _pthread_exit_mask(status, NULL); 20702c3c858SDavid Xu } 20802c3c858SDavid Xu 20902c3c858SDavid Xu void 21002c3c858SDavid Xu _pthread_exit_mask(void *status, sigset_t *mask) 21102c3c858SDavid Xu { 212a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 2134cd18a22SMike Makonnen 214bb535300SJeff Roberson /* Check if this thread is already in the process of exiting: */ 215c72ef5eaSConrad Meyer if (curthread->cancelling) 216c72ef5eaSConrad Meyer PANIC("Thread %p has called " 217a091d823SDavid Xu "pthread_exit() from a destructor. POSIX 1003.1 " 218a091d823SDavid Xu "1996 s16.2.5.2 does not allow this!", curthread); 219bb535300SJeff Roberson 220a091d823SDavid Xu /* Flag this thread as exiting. */ 221f08e1bf6SDavid Xu curthread->cancelling = 1; 2224173ebefSDavid Xu curthread->no_cancel = 1; 223cdcffc3fSDavid Xu curthread->cancel_async = 0; 22402c3c858SDavid Xu curthread->cancel_point = 0; 22502c3c858SDavid Xu if (mask != NULL) 22602c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, mask, NULL); 22702c3c858SDavid Xu if (curthread->unblock_sigcancel) { 22802c3c858SDavid Xu sigset_t set; 22902c3c858SDavid Xu 23002c3c858SDavid Xu curthread->unblock_sigcancel = 0; 23102c3c858SDavid Xu SIGEMPTYSET(set); 23202c3c858SDavid Xu SIGADDSET(set, SIGCANCEL); 23302c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); 23402c3c858SDavid Xu } 235a091d823SDavid Xu 236bb535300SJeff Roberson /* Save the return value: */ 237bb535300SJeff Roberson curthread->ret = status; 2383832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 2398690b9f6SDavid Xu 240294246bbSEd Maste #ifdef PIC 2413832fd24SDavid Xu thread_uw_init(); 2428690b9f6SDavid Xu if (uwl_forcedunwind != NULL) { 2438690b9f6SDavid Xu #else 2448690b9f6SDavid Xu if (_Unwind_ForcedUnwind != NULL) { 2458690b9f6SDavid Xu #endif 2468690b9f6SDavid Xu if (curthread->unwind_disabled) { 2478690b9f6SDavid Xu if (message_printed == 0) { 2488690b9f6SDavid Xu message_printed = 1; 2498690b9f6SDavid Xu _thread_printf(2, "Warning: old _pthread_cleanup_push was called, " 2508690b9f6SDavid Xu "stack unwinding is disabled.\n"); 2518690b9f6SDavid Xu } 2528690b9f6SDavid Xu goto cleanup; 2538690b9f6SDavid Xu } 2548690b9f6SDavid Xu thread_unwind(); 2558690b9f6SDavid Xu 2568690b9f6SDavid Xu } else { 2578690b9f6SDavid Xu cleanup: 2583832fd24SDavid Xu while (curthread->cleanup != NULL) { 2593832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2603832fd24SDavid Xu } 261b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 262b585cd3eSKonstantin Belousov 2633832fd24SDavid Xu exit_thread(); 2643832fd24SDavid Xu } 2653832fd24SDavid Xu 2663832fd24SDavid Xu #else 2673832fd24SDavid Xu while (curthread->cleanup != NULL) { 2683832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2693832fd24SDavid Xu } 270b585cd3eSKonstantin Belousov __cxa_thread_call_dtors(); 2713832fd24SDavid Xu 2723832fd24SDavid Xu exit_thread(); 2733832fd24SDavid Xu #endif /* _PTHREAD_FORCED_UNWIND */ 2743832fd24SDavid Xu } 2753832fd24SDavid Xu 2763832fd24SDavid Xu static void 2773832fd24SDavid Xu exit_thread(void) 2783832fd24SDavid Xu { 2793832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 28037a6356bSDavid Xu 281bb535300SJeff Roberson /* Check if there is thread specific data: */ 282bb535300SJeff Roberson if (curthread->specific != NULL) { 283bb535300SJeff Roberson /* Run the thread-specific data destructors: */ 284bb535300SJeff Roberson _thread_cleanupspecific(); 285bb535300SJeff Roberson } 286bb535300SJeff Roberson 287a091d823SDavid Xu if (!_thr_isthreaded()) 2884e3f7b6eSMike Makonnen exit(0); 289f97591bfSMike Makonnen 290a9b764e2SDavid Xu if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { 291a091d823SDavid Xu exit(0); 292a091d823SDavid Xu /* Never reach! */ 293bb535300SJeff Roberson } 2945b3842aeSJason Evans 2955b3842aeSJason Evans /* Tell malloc that the thread is exiting. */ 2965b3842aeSJason Evans _malloc_thread_cleanup(); 2975b3842aeSJason Evans 298bc414752SDavid Xu THR_LOCK(curthread); 299bc414752SDavid Xu curthread->state = PS_DEAD; 3002ea1f90aSDavid Xu if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { 3012ea1f90aSDavid Xu curthread->cycle++; 3028d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 3032ea1f90aSDavid Xu } 3041d5b5089SDavid Xu if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) 3051d5b5089SDavid Xu _thr_report_death(curthread); 306bc414752SDavid Xu /* 307bc414752SDavid Xu * Thread was created with initial refcount 1, we drop the 308bc414752SDavid Xu * reference count to allow it to be garbage collected. 309bc414752SDavid Xu */ 310bc414752SDavid Xu curthread->refcount--; 311a9b764e2SDavid Xu _thr_try_gc(curthread, curthread); /* thread lock released */ 312a9b764e2SDavid Xu 313a9b764e2SDavid Xu #if defined(_PTHREADS_INVARIANTS) 314a9b764e2SDavid Xu if (THR_IN_CRITICAL(curthread)) 315c72ef5eaSConrad Meyer PANIC("thread %p exits with resources held!", curthread); 316a9b764e2SDavid Xu #endif 317d7f119abSDavid Xu /* 318d7f119abSDavid Xu * Kernel will do wakeup at the address, so joiner thread 319d7f119abSDavid Xu * will be resumed if it is sleeping at the address. 320d7f119abSDavid Xu */ 321d245d9e1SDavid Xu thr_exit(&curthread->tid); 322a091d823SDavid Xu PANIC("thr_exit() returned"); 323a091d823SDavid Xu /* Never reach! */ 3241c6f6301SMike Makonnen } 325