]> git.saurik.com Git - apple/libc.git/blame - stdlib/FreeBSD/atexit.c
Libc-1439.100.3.tar.gz
[apple/libc.git] / stdlib / FreeBSD / atexit.c
CommitLineData
9385eb3d 1/*-
e9ce8d39
A
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.
e9ce8d39
A
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
70ad1dc8
A
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
9385eb3d
A
39#if defined(LIBC_SCCS) && !defined(lint)
40static char sccsid[] = "@(#)atexit.c 8.2 (Berkeley) 7/3/94";
41#endif /* LIBC_SCCS and not lint */
42#include <sys/cdefs.h>
1f2f436a 43__FBSDID("$FreeBSD: src/lib/libc/stdlib/atexit.c,v 1.8 2007/01/09 00:28:09 imp Exp $");
507116e3 44#include <TargetConditionals.h>
e9ce8d39 45
9385eb3d 46#include "namespace.h"
23e20b00 47#include <errno.h>
e9ce8d39
A
48#include <stddef.h>
49#include <stdlib.h>
9385eb3d 50#include <unistd.h>
23e20b00 51#include <assert.h>
9385eb3d 52#include <pthread.h>
507116e3 53#if (defined(__DYNAMIC__) || defined (__BLOCKS__)) && !TARGET_OS_DRIVERKIT
ad3c9f2a
A
54#include <dlfcn.h>
55#endif /* defined(__DYNAMIC__) */
e9ce8d39 56#include "atexit.h"
9385eb3d
A
57#include "un-namespace.h"
58
ad3c9f2a
A
59#ifdef __BLOCKS__
60#include <Block.h>
23e20b00 61#include <Block_private.h>
ad3c9f2a 62#endif /* __BLOCKS__ */
9385eb3d 63#include "libc_private.h"
23e20b00 64#include <os/alloc_once_private.h>
9385eb3d 65
59e0d9fe
A
66#define ATEXIT_FN_EMPTY 0
67#define ATEXIT_FN_STD 1
68#define ATEXIT_FN_CXA 2
ad3c9f2a
A
69#ifdef __BLOCKS__
70#define ATEXIT_FN_BLK 3
71#endif /* __BLOCKS__ */
59e0d9fe 72
9385eb3d
A
73static 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)
e9ce8d39 77
59e0d9fe
A
78struct 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 *);
ad3c9f2a
A
86#ifdef __BLOCKS__
87 void (^block)(void);
88#endif /* __BLOCKS__ */
59e0d9fe
A
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
95static struct atexit *__atexit; /* points to head of LIFO stack */
23e20b00
A
96static int __atexit_new_registration;
97
98__attribute__ ((visibility ("hidden")))
99void
100__atexit_init(void)
101{
102 __atexit = os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_C,
103 sizeof(struct atexit), NULL);
104}
e9ce8d39
A
105
106/*
59e0d9fe
A
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.
e9ce8d39 110 */
59e0d9fe
A
111static int
112atexit_register(struct atexit_fn *fptr)
e9ce8d39 113{
23e20b00
A
114 struct atexit *p = __atexit;
115 assert(p);
9385eb3d 116 _MUTEX_LOCK(&atexit_mutex);
23e20b00 117 while (p->ind >= ATEXIT_SIZE) {
9385eb3d
A
118 struct atexit *old__atexit;
119 old__atexit = __atexit;
120 _MUTEX_UNLOCK(&atexit_mutex);
121 if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL)
e9ce8d39 122 return (-1);
9385eb3d
A
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 }
e9ce8d39
A
132 p->ind = 0;
133 p->next = __atexit;
134 __atexit = p;
135 }
59e0d9fe 136 p->fns[p->ind++] = *fptr;
23e20b00 137 __atexit_new_registration = 1;
59e0d9fe
A
138 _MUTEX_UNLOCK(&atexit_mutex);
139 return 0;
140}
141
142/*
143 * Register a function to be performed at exit.
144 */
145int
146atexit(void (*func)(void))
147{
148 struct atexit_fn fn;
149 int error;
150
151 fn.fn_type = ATEXIT_FN_STD;
ad3c9f2a 152 fn.fn_ptr.std_func = func;
59e0d9fe
A
153 fn.fn_arg = NULL;
154 fn.fn_dso = NULL;
23e20b00 155
507116e3 156#if defined(__DYNAMIC__) && !TARGET_OS_IPHONE && !TARGET_OS_DRIVERKIT
23e20b00
A
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
59e0d9fe
A
163
164 error = atexit_register(&fn);
165 return (error);
166}
167
ad3c9f2a
A
168#ifdef __BLOCKS__
169int
170atexit_b(void (^block)(void))
171{
172 struct atexit_fn fn;
ad3c9f2a
A
173 int error;
174
175 fn.fn_type = ATEXIT_FN_BLK;
176 fn.fn_ptr.block = Block_copy(block);
177 fn.fn_arg = NULL;
ad3c9f2a 178 fn.fn_dso = NULL;
ad3c9f2a
A
179
180 error = atexit_register(&fn);
181 return (error);
182}
183#endif /* __BLOCKS__ */
184
59e0d9fe
A
185/*
186 * Register a function to be performed at exit or when an shared object
187 * with given dso handle is unloaded dynamically.
188 */
189int
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
23e20b00
A
204static 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
59e0d9fe 224/*
23e20b00
A
225 * Call handlers registered via __cxa_atexit/atexit that are in a
226 * a range specified.
227 * Note: rangeCount==0, means call all handlers.
59e0d9fe
A
228 */
229void
23e20b00 230__cxa_finalize_ranges(const struct __cxa_range_t ranges[], unsigned int count)
59e0d9fe
A
231{
232 struct atexit *p;
23e20b00 233 struct atexit_fn *fn;
59e0d9fe 234 int n;
59e0d9fe 235 _MUTEX_LOCK(&atexit_mutex);
23e20b00 236
ad3c9f2a 237restart:
59e0d9fe
A
238 for (p = __atexit; p; p = p->next) {
239 for (n = p->ind; --n >= 0;) {
23e20b00
A
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 }
ad3c9f2a 258#ifdef __BLOCKS__
23e20b00
A
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.
59e0d9fe 289 _MUTEX_LOCK(&atexit_mutex);
23e20b00 290 if (__atexit_new_registration) {
ad3c9f2a 291 goto restart;
23e20b00 292 }
59e0d9fe
A
293 }
294 }
9385eb3d 295 _MUTEX_UNLOCK(&atexit_mutex);
e9ce8d39 296}
6465356a 297
23e20b00
A
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 */
304void
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
a9aaacca 319#if TARGET_OS_OSX
6465356a
A
320/*
321 * Support for thread_local in C++, using existing _tlv_atexit() in libdyld
322 */
323
324void _tlv_atexit(void(*f)(void*), void* arg); /* in libdyld */
325
326void
327__cxa_thread_atexit(void(*f)(void*), void* arg)
328{
329 _tlv_atexit(f, arg);
330}
331#endif
70ad1dc8 332#pragma clang diagnostic pop