xref: /netbsd-src/lib/libc/stdlib/atexit.c (revision 27ef7ea778bca0a2b2bff6025d41b3fb2e710f89)
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