]> git.saurik.com Git - apple/libc.git/blob - stdlib/FreeBSD/atexit.c
48ada4c3d7d4781223bdd32e7963b1ec518c7826
[apple/libc.git] / stdlib / FreeBSD / atexit.c
1 /*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #pragma clang diagnostic push
34 #pragma clang diagnostic ignored "-Wimplicit-function-declaration"
35 #pragma clang diagnostic ignored "-Wstrict-prototypes"
36 #pragma clang diagnostic ignored "-Winvalid-pp-token"
37 #pragma clang diagnostic ignored "-Wint-conversion"
38
39 #if defined(LIBC_SCCS) && !defined(lint)
40 static char sccsid[] = "@(#)atexit.c 8.2 (Berkeley) 7/3/94";
41 #endif /* LIBC_SCCS and not lint */
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD: src/lib/libc/stdlib/atexit.c,v 1.8 2007/01/09 00:28:09 imp Exp $");
44 #include <TargetConditionals.h>
45
46 #include "namespace.h"
47 #include <errno.h>
48 #include <stddef.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <assert.h>
52 #include <pthread.h>
53 #if (defined(__DYNAMIC__) || defined (__BLOCKS__)) && !TARGET_OS_DRIVERKIT
54 #include <dlfcn.h>
55 #endif /* defined(__DYNAMIC__) */
56 #include "atexit.h"
57 #include "un-namespace.h"
58
59 #ifdef __BLOCKS__
60 #include <Block.h>
61 #include <Block_private.h>
62 #endif /* __BLOCKS__ */
63 #include "libc_private.h"
64 #include <os/alloc_once_private.h>
65
66 #define ATEXIT_FN_EMPTY 0
67 #define ATEXIT_FN_STD 1
68 #define ATEXIT_FN_CXA 2
69 #ifdef __BLOCKS__
70 #define ATEXIT_FN_BLK 3
71 #endif /* __BLOCKS__ */
72
73 static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
74
75 #define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)
76 #define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)
77
78 struct atexit {
79 struct atexit *next; /* next in list */
80 int ind; /* next index in this table */
81 struct atexit_fn {
82 int fn_type; /* ATEXIT_? from above */
83 union {
84 void (*std_func)(void);
85 void (*cxa_func)(void *);
86 #ifdef __BLOCKS__
87 void (^block)(void);
88 #endif /* __BLOCKS__ */
89 } fn_ptr; /* function pointer */
90 void *fn_arg; /* argument for CXA callback */
91 void *fn_dso; /* shared module handle */
92 } fns[ATEXIT_SIZE]; /* the table itself */
93 };
94
95 static struct atexit *__atexit; /* points to head of LIFO stack */
96 static int __atexit_new_registration;
97
98 __attribute__ ((visibility ("hidden")))
99 void
100 __atexit_init(void)
101 {
102 __atexit = os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_C,
103 sizeof(struct atexit), NULL);
104 }
105
106 /*
107 * Register the function described by 'fptr' to be called at application
108 * exit or owning shared object unload time. This is a helper function
109 * for atexit and __cxa_atexit.
110 */
111 static int
112 atexit_register(struct atexit_fn *fptr)
113 {
114 struct atexit *p = __atexit;
115 assert(p);
116 _MUTEX_LOCK(&atexit_mutex);
117 while (p->ind >= ATEXIT_SIZE) {
118 struct atexit *old__atexit;
119 old__atexit = __atexit;
120 _MUTEX_UNLOCK(&atexit_mutex);
121 if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL)
122 return (-1);
123 _MUTEX_LOCK(&atexit_mutex);
124 if (old__atexit != __atexit) {
125 /* Lost race, retry operation */
126 _MUTEX_UNLOCK(&atexit_mutex);
127 free(p);
128 _MUTEX_LOCK(&atexit_mutex);
129 p = __atexit;
130 continue;
131 }
132 p->ind = 0;
133 p->next = __atexit;
134 __atexit = p;
135 }
136 p->fns[p->ind++] = *fptr;
137 __atexit_new_registration = 1;
138 _MUTEX_UNLOCK(&atexit_mutex);
139 return 0;
140 }
141
142 /*
143 * Register a function to be performed at exit.
144 */
145 int
146 atexit(void (*func)(void))
147 {
148 struct atexit_fn fn;
149 int error;
150
151 fn.fn_type = ATEXIT_FN_STD;
152 fn.fn_ptr.std_func = func;
153 fn.fn_arg = NULL;
154 fn.fn_dso = NULL;
155
156 #if defined(__DYNAMIC__) && !TARGET_OS_IPHONE && !TARGET_OS_DRIVERKIT
157 // <rdar://problem/14596032&15173956>
158 struct dl_info info;
159 if (dladdr(func, &info)) {
160 fn.fn_dso = info.dli_fbase;
161 }
162 #endif
163
164 error = atexit_register(&fn);
165 return (error);
166 }
167
168 #ifdef __BLOCKS__
169 int
170 atexit_b(void (^block)(void))
171 {
172 struct atexit_fn fn;
173 int error;
174
175 fn.fn_type = ATEXIT_FN_BLK;
176 fn.fn_ptr.block = Block_copy(block);
177 fn.fn_arg = NULL;
178 fn.fn_dso = NULL;
179
180 error = atexit_register(&fn);
181 return (error);
182 }
183 #endif /* __BLOCKS__ */
184
185 /*
186 * Register a function to be performed at exit or when an shared object
187 * with given dso handle is unloaded dynamically.
188 */
189 int
190 __cxa_atexit(void (*func)(void *), void *arg, void *dso)
191 {
192 struct atexit_fn fn;
193 int error;
194
195 fn.fn_type = ATEXIT_FN_CXA;
196 fn.fn_ptr.cxa_func = func;;
197 fn.fn_arg = arg;
198 fn.fn_dso = dso;
199
200 error = atexit_register(&fn);
201 return (error);
202 }
203
204 static bool
205 __cxa_in_range(const struct __cxa_range_t ranges[],
206 unsigned int count,
207 const void* fn)
208 {
209 uintptr_t addr = (uintptr_t)fn;
210
211 unsigned int i;
212 for (i = 0; i < count; ++i) {
213 const struct __cxa_range_t *r = &ranges[i];
214 if (addr < (uintptr_t)r->addr) {
215 continue;
216 }
217 if (addr < ((uintptr_t)r->addr + r->length)) {
218 return true;
219 }
220 }
221 return false;
222 }
223
224 /*
225 * Call handlers registered via __cxa_atexit/atexit that are in a
226 * a range specified.
227 * Note: rangeCount==0, means call all handlers.
228 */
229 void
230 __cxa_finalize_ranges(const struct __cxa_range_t ranges[], unsigned int count)
231 {
232 struct atexit *p;
233 struct atexit_fn *fn;
234 int n;
235 _MUTEX_LOCK(&atexit_mutex);
236
237 restart:
238 for (p = __atexit; p; p = p->next) {
239 for (n = p->ind; --n >= 0;) {
240 fn = &p->fns[n];
241
242 if (fn->fn_type == ATEXIT_FN_EMPTY) {
243 continue; // already been called
244 }
245
246 // Verify that the entry is within the range being unloaded.
247 if (count > 0) {
248 if (fn->fn_type == ATEXIT_FN_CXA) {
249 // for __cxa_atexit(), call if *dso* is in range be unloaded
250 if (!__cxa_in_range(ranges, count, fn->fn_dso)) {
251 continue; // not being unloaded yet
252 }
253 } else if (fn->fn_type == ATEXIT_FN_STD) {
254 // for atexit, call if *function* is in range be unloaded
255 if (!__cxa_in_range(ranges, count, fn->fn_ptr.std_func)) {
256 continue; // not being unloaded yet
257 }
258 #ifdef __BLOCKS__
259 } else if (fn->fn_type == ATEXIT_FN_BLK) {
260 // for atexit_b, call if block code is in range be unloaded
261 void *a = ((struct Block_layout *)fn->fn_ptr.block)->invoke;
262 if (!__cxa_in_range(ranges, count, a)) {
263 continue; // not being unloaded yet
264 }
265 #endif // __BLOCKS__
266 }
267 }
268
269 // Clear the entry to indicate that this handler has been called.
270 int fn_type = fn->fn_type;
271 fn->fn_type = ATEXIT_FN_EMPTY;
272
273 // Detect recursive registrations.
274 __atexit_new_registration = 0;
275 _MUTEX_UNLOCK(&atexit_mutex);
276
277 // Call the handler.
278 if (fn_type == ATEXIT_FN_CXA) {
279 fn->fn_ptr.cxa_func(fn->fn_arg);
280 } else if (fn_type == ATEXIT_FN_STD) {
281 fn->fn_ptr.std_func();
282 #ifdef __BLOCKS__
283 } else if (fn_type == ATEXIT_FN_BLK) {
284 fn->fn_ptr.block();
285 #endif // __BLOCKS__
286 }
287
288 // Call any recursively registered handlers.
289 _MUTEX_LOCK(&atexit_mutex);
290 if (__atexit_new_registration) {
291 goto restart;
292 }
293 }
294 }
295 _MUTEX_UNLOCK(&atexit_mutex);
296 }
297
298
299 /*
300 * Call all handlers registered with __cxa_atexit for the shared
301 * object owning 'dso'. Note: if 'dso' is NULL, then all remaining
302 * handlers are called.
303 */
304 void
305 __cxa_finalize(const void *dso)
306 {
307 if (dso != NULL) {
308 // Note: this should not happen as only dyld should be calling
309 // this and dyld has switched to call __cxa_finalize_ranges directly.
310 struct __cxa_range_t range;
311 range.addr = dso;
312 range.length = 1;
313 __cxa_finalize_ranges(&range, 1);
314 } else {
315 __cxa_finalize_ranges(NULL, 0);
316 }
317 }
318
319 #if !TARGET_IPHONE_SIMULATOR && (__i386__ || __x86_64__)
320 /*
321 * Support for thread_local in C++, using existing _tlv_atexit() in libdyld
322 */
323
324 void _tlv_atexit(void(*f)(void*), void* arg); /* in libdyld */
325
326 void
327 __cxa_thread_atexit(void(*f)(void*), void* arg)
328 {
329 _tlv_atexit(f, arg);
330 }
331 #endif
332 #pragma clang diagnostic pop