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