xref: /freebsd/lib/libc/stdlib/cxa_thread_atexit_impl.c (revision b27eb9ce96b838622e125fd969e8dc4914aabe18)
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