1b7c7684aSKonstantin Belousov /*- 2b7c7684aSKonstantin Belousov * Copyright (c) 2016 Mahdi Mokhtari <mokhi64@gmail.com> 3b7c7684aSKonstantin Belousov * Copyright (c) 2016, 2017 The FreeBSD Foundation 4b7c7684aSKonstantin Belousov * All rights reserved. 5b7c7684aSKonstantin Belousov * 6b7c7684aSKonstantin Belousov * Portions of this software were developed by Konstantin Belousov 7b7c7684aSKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 8b7c7684aSKonstantin Belousov * 9b7c7684aSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 10b7c7684aSKonstantin Belousov * modification, are permitted provided that the following conditions 11b7c7684aSKonstantin Belousov * are met: 12b7c7684aSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 13b7c7684aSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 14b7c7684aSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 15b7c7684aSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 16b7c7684aSKonstantin Belousov * documentation and/or other materials provided with the distribution. 17b7c7684aSKonstantin Belousov * 18b7c7684aSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19b7c7684aSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20b7c7684aSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21b7c7684aSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22b7c7684aSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23b7c7684aSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24b7c7684aSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25b7c7684aSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26b7c7684aSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27b7c7684aSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28b7c7684aSKonstantin Belousov * SUCH DAMAGE. 29b7c7684aSKonstantin Belousov */ 30b7c7684aSKonstantin Belousov 31b7c7684aSKonstantin Belousov #include <sys/queue.h> 32b7c7684aSKonstantin Belousov #include "namespace.h" 33b7c7684aSKonstantin Belousov #include <errno.h> 34b7c7684aSKonstantin Belousov #include <link.h> 35b7c7684aSKonstantin Belousov #include <pthread.h> 36b7c7684aSKonstantin Belousov #include <stddef.h> 37b7c7684aSKonstantin Belousov #include <stdlib.h> 38b7c7684aSKonstantin Belousov #include <stdio.h> 39b7c7684aSKonstantin Belousov #include "un-namespace.h" 40b7c7684aSKonstantin Belousov #include "libc_private.h" 41b7c7684aSKonstantin Belousov 42b7c7684aSKonstantin Belousov /* 43b7c7684aSKonstantin Belousov * C++11 introduces the thread_local scope (like __thread with some 44b7c7684aSKonstantin Belousov * additions). As a key-feature it should support non-trivial 45b7c7684aSKonstantin Belousov * destructors, registered with __cxa_thread_atexit() to be executed 46b7c7684aSKonstantin Belousov * at the thread termination. 47b7c7684aSKonstantin Belousov * 48b7c7684aSKonstantin Belousov * The implemention keeps a _Thread_local list of destructors per each 49b7c7684aSKonstantin Belousov * thread, and calls __cxa_thread_call_dtors() on each thread's exit 50b7c7684aSKonstantin Belousov * to do cleanup. For a thread calling exit(3), in particular, for 51b7c7684aSKonstantin Belousov * the initial thread returning from main(), we call 52b7c7684aSKonstantin Belousov * __cxa_thread_call_dtors() inside exit(). 53b7c7684aSKonstantin Belousov * 54b7c7684aSKonstantin Belousov * It could be possible that a dynamically loaded library, use 55b7c7684aSKonstantin Belousov * thread_local variable but is dlclose()'d before thread exit. The 56b7c7684aSKonstantin Belousov * destructor of this variable will then try to access the address, 57b7c7684aSKonstantin Belousov * for calling it but it's unloaded, so it'll crash. We're using 58b7c7684aSKonstantin Belousov * __elf_phdr_match_addr() to detect and prevent such cases and so 59b7c7684aSKonstantin Belousov * prevent the crash. 60b7c7684aSKonstantin Belousov */ 61b7c7684aSKonstantin Belousov 62b7c7684aSKonstantin Belousov #define CXA_DTORS_ITERATIONS 4 63b7c7684aSKonstantin Belousov 64b7c7684aSKonstantin Belousov struct cxa_thread_dtor { 65b7c7684aSKonstantin Belousov void *obj; 66b7c7684aSKonstantin Belousov void (*func)(void *); 67b7c7684aSKonstantin Belousov void *dso; 68b7c7684aSKonstantin Belousov LIST_ENTRY(cxa_thread_dtor) entry; 69b7c7684aSKonstantin Belousov }; 70b7c7684aSKonstantin Belousov static _Thread_local LIST_HEAD(dtor_list, cxa_thread_dtor) dtors = 71b7c7684aSKonstantin Belousov LIST_HEAD_INITIALIZER(dtors); 72b7c7684aSKonstantin Belousov 73b7c7684aSKonstantin Belousov int 74b7c7684aSKonstantin Belousov __cxa_thread_atexit_impl(void (*dtor_func)(void *), void *obj, 75b7c7684aSKonstantin Belousov void *dso_symbol) 76b7c7684aSKonstantin Belousov { 77b7c7684aSKonstantin Belousov 78b7c7684aSKonstantin Belousov return (__cxa_thread_atexit_hidden(dtor_func, obj, dso_symbol)); 79b7c7684aSKonstantin Belousov } 80b7c7684aSKonstantin Belousov 81b7c7684aSKonstantin Belousov int 82b7c7684aSKonstantin Belousov __cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj, 83b7c7684aSKonstantin Belousov void *dso_symbol) 84b7c7684aSKonstantin Belousov { 85b7c7684aSKonstantin Belousov struct cxa_thread_dtor *new_dtor; 86b7c7684aSKonstantin Belousov 87b7c7684aSKonstantin Belousov new_dtor = malloc(sizeof(*new_dtor)); 88b7c7684aSKonstantin Belousov if (new_dtor == NULL) { 89b7c7684aSKonstantin Belousov errno = ENOMEM; /* forcibly override malloc(3) error */ 90b7c7684aSKonstantin Belousov return (-1); 91b7c7684aSKonstantin Belousov } 92b7c7684aSKonstantin Belousov 93b7c7684aSKonstantin Belousov new_dtor->obj = obj; 94b7c7684aSKonstantin Belousov new_dtor->func = dtor_func; 95b7c7684aSKonstantin Belousov new_dtor->dso = dso_symbol; 96b7c7684aSKonstantin Belousov LIST_INSERT_HEAD(&dtors, new_dtor, entry); 97b7c7684aSKonstantin Belousov return (0); 98b7c7684aSKonstantin Belousov } 99b7c7684aSKonstantin Belousov 100b7c7684aSKonstantin Belousov static void 101b7c7684aSKonstantin Belousov walk_cb_call(struct cxa_thread_dtor *dtor) 102b7c7684aSKonstantin Belousov { 103b7c7684aSKonstantin Belousov struct dl_phdr_info phdr_info; 104b7c7684aSKonstantin Belousov 105*b27eb9ceSKonstantin Belousov if (_rtld_addr_phdr(dtor->func, &phdr_info) && 106b7c7684aSKonstantin Belousov __elf_phdr_match_addr(&phdr_info, dtor->func)) 107b7c7684aSKonstantin Belousov dtor->func(dtor->obj); 108b7c7684aSKonstantin Belousov else 109dedc71fdSEd Maste fprintf(stderr, 110dedc71fdSEd Maste "__cxa_thread_call_dtors: dtr %p from unloaded dso, skipping\n", 111dedc71fdSEd Maste (void *)(dtor->func)); 112b7c7684aSKonstantin Belousov } 113b7c7684aSKonstantin Belousov 114b7c7684aSKonstantin Belousov static void 115b7c7684aSKonstantin Belousov walk_cb_nocall(struct cxa_thread_dtor *dtor __unused) 116b7c7684aSKonstantin Belousov { 117b7c7684aSKonstantin Belousov } 118b7c7684aSKonstantin Belousov 119b7c7684aSKonstantin Belousov static void 120b7c7684aSKonstantin Belousov cxa_thread_walk(void (*cb)(struct cxa_thread_dtor *)) 121b7c7684aSKonstantin Belousov { 122b7c7684aSKonstantin Belousov struct cxa_thread_dtor *dtor, *tdtor; 123b7c7684aSKonstantin Belousov 124b7c7684aSKonstantin Belousov LIST_FOREACH_SAFE(dtor, &dtors, entry, tdtor) { 125b7c7684aSKonstantin Belousov LIST_REMOVE(dtor, entry); 126b7c7684aSKonstantin Belousov cb(dtor); 127b7c7684aSKonstantin Belousov free(dtor); 128b7c7684aSKonstantin Belousov } 129b7c7684aSKonstantin Belousov } 130b7c7684aSKonstantin Belousov 131b7c7684aSKonstantin Belousov /* 132b7c7684aSKonstantin Belousov * This is the callback function we use to call destructors, once for 133b7c7684aSKonstantin Belousov * each thread. It is called in exit(3) in libc/stdlib/exit.c and 134b7c7684aSKonstantin Belousov * before exit_thread() in libthr/thread/thr_exit.c. 135b7c7684aSKonstantin Belousov */ 136b7c7684aSKonstantin Belousov void 137b7c7684aSKonstantin Belousov __cxa_thread_call_dtors(void) 138b7c7684aSKonstantin Belousov { 139b7c7684aSKonstantin Belousov int i; 140b7c7684aSKonstantin Belousov 141b7c7684aSKonstantin Belousov for (i = 0; i < CXA_DTORS_ITERATIONS && !LIST_EMPTY(&dtors); i++) 142b7c7684aSKonstantin Belousov cxa_thread_walk(walk_cb_call); 143b7c7684aSKonstantin Belousov 144b7c7684aSKonstantin Belousov if (!LIST_EMPTY(&dtors)) { 145b7c7684aSKonstantin Belousov fprintf(stderr, "Thread %p is exiting with more " 146b7c7684aSKonstantin Belousov "thread-specific dtors created after %d iterations " 147b7c7684aSKonstantin Belousov "of destructor calls\n", 148b7c7684aSKonstantin Belousov _pthread_self(), i); 149b7c7684aSKonstantin Belousov cxa_thread_walk(walk_cb_nocall); 150b7c7684aSKonstantin Belousov } 151b7c7684aSKonstantin Belousov } 152