1*27ef7ea7Sjoerg /* $NetBSD: atexit.c,v 1.32 2017/11/06 14:26:03 joerg Exp $ */
26dda330eSthorpej
361f28255Scgd /*-
426be93faSthorpej * Copyright (c) 2003 The NetBSD Foundation, Inc.
526be93faSthorpej * All rights reserved.
661f28255Scgd *
726be93faSthorpej * This code is derived from software contributed to The NetBSD Foundation
826be93faSthorpej * by Jason R. Thorpe.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
1861f28255Scgd *
1926be93faSthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2026be93faSthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2126be93faSthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2226be93faSthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2326be93faSthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2426be93faSthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2526be93faSthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2626be93faSthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2726be93faSthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2826be93faSthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2926be93faSthorpej * POSSIBILITY OF SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
3288c3eadbSlukem #include <sys/cdefs.h>
3388c3eadbSlukem #if defined(LIBC_SCCS) && !defined(lint)
34*27ef7ea7Sjoerg __RCSID("$NetBSD: atexit.c,v 1.32 2017/11/06 14:26:03 joerg Exp $");
3588c3eadbSlukem #endif /* LIBC_SCCS and not lint */
3688c3eadbSlukem
3726be93faSthorpej #include "reentrant.h"
3861f28255Scgd
39b48252f3Slukem #include <assert.h>
4061f28255Scgd #include <stdlib.h>
4126be93faSthorpej
4261f28255Scgd #include "atexit.h"
4361f28255Scgd
4426be93faSthorpej struct atexit_handler {
4526be93faSthorpej struct atexit_handler *ah_next;
4626be93faSthorpej union {
4726be93faSthorpej void (*fun_atexit)(void);
4826be93faSthorpej void (*fun_cxa_atexit)(void *);
4926be93faSthorpej } ah_fun;
5026be93faSthorpej #define ah_atexit ah_fun.fun_atexit
5126be93faSthorpej #define ah_cxa_atexit ah_fun.fun_cxa_atexit
5226be93faSthorpej
5326be93faSthorpej void *ah_arg; /* argument for cxa_atexit handlers */
5426be93faSthorpej void *ah_dso; /* home DSO for cxa_atexit handlers */
5526be93faSthorpej };
5626be93faSthorpej
5726be93faSthorpej /*
5826be93faSthorpej * There must be at least 32 to guarantee ANSI conformance, plus
5926be93faSthorpej * 3 additional ones for the benefit of the startup code, which
6026be93faSthorpej * may use them to register the dynamic loader's cleanup routine,
6126be93faSthorpej * the profiling cleanup routine, and the global destructor routine.
6226be93faSthorpej */
6326be93faSthorpej #define NSTATIC_HANDLERS (32 + 3)
6426be93faSthorpej static struct atexit_handler atexit_handler0[NSTATIC_HANDLERS];
6526be93faSthorpej
6626be93faSthorpej #define STATIC_HANDLER_P(ah) \
6726be93faSthorpej (ah >= &atexit_handler0[0] && ah < &atexit_handler0[NSTATIC_HANDLERS])
6826be93faSthorpej
6926be93faSthorpej /*
7026be93faSthorpej * Stack of atexit handlers. Handlers must be called in the opposite
7126be93faSthorpej * order they were registered.
7226be93faSthorpej */
7326be93faSthorpej static struct atexit_handler *atexit_handler_stack;
746eaa1d60Sjtc
753fdac2b8Sthorpej #ifdef _REENTRANT
7626be93faSthorpej /* ..and a mutex to protect it all. */
77f5c1f214Schristos mutex_t __atexit_mutex;
7826be93faSthorpej #endif /* _REENTRANT */
7926be93faSthorpej
8095157b04Sxtraeme void __libc_atexit_init(void) __attribute__ ((visibility("hidden")));
8195157b04Sxtraeme
8226be93faSthorpej /*
8326be93faSthorpej * Allocate an atexit handler descriptor. If "dso" is NULL, it indicates
8426be93faSthorpej * a normal atexit handler, which must be allocated from the static pool,
8526be93faSthorpej * if possible. cxa_atexit handlers are never allocated from the static
8626be93faSthorpej * pool.
8726be93faSthorpej *
88f5c1f214Schristos * __atexit_mutex must be held.
8926be93faSthorpej */
9026be93faSthorpej static struct atexit_handler *
atexit_handler_alloc(void * dso)9126be93faSthorpej atexit_handler_alloc(void *dso)
9226be93faSthorpej {
9326be93faSthorpej struct atexit_handler *ah;
9426be93faSthorpej int i;
9526be93faSthorpej
9626be93faSthorpej if (dso == NULL) {
9726be93faSthorpej for (i = 0; i < NSTATIC_HANDLERS; i++) {
9826be93faSthorpej ah = &atexit_handler0[i];
99af220472Skristerw if (ah->ah_atexit == NULL && ah->ah_next == NULL) {
10026be93faSthorpej /* Slot is free. */
10126be93faSthorpej return (ah);
10226be93faSthorpej }
10326be93faSthorpej }
10426be93faSthorpej }
10526be93faSthorpej
10626be93faSthorpej /*
10726be93faSthorpej * Either no static slot was free, or this is a cxa_atexit
108f5c1f214Schristos * handler. Allocate a new one. We keep the __atexit_mutex
10926be93faSthorpej * held to prevent handlers from being run while we (potentially)
11026be93faSthorpej * block in malloc().
11126be93faSthorpej */
11226be93faSthorpej ah = malloc(sizeof(*ah));
11326be93faSthorpej return (ah);
11426be93faSthorpej }
11526be93faSthorpej
1161b5143aaSxtraeme /*
117f5c1f214Schristos * Initialize __atexit_mutex with the PTHREAD_MUTEX_RECURSIVE attribute.
1181b5143aaSxtraeme * Note that __cxa_finalize may generate calls to __cxa_atexit.
1191b5143aaSxtraeme */
1208caf1030Smatt void __section(".text.startup")
__libc_atexit_init(void)12195157b04Sxtraeme __libc_atexit_init(void)
12295157b04Sxtraeme {
12326ba8048Schristos #ifdef _REENTRANT
12495157b04Sxtraeme mutexattr_t atexit_mutex_attr;
12595157b04Sxtraeme mutexattr_init(&atexit_mutex_attr);
12695157b04Sxtraeme mutexattr_settype(&atexit_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
127f5c1f214Schristos mutex_init(&__atexit_mutex, &atexit_mutex_attr);
12826ba8048Schristos #endif
12995157b04Sxtraeme }
13095157b04Sxtraeme
13126be93faSthorpej /*
13226be93faSthorpej * Register an atexit routine. This is suitable either for a cxa_atexit
13326be93faSthorpej * or normal atexit type handler. The __cxa_atexit() name and arguments
13426be93faSthorpej * are specified by the C++ ABI. See:
13526be93faSthorpej *
13626be93faSthorpej * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
13726be93faSthorpej */
138e88efb11Sjoerg #if defined(__ARM_EABI__) && !defined(lint)
13966e8846bSjoerg int
14066e8846bSjoerg __aeabi_atexit(void *arg, void (*func)(void *), void *dso);
14166e8846bSjoerg
14266e8846bSjoerg int
__aeabi_atexit(void * arg,void (* func)(void *),void * dso)14366e8846bSjoerg __aeabi_atexit(void *arg, void (*func)(void *), void *dso)
14466e8846bSjoerg {
145*27ef7ea7Sjoerg return (__cxa_atexit(func, arg, dso));
14666e8846bSjoerg }
147e88efb11Sjoerg #endif
148e88efb11Sjoerg
149*27ef7ea7Sjoerg static int
__cxa_atexit_internal(void (* func)(void *),void * arg,void * dso)150*27ef7ea7Sjoerg __cxa_atexit_internal(void (*func)(void *), void *arg, void *dso)
15126be93faSthorpej {
15226be93faSthorpej struct atexit_handler *ah;
15326be93faSthorpej
15426be93faSthorpej _DIAGASSERT(func != NULL);
15526be93faSthorpej
156f5c1f214Schristos mutex_lock(&__atexit_mutex);
15726be93faSthorpej
15826be93faSthorpej ah = atexit_handler_alloc(dso);
15926be93faSthorpej if (ah == NULL) {
160f5c1f214Schristos mutex_unlock(&__atexit_mutex);
16126be93faSthorpej return (-1);
16226be93faSthorpej }
16326be93faSthorpej
16426be93faSthorpej ah->ah_cxa_atexit = func;
16526be93faSthorpej ah->ah_arg = arg;
16626be93faSthorpej ah->ah_dso = dso;
16726be93faSthorpej
16826be93faSthorpej ah->ah_next = atexit_handler_stack;
16926be93faSthorpej atexit_handler_stack = ah;
17026be93faSthorpej
171f5c1f214Schristos mutex_unlock(&__atexit_mutex);
17226be93faSthorpej return (0);
17326be93faSthorpej }
17426be93faSthorpej
175*27ef7ea7Sjoerg int
__cxa_atexit(void (* func)(void *),void * arg,void * dso)176*27ef7ea7Sjoerg __cxa_atexit(void (*func)(void *), void *arg, void *dso)
177*27ef7ea7Sjoerg {
178*27ef7ea7Sjoerg _DIAGASSERT(dso != NULL);
179*27ef7ea7Sjoerg return (__cxa_atexit_internal(func, arg, dso));
180*27ef7ea7Sjoerg }
181*27ef7ea7Sjoerg
18226be93faSthorpej /*
18326be93faSthorpej * Run the list of atexit handlers. If dso is NULL, run all of them,
18426be93faSthorpej * otherwise run only those matching the specified dso.
18565ec6ba6Sthorpej *
18665ec6ba6Sthorpej * Note that we can be recursively invoked; rtld cleanup is via an
18765ec6ba6Sthorpej * atexit handler, and rtld cleanup invokes _fini() for DSOs, which
18865ec6ba6Sthorpej * in turn invokes __cxa_finalize() for the DSO.
18926be93faSthorpej */
19026be93faSthorpej void
__cxa_finalize(void * dso)19126be93faSthorpej __cxa_finalize(void *dso)
19226be93faSthorpej {
19365ec6ba6Sthorpej static u_int call_depth;
19426be93faSthorpej struct atexit_handler *ah, *dead_handlers = NULL, **prevp;
19565ec6ba6Sthorpej void (*cxa_func)(void *);
19665ec6ba6Sthorpej void (*atexit_func)(void);
19726be93faSthorpej
198f5c1f214Schristos mutex_lock(&__atexit_mutex);
19965ec6ba6Sthorpej call_depth++;
20065ec6ba6Sthorpej
20165ec6ba6Sthorpej /*
20265ec6ba6Sthorpej * If we are at call depth 1 (which is usually the "do everything"
20365ec6ba6Sthorpej * call from exit(3)), we go ahead and remove elements from the
20465ec6ba6Sthorpej * list as we call them. This will prevent any nested calls from
20565ec6ba6Sthorpej * having to traverse elements we've already processed. If we are
20665ec6ba6Sthorpej * at call depth > 1, we simply mark elements we process as unused.
20765ec6ba6Sthorpej * When the depth 1 caller sees those, it will simply unlink them
20865ec6ba6Sthorpej * for us.
20965ec6ba6Sthorpej */
2103a6ef3e3Skristerw again:
21126be93faSthorpej for (prevp = &atexit_handler_stack; (ah = (*prevp)) != NULL;) {
21265ec6ba6Sthorpej if (dso == NULL || dso == ah->ah_dso || ah->ah_atexit == NULL) {
21365ec6ba6Sthorpej if (ah->ah_atexit != NULL) {
2143a6ef3e3Skristerw void *p = atexit_handler_stack;
2153720ea4fSkamil if (ah->ah_dso != NULL) {
21665ec6ba6Sthorpej cxa_func = ah->ah_cxa_atexit;
21765ec6ba6Sthorpej ah->ah_cxa_atexit = NULL;
21865ec6ba6Sthorpej (*cxa_func)(ah->ah_arg);
21965ec6ba6Sthorpej } else {
22065ec6ba6Sthorpej atexit_func = ah->ah_atexit;
22165ec6ba6Sthorpej ah->ah_atexit = NULL;
22265ec6ba6Sthorpej (*atexit_func)();
22365ec6ba6Sthorpej }
2243a6ef3e3Skristerw /* Restart if new atexit handler was added. */
2253a6ef3e3Skristerw if (p != atexit_handler_stack)
2263a6ef3e3Skristerw goto again;
22765ec6ba6Sthorpej }
22826be93faSthorpej
22965ec6ba6Sthorpej if (call_depth == 1) {
23026be93faSthorpej *prevp = ah->ah_next;
231af220472Skristerw if (STATIC_HANDLER_P(ah))
232af220472Skristerw ah->ah_next = NULL;
233af220472Skristerw else {
23426be93faSthorpej ah->ah_next = dead_handlers;
23526be93faSthorpej dead_handlers = ah;
23626be93faSthorpej }
23726be93faSthorpej } else
23826be93faSthorpej prevp = &ah->ah_next;
23965ec6ba6Sthorpej } else
24065ec6ba6Sthorpej prevp = &ah->ah_next;
24126be93faSthorpej }
242aa70f03bSnathanw call_depth--;
243f5c1f214Schristos mutex_unlock(&__atexit_mutex);
244aa70f03bSnathanw
245aa70f03bSnathanw if (call_depth > 0)
24665ec6ba6Sthorpej return;
24765ec6ba6Sthorpej
24826be93faSthorpej /*
24926be93faSthorpej * Now free any dead handlers. Do this even if we're about to
25026be93faSthorpej * exit, in case a leak-detecting malloc is being used.
25126be93faSthorpej */
25226be93faSthorpej while ((ah = dead_handlers) != NULL) {
25326be93faSthorpej dead_handlers = ah->ah_next;
25426be93faSthorpej free(ah);
25526be93faSthorpej }
25626be93faSthorpej }
257cd85b5e5Skleink
25861f28255Scgd /*
25961f28255Scgd * Register a function to be performed at exit.
26061f28255Scgd */
26161f28255Scgd int
atexit(void (* func)(void))26226be93faSthorpej atexit(void (*func)(void))
26361f28255Scgd {
26461f28255Scgd
265*27ef7ea7Sjoerg return (__cxa_atexit_internal((void (*)(void *))func, NULL, NULL));
26661f28255Scgd }
267