1 1.32 joerg /* $NetBSD: atexit.c,v 1.32 2017/11/06 14:26:03 joerg Exp $ */ 2 1.6 thorpej 3 1.1 cgd /*- 4 1.14 thorpej * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 1.14 thorpej * All rights reserved. 6 1.1 cgd * 7 1.14 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.14 thorpej * by Jason R. Thorpe. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.1 cgd * 19 1.14 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.14 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.14 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.14 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.14 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.14 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.14 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.14 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.14 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.14 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.14 thorpej * POSSIBILITY OF SUCH DAMAGE. 30 1.14 thorpej */ 31 1.14 thorpej 32 1.17 lukem #include <sys/cdefs.h> 33 1.17 lukem #if defined(LIBC_SCCS) && !defined(lint) 34 1.32 joerg __RCSID("$NetBSD: atexit.c,v 1.32 2017/11/06 14:26:03 joerg Exp $"); 35 1.17 lukem #endif /* LIBC_SCCS and not lint */ 36 1.17 lukem 37 1.14 thorpej #include "reentrant.h" 38 1.1 cgd 39 1.11 lukem #include <assert.h> 40 1.1 cgd #include <stdlib.h> 41 1.14 thorpej 42 1.1 cgd #include "atexit.h" 43 1.5 jtc 44 1.14 thorpej struct atexit_handler { 45 1.14 thorpej struct atexit_handler *ah_next; 46 1.14 thorpej union { 47 1.14 thorpej void (*fun_atexit)(void); 48 1.14 thorpej void (*fun_cxa_atexit)(void *); 49 1.14 thorpej } ah_fun; 50 1.14 thorpej #define ah_atexit ah_fun.fun_atexit 51 1.14 thorpej #define ah_cxa_atexit ah_fun.fun_cxa_atexit 52 1.14 thorpej 53 1.14 thorpej void *ah_arg; /* argument for cxa_atexit handlers */ 54 1.14 thorpej void *ah_dso; /* home DSO for cxa_atexit handlers */ 55 1.14 thorpej }; 56 1.14 thorpej 57 1.14 thorpej /* 58 1.14 thorpej * There must be at least 32 to guarantee ANSI conformance, plus 59 1.14 thorpej * 3 additional ones for the benefit of the startup code, which 60 1.14 thorpej * may use them to register the dynamic loader's cleanup routine, 61 1.14 thorpej * the profiling cleanup routine, and the global destructor routine. 62 1.14 thorpej */ 63 1.14 thorpej #define NSTATIC_HANDLERS (32 + 3) 64 1.14 thorpej static struct atexit_handler atexit_handler0[NSTATIC_HANDLERS]; 65 1.14 thorpej 66 1.14 thorpej #define STATIC_HANDLER_P(ah) \ 67 1.14 thorpej (ah >= &atexit_handler0[0] && ah < &atexit_handler0[NSTATIC_HANDLERS]) 68 1.14 thorpej 69 1.14 thorpej /* 70 1.14 thorpej * Stack of atexit handlers. Handlers must be called in the opposite 71 1.14 thorpej * order they were registered. 72 1.14 thorpej */ 73 1.14 thorpej static struct atexit_handler *atexit_handler_stack; 74 1.1 cgd 75 1.13 thorpej #ifdef _REENTRANT 76 1.14 thorpej /* ..and a mutex to protect it all. */ 77 1.25 christos mutex_t __atexit_mutex; 78 1.14 thorpej #endif /* _REENTRANT */ 79 1.10 kleink 80 1.20 xtraeme void __libc_atexit_init(void) __attribute__ ((visibility("hidden"))); 81 1.20 xtraeme 82 1.1 cgd /* 83 1.14 thorpej * Allocate an atexit handler descriptor. If "dso" is NULL, it indicates 84 1.14 thorpej * a normal atexit handler, which must be allocated from the static pool, 85 1.14 thorpej * if possible. cxa_atexit handlers are never allocated from the static 86 1.14 thorpej * pool. 87 1.14 thorpej * 88 1.25 christos * __atexit_mutex must be held. 89 1.14 thorpej */ 90 1.14 thorpej static struct atexit_handler * 91 1.14 thorpej atexit_handler_alloc(void *dso) 92 1.14 thorpej { 93 1.14 thorpej struct atexit_handler *ah; 94 1.14 thorpej int i; 95 1.14 thorpej 96 1.14 thorpej if (dso == NULL) { 97 1.14 thorpej for (i = 0; i < NSTATIC_HANDLERS; i++) { 98 1.14 thorpej ah = &atexit_handler0[i]; 99 1.18 kristerw if (ah->ah_atexit == NULL && ah->ah_next == NULL) { 100 1.14 thorpej /* Slot is free. */ 101 1.14 thorpej return (ah); 102 1.14 thorpej } 103 1.14 thorpej } 104 1.14 thorpej } 105 1.14 thorpej 106 1.14 thorpej /* 107 1.14 thorpej * Either no static slot was free, or this is a cxa_atexit 108 1.25 christos * handler. Allocate a new one. We keep the __atexit_mutex 109 1.14 thorpej * held to prevent handlers from being run while we (potentially) 110 1.14 thorpej * block in malloc(). 111 1.14 thorpej */ 112 1.14 thorpej ah = malloc(sizeof(*ah)); 113 1.14 thorpej return (ah); 114 1.14 thorpej } 115 1.14 thorpej 116 1.21 xtraeme /* 117 1.25 christos * Initialize __atexit_mutex with the PTHREAD_MUTEX_RECURSIVE attribute. 118 1.21 xtraeme * Note that __cxa_finalize may generate calls to __cxa_atexit. 119 1.21 xtraeme */ 120 1.26 matt void __section(".text.startup") 121 1.20 xtraeme __libc_atexit_init(void) 122 1.20 xtraeme { 123 1.27 christos #ifdef _REENTRANT 124 1.20 xtraeme mutexattr_t atexit_mutex_attr; 125 1.20 xtraeme mutexattr_init(&atexit_mutex_attr); 126 1.20 xtraeme mutexattr_settype(&atexit_mutex_attr, PTHREAD_MUTEX_RECURSIVE); 127 1.25 christos mutex_init(&__atexit_mutex, &atexit_mutex_attr); 128 1.27 christos #endif 129 1.20 xtraeme } 130 1.20 xtraeme 131 1.14 thorpej /* 132 1.14 thorpej * Register an atexit routine. This is suitable either for a cxa_atexit 133 1.14 thorpej * or normal atexit type handler. The __cxa_atexit() name and arguments 134 1.14 thorpej * are specified by the C++ ABI. See: 135 1.14 thorpej * 136 1.14 thorpej * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor 137 1.1 cgd */ 138 1.28 joerg #if defined(__ARM_EABI__) && !defined(lint) 139 1.29 joerg int 140 1.29 joerg __aeabi_atexit(void *arg, void (*func)(void *), void *dso); 141 1.29 joerg 142 1.29 joerg int 143 1.29 joerg __aeabi_atexit(void *arg, void (*func)(void *), void *dso) 144 1.29 joerg { 145 1.32 joerg return (__cxa_atexit(func, arg, dso)); 146 1.29 joerg } 147 1.28 joerg #endif 148 1.28 joerg 149 1.32 joerg static int 150 1.32 joerg __cxa_atexit_internal(void (*func)(void *), void *arg, void *dso) 151 1.1 cgd { 152 1.14 thorpej struct atexit_handler *ah; 153 1.11 lukem 154 1.14 thorpej _DIAGASSERT(func != NULL); 155 1.1 cgd 156 1.25 christos mutex_lock(&__atexit_mutex); 157 1.14 thorpej 158 1.14 thorpej ah = atexit_handler_alloc(dso); 159 1.14 thorpej if (ah == NULL) { 160 1.25 christos mutex_unlock(&__atexit_mutex); 161 1.14 thorpej return (-1); 162 1.1 cgd } 163 1.14 thorpej 164 1.14 thorpej ah->ah_cxa_atexit = func; 165 1.14 thorpej ah->ah_arg = arg; 166 1.14 thorpej ah->ah_dso = dso; 167 1.14 thorpej 168 1.14 thorpej ah->ah_next = atexit_handler_stack; 169 1.14 thorpej atexit_handler_stack = ah; 170 1.14 thorpej 171 1.25 christos mutex_unlock(&__atexit_mutex); 172 1.1 cgd return (0); 173 1.14 thorpej } 174 1.14 thorpej 175 1.32 joerg int 176 1.32 joerg __cxa_atexit(void (*func)(void *), void *arg, void *dso) 177 1.32 joerg { 178 1.32 joerg _DIAGASSERT(dso != NULL); 179 1.32 joerg return (__cxa_atexit_internal(func, arg, dso)); 180 1.32 joerg } 181 1.32 joerg 182 1.14 thorpej /* 183 1.14 thorpej * Run the list of atexit handlers. If dso is NULL, run all of them, 184 1.14 thorpej * otherwise run only those matching the specified dso. 185 1.15 thorpej * 186 1.15 thorpej * Note that we can be recursively invoked; rtld cleanup is via an 187 1.15 thorpej * atexit handler, and rtld cleanup invokes _fini() for DSOs, which 188 1.15 thorpej * in turn invokes __cxa_finalize() for the DSO. 189 1.14 thorpej */ 190 1.14 thorpej void 191 1.14 thorpej __cxa_finalize(void *dso) 192 1.14 thorpej { 193 1.15 thorpej static u_int call_depth; 194 1.14 thorpej struct atexit_handler *ah, *dead_handlers = NULL, **prevp; 195 1.15 thorpej void (*cxa_func)(void *); 196 1.15 thorpej void (*atexit_func)(void); 197 1.15 thorpej 198 1.25 christos mutex_lock(&__atexit_mutex); 199 1.15 thorpej call_depth++; 200 1.14 thorpej 201 1.15 thorpej /* 202 1.15 thorpej * If we are at call depth 1 (which is usually the "do everything" 203 1.15 thorpej * call from exit(3)), we go ahead and remove elements from the 204 1.15 thorpej * list as we call them. This will prevent any nested calls from 205 1.15 thorpej * having to traverse elements we've already processed. If we are 206 1.15 thorpej * at call depth > 1, we simply mark elements we process as unused. 207 1.15 thorpej * When the depth 1 caller sees those, it will simply unlink them 208 1.15 thorpej * for us. 209 1.15 thorpej */ 210 1.19 kristerw again: 211 1.14 thorpej for (prevp = &atexit_handler_stack; (ah = (*prevp)) != NULL;) { 212 1.15 thorpej if (dso == NULL || dso == ah->ah_dso || ah->ah_atexit == NULL) { 213 1.15 thorpej if (ah->ah_atexit != NULL) { 214 1.19 kristerw void *p = atexit_handler_stack; 215 1.31 kamil if (ah->ah_dso != NULL) { 216 1.15 thorpej cxa_func = ah->ah_cxa_atexit; 217 1.15 thorpej ah->ah_cxa_atexit = NULL; 218 1.15 thorpej (*cxa_func)(ah->ah_arg); 219 1.15 thorpej } else { 220 1.15 thorpej atexit_func = ah->ah_atexit; 221 1.15 thorpej ah->ah_atexit = NULL; 222 1.15 thorpej (*atexit_func)(); 223 1.15 thorpej } 224 1.19 kristerw /* Restart if new atexit handler was added. */ 225 1.19 kristerw if (p != atexit_handler_stack) 226 1.19 kristerw goto again; 227 1.14 thorpej } 228 1.15 thorpej 229 1.15 thorpej if (call_depth == 1) { 230 1.15 thorpej *prevp = ah->ah_next; 231 1.18 kristerw if (STATIC_HANDLER_P(ah)) 232 1.18 kristerw ah->ah_next = NULL; 233 1.18 kristerw else { 234 1.15 thorpej ah->ah_next = dead_handlers; 235 1.15 thorpej dead_handlers = ah; 236 1.15 thorpej } 237 1.15 thorpej } else 238 1.15 thorpej prevp = &ah->ah_next; 239 1.14 thorpej } else 240 1.14 thorpej prevp = &ah->ah_next; 241 1.14 thorpej } 242 1.16 nathanw call_depth--; 243 1.25 christos mutex_unlock(&__atexit_mutex); 244 1.16 nathanw 245 1.16 nathanw if (call_depth > 0) 246 1.15 thorpej return; 247 1.14 thorpej 248 1.14 thorpej /* 249 1.14 thorpej * Now free any dead handlers. Do this even if we're about to 250 1.14 thorpej * exit, in case a leak-detecting malloc is being used. 251 1.14 thorpej */ 252 1.14 thorpej while ((ah = dead_handlers) != NULL) { 253 1.14 thorpej dead_handlers = ah->ah_next; 254 1.14 thorpej free(ah); 255 1.14 thorpej } 256 1.14 thorpej } 257 1.14 thorpej 258 1.14 thorpej /* 259 1.14 thorpej * Register a function to be performed at exit. 260 1.14 thorpej */ 261 1.14 thorpej int 262 1.14 thorpej atexit(void (*func)(void)) 263 1.14 thorpej { 264 1.14 thorpej 265 1.32 joerg return (__cxa_atexit_internal((void (*)(void *))func, NULL, NULL)); 266 1.1 cgd } 267