1327952Sdim/*- 2320957Sdim * Copyright (c) 1990, 1993 3353358Sdim * The Regents of the University of California. All rights reserved. 4353358Sdim * 5353358Sdim * This code is derived from software contributed to Berkeley by 6320957Sdim * Chris Torek. 7320957Sdim * 8320957Sdim * Redistribution and use in source and binary forms, with or without 9320957Sdim * modification, are permitted provided that the following conditions 10320957Sdim * are met: 11320957Sdim * 1. Redistributions of source code must retain the above copyright 12320957Sdim * notice, this list of conditions and the following disclaimer. 13320957Sdim * 2. Redistributions in binary form must reproduce the above copyright 14320957Sdim * notice, this list of conditions and the following disclaimer in the 15320957Sdim * documentation and/or other materials provided with the distribution. 16320957Sdim * 4. Neither the name of the University nor the names of its contributors 17320957Sdim * may be used to endorse or promote products derived from this software 18327952Sdim * without specific prior written permission. 19327952Sdim * 20327952Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21341825Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22327952Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23327952Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24327952Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25327952Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26327952Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27327952Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28327952Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29360784Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30327952Sdim * SUCH DAMAGE. 31327952Sdim */ 32327952Sdim 33327952Sdim#if defined(LIBC_SCCS) && !defined(lint) 34327952Sdimstatic char sccsid[] = "@(#)atexit.c 8.2 (Berkeley) 7/3/94"; 35327952Sdim#endif /* LIBC_SCCS and not lint */ 36327952Sdim#include <sys/cdefs.h> 37320957Sdim__FBSDID("$FreeBSD$"); 38327952Sdim 39327952Sdim#include "namespace.h" 40327952Sdim#include <link.h> 41327952Sdim#include <stddef.h> 42327952Sdim#include <stdlib.h> 43327952Sdim#include <unistd.h> 44341825Sdim#include <pthread.h> 45327952Sdim#include "atexit.h" 46327952Sdim#include "un-namespace.h" 47320957Sdim 48341825Sdim#include "libc_private.h" 49320957Sdim 50320957Sdim#define ATEXIT_FN_EMPTY 0 51341825Sdim#define ATEXIT_FN_STD 1 52341825Sdim#define ATEXIT_FN_CXA 2 53320957Sdim 54320957Sdimstatic pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER; 55327952Sdim 56327952Sdim#define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) 57341825Sdim#define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) 58327952Sdim#define _MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x) 59341825Sdim 60327952Sdimstruct atexit { 61327952Sdim struct atexit *next; /* next in list */ 62327952Sdim int ind; /* next index in this table */ 63327952Sdim struct atexit_fn { 64327952Sdim int fn_type; /* ATEXIT_? from above */ 65327952Sdim union { 66327952Sdim void (*std_func)(void); 67341825Sdim void (*cxa_func)(void *); 68327952Sdim } fn_ptr; /* function pointer */ 69327952Sdim void *fn_arg; /* argument for CXA callback */ 70327952Sdim void *fn_dso; /* shared module handle */ 71327952Sdim } fns[ATEXIT_SIZE]; /* the table itself */ 72327952Sdim}; 73327952Sdim 74320957Sdimstatic struct atexit *__atexit; /* points to head of LIFO stack */ 75327952Sdim 76341825Sdim/* 77341825Sdim * Register the function described by 'fptr' to be called at application 78327952Sdim * exit or owning shared object unload time. This is a helper function 79327952Sdim * for atexit and __cxa_atexit. 80327952Sdim */ 81327952Sdimstatic int 82327952Sdimatexit_register(struct atexit_fn *fptr) 83327952Sdim{ 84327952Sdim static struct atexit __atexit0; /* one guaranteed table */ 85320957Sdim struct atexit *p; 86327952Sdim 87327952Sdim _MUTEX_LOCK(&atexit_mutex); 88327952Sdim if ((p = __atexit) == NULL) 89320957Sdim __atexit = p = &__atexit0; 90320957Sdim else while (p->ind >= ATEXIT_SIZE) { 91320957Sdim struct atexit *old__atexit; 92320957Sdim old__atexit = __atexit; 93320957Sdim _MUTEX_UNLOCK(&atexit_mutex); 94320957Sdim if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL) 95320957Sdim return (-1); 96327952Sdim _MUTEX_LOCK(&atexit_mutex); 97327952Sdim if (old__atexit != __atexit) { 98327952Sdim /* Lost race, retry operation */ 99327952Sdim _MUTEX_UNLOCK(&atexit_mutex); 100327952Sdim free(p); 101320957Sdim _MUTEX_LOCK(&atexit_mutex); 102360784Sdim p = __atexit; 103327952Sdim continue; 104327952Sdim } 105327952Sdim p->ind = 0; 106327952Sdim p->next = __atexit; 107327952Sdim __atexit = p; 108320957Sdim } 109320957Sdim p->fns[p->ind++] = *fptr; 110327952Sdim _MUTEX_UNLOCK(&atexit_mutex); 111327952Sdim return 0; 112327952Sdim} 113327952Sdim 114327952Sdim/* 115327952Sdim * Register a function to be performed at exit. 116327952Sdim */ 117327952Sdimint 118327952Sdimatexit(void (*func)(void)) 119327952Sdim{ 120327952Sdim struct atexit_fn fn; 121327952Sdim int error; 122320957Sdim 123320957Sdim fn.fn_type = ATEXIT_FN_STD; 124320957Sdim fn.fn_ptr.std_func = func; 125320957Sdim fn.fn_arg = NULL; 126327952Sdim fn.fn_dso = NULL; 127327952Sdim 128327952Sdim error = atexit_register(&fn); 129327952Sdim return (error); 130327952Sdim} 131341825Sdim 132341825Sdim/* 133327952Sdim * Register a function to be performed at exit or when an shared object 134327952Sdim * with given dso handle is unloaded dynamically. 135320957Sdim */ 136320957Sdimint 137320957Sdim__cxa_atexit(void (*func)(void *), void *arg, void *dso) 138320957Sdim{ 139320957Sdim struct atexit_fn fn; 140327952Sdim int error; 141327952Sdim 142320957Sdim fn.fn_type = ATEXIT_FN_CXA; 143341825Sdim fn.fn_ptr.cxa_func = func; 144320957Sdim fn.fn_arg = arg; 145341825Sdim fn.fn_dso = dso; 146327952Sdim 147327952Sdim error = atexit_register(&fn); 148327952Sdim return (error); 149327952Sdim} 150327952Sdim 151327952Sdim#pragma weak __pthread_cxa_finalize 152327952Sdimvoid __pthread_cxa_finalize(const struct dl_phdr_info *); 153327952Sdim 154320957Sdimstatic int global_exit; 155320957Sdim 156327952Sdim/* 157341825Sdim * Call all handlers registered with __cxa_atexit for the shared 158341825Sdim * object owning 'dso'. Note: if 'dso' is NULL, then all remaining 159341825Sdim * handlers are called. 160341825Sdim */ 161341825Sdimvoid 162341825Sdim__cxa_finalize(void *dso) 163341825Sdim{ 164341825Sdim struct dl_phdr_info phdr_info; 165341825Sdim struct atexit *p; 166341825Sdim struct atexit_fn fn; 167341825Sdim int n, has_phdr; 168341825Sdim 169341825Sdim if (dso != NULL) { 170341825Sdim has_phdr = _rtld_addr_phdr(dso, &phdr_info); 171341825Sdim } else { 172341825Sdim has_phdr = 0; 173341825Sdim global_exit = 1; 174341825Sdim } 175341825Sdim 176341825Sdim _MUTEX_LOCK(&atexit_mutex); 177341825Sdim for (p = __atexit; p; p = p->next) { 178341825Sdim for (n = p->ind; --n >= 0;) { 179341825Sdim if (p->fns[n].fn_type == ATEXIT_FN_EMPTY) 180341825Sdim continue; /* already been called */ 181341825Sdim fn = p->fns[n]; 182341825Sdim if (dso != NULL && dso != fn.fn_dso) { 183341825Sdim /* wrong DSO ? */ 184341825Sdim if (!has_phdr || global_exit || 185341825Sdim !__elf_phdr_match_addr(&phdr_info, 186341825Sdim fn.fn_ptr.cxa_func)) 187341825Sdim continue; 188341825Sdim } 189341825Sdim /* 190341825Sdim Mark entry to indicate that this particular handler 191341825Sdim has already been called. 192341825Sdim */ 193341825Sdim p->fns[n].fn_type = ATEXIT_FN_EMPTY; 194341825Sdim _MUTEX_UNLOCK(&atexit_mutex); 195341825Sdim 196341825Sdim /* Call the function of correct type. */ 197341825Sdim if (fn.fn_type == ATEXIT_FN_CXA) 198341825Sdim fn.fn_ptr.cxa_func(fn.fn_arg); 199341825Sdim else if (fn.fn_type == ATEXIT_FN_STD) 200341825Sdim fn.fn_ptr.std_func(); 201341825Sdim _MUTEX_LOCK(&atexit_mutex); 202341825Sdim } 203341825Sdim } 204341825Sdim _MUTEX_UNLOCK(&atexit_mutex); 205341825Sdim if (dso == NULL) 206341825Sdim _MUTEX_DESTROY(&atexit_mutex); 207341825Sdim 208341825Sdim if (has_phdr && !global_exit && &__pthread_cxa_finalize != NULL) 209341825Sdim __pthread_cxa_finalize(&phdr_info); 210341825Sdim} 211341825Sdim