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