/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1990, 19934* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Chris Torek.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include "namespace.h"35#include <errno.h>36#include <link.h>37#include <stdbool.h>38#include <stddef.h>39#include <stdlib.h>40#include <unistd.h>41#include <pthread.h>42#include "atexit.h"43#include "un-namespace.h"44#include "block_abi.h"4546#include "libc_private.h"4748/**49* The _Block_copy() function is provided by the block runtime.50*/51__attribute__((weak)) void*52_Block_copy(void*);5354#define ATEXIT_FN_EMPTY 055#define ATEXIT_FN_STD 156#define ATEXIT_FN_CXA 25758static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER;59static void *current_finalize_dso = NULL;60static bool call_finalize_again = false;6162#define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)63#define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)64#define _MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x)6566struct atexit {67struct atexit *next; /* next in list */68int ind; /* next index in this table */69struct atexit_fn {70int fn_type; /* ATEXIT_? from above */71union {72void (*std_func)(void);73void (*cxa_func)(void *);74} fn_ptr; /* function pointer */75void *fn_arg; /* argument for CXA callback */76void *fn_dso; /* shared module handle */77} fns[ATEXIT_SIZE]; /* the table itself */78};7980static struct atexit *__atexit; /* points to head of LIFO stack */81typedef DECLARE_BLOCK(void, atexit_block, void);8283int atexit_b(atexit_block);84int __cxa_atexit(void (*)(void *), void *, void *);8586/*87* Register the function described by 'fptr' to be called at application88* exit or owning shared object unload time. This is a helper function89* for atexit and __cxa_atexit.90*/91static int92atexit_register(struct atexit_fn *fptr)93{94static struct atexit __atexit0; /* one guaranteed table */95struct atexit *p;9697_MUTEX_LOCK(&atexit_mutex);98if ((p = __atexit) == NULL)99__atexit = p = &__atexit0;100else while (p->ind >= ATEXIT_SIZE) {101struct atexit *old__atexit;102old__atexit = __atexit;103_MUTEX_UNLOCK(&atexit_mutex);104if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL)105return (-1);106_MUTEX_LOCK(&atexit_mutex);107if (old__atexit != __atexit) {108/* Lost race, retry operation */109_MUTEX_UNLOCK(&atexit_mutex);110free(p);111_MUTEX_LOCK(&atexit_mutex);112p = __atexit;113continue;114}115p->ind = 0;116p->next = __atexit;117__atexit = p;118}119p->fns[p->ind++] = *fptr;120if (current_finalize_dso != NULL &&121current_finalize_dso == fptr->fn_dso)122call_finalize_again = true;123_MUTEX_UNLOCK(&atexit_mutex);124return 0;125}126127/*128* Register a function to be performed at exit.129*/130int131atexit(void (*func)(void))132{133struct atexit_fn fn;134int error;135136fn.fn_type = ATEXIT_FN_STD;137fn.fn_ptr.std_func = func;138fn.fn_arg = NULL;139fn.fn_dso = NULL;140141error = atexit_register(&fn);142return (error);143}144__weak_reference(atexit, __libc_atexit);145146/**147* Register a block to be performed at exit.148*/149int150atexit_b(atexit_block func)151{152struct atexit_fn fn;153int error;154if (_Block_copy == 0) {155errno = ENOSYS;156return -1;157}158func = _Block_copy(func);159160// Blocks are not C++ destructors, but they have the same signature (a161// single void* parameter), so we can pretend that they are.162fn.fn_type = ATEXIT_FN_CXA;163fn.fn_ptr.cxa_func = (void(*)(void*))GET_BLOCK_FUNCTION(func);164fn.fn_arg = func;165fn.fn_dso = NULL;166167error = atexit_register(&fn);168return (error);169}170171/*172* Register a function to be performed at exit or when an shared object173* with given dso handle is unloaded dynamically.174*/175int176__cxa_atexit(void (*func)(void *), void *arg, void *dso)177{178struct atexit_fn fn;179int error;180181fn.fn_type = ATEXIT_FN_CXA;182fn.fn_ptr.cxa_func = func;183fn.fn_arg = arg;184fn.fn_dso = dso;185186error = atexit_register(&fn);187return (error);188}189190#pragma weak __pthread_cxa_finalize191void __pthread_cxa_finalize(const struct dl_phdr_info *);192193static int global_exit;194195/*196* Call all handlers registered with __cxa_atexit for the shared197* object owning 'dso'. Note: if 'dso' is NULL, then all remaining198* handlers are called.199*/200void201__cxa_finalize(void *dso)202{203struct dl_phdr_info phdr_info;204struct atexit *p;205struct atexit_fn fn;206int n, has_phdr;207208if (dso != NULL) {209has_phdr = _rtld_addr_phdr(dso, &phdr_info);210} else {211has_phdr = 0;212global_exit = 1;213}214215_MUTEX_LOCK(&atexit_mutex);216current_finalize_dso = dso;217do {218call_finalize_again = false;219for (p = __atexit; p; p = p->next) {220for (n = p->ind; --n >= 0;) {221if (p->fns[n].fn_type == ATEXIT_FN_EMPTY)222continue; /* already been called */223fn = p->fns[n];224if (dso != NULL && dso != fn.fn_dso) {225/* wrong DSO ? */226if (!has_phdr || global_exit ||227!__elf_phdr_match_addr(&phdr_info,228fn.fn_ptr.cxa_func))229continue;230}231/*232Mark entry to indicate that this particular233handler has already been called.234*/235p->fns[n].fn_type = ATEXIT_FN_EMPTY;236_MUTEX_UNLOCK(&atexit_mutex);237238/* Call the function of correct type. */239if (fn.fn_type == ATEXIT_FN_CXA)240fn.fn_ptr.cxa_func(fn.fn_arg);241else if (fn.fn_type == ATEXIT_FN_STD)242fn.fn_ptr.std_func();243_MUTEX_LOCK(&atexit_mutex);244}245}246} while (call_finalize_again);247current_finalize_dso = NULL;248_MUTEX_UNLOCK(&atexit_mutex);249if (dso == NULL)250_MUTEX_DESTROY(&atexit_mutex);251252if (has_phdr && !global_exit && &__pthread_cxa_finalize != NULL)253__pthread_cxa_finalize(&phdr_info);254}255256257