#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-function-declaration"
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#pragma clang diagnostic ignored "-Winvalid-pp-token"
#pragma clang diagnostic ignored "-Wint-conversion"
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)atexit.c 8.2 (Berkeley) 7/3/94";
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/stdlib/atexit.c,v 1.8 2007/01/09 00:28:09 imp Exp $");
#include <TargetConditionals.h>
#include "namespace.h"
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#if (defined(__DYNAMIC__) || defined (__BLOCKS__)) && !TARGET_OS_DRIVERKIT
#include <dlfcn.h>
#endif
#include "atexit.h"
#include "un-namespace.h"
#ifdef __BLOCKS__
#include <Block.h>
#include <Block_private.h>
#endif
#include "libc_private.h"
#include <os/alloc_once_private.h>
#define ATEXIT_FN_EMPTY 0
#define ATEXIT_FN_STD 1
#define ATEXIT_FN_CXA 2
#ifdef __BLOCKS__
#define ATEXIT_FN_BLK 3
#endif
static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
#define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)
#define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)
struct atexit {
struct atexit *next;
int ind;
struct atexit_fn {
int fn_type;
union {
void (*std_func)(void);
void (*cxa_func)(void *);
#ifdef __BLOCKS__
void (^block)(void);
#endif
} fn_ptr;
void *fn_arg;
void *fn_dso;
} fns[ATEXIT_SIZE];
};
static struct atexit *__atexit;
static int __atexit_new_registration;
__attribute__ ((visibility ("hidden")))
void
__atexit_init(void)
{
__atexit = os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_C,
sizeof(struct atexit), NULL);
}
static int
atexit_register(struct atexit_fn *fptr)
{
struct atexit *p = __atexit;
assert(p);
_MUTEX_LOCK(&atexit_mutex);
while (p->ind >= ATEXIT_SIZE) {
struct atexit *old__atexit;
old__atexit = __atexit;
_MUTEX_UNLOCK(&atexit_mutex);
if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL)
return (-1);
_MUTEX_LOCK(&atexit_mutex);
if (old__atexit != __atexit) {
_MUTEX_UNLOCK(&atexit_mutex);
free(p);
_MUTEX_LOCK(&atexit_mutex);
p = __atexit;
continue;
}
p->ind = 0;
p->next = __atexit;
__atexit = p;
}
p->fns[p->ind++] = *fptr;
__atexit_new_registration = 1;
_MUTEX_UNLOCK(&atexit_mutex);
return 0;
}
int
atexit(void (*func)(void))
{
struct atexit_fn fn;
int error;
fn.fn_type = ATEXIT_FN_STD;
fn.fn_ptr.std_func = func;
fn.fn_arg = NULL;
fn.fn_dso = NULL;
#if defined(__DYNAMIC__) && !TARGET_OS_IPHONE && !TARGET_OS_DRIVERKIT
struct dl_info info;
if (dladdr(func, &info)) {
fn.fn_dso = info.dli_fbase;
}
#endif
error = atexit_register(&fn);
return (error);
}
#ifdef __BLOCKS__
int
atexit_b(void (^block)(void))
{
struct atexit_fn fn;
int error;
fn.fn_type = ATEXIT_FN_BLK;
fn.fn_ptr.block = Block_copy(block);
fn.fn_arg = NULL;
fn.fn_dso = NULL;
error = atexit_register(&fn);
return (error);
}
#endif
int
__cxa_atexit(void (*func)(void *), void *arg, void *dso)
{
struct atexit_fn fn;
int error;
fn.fn_type = ATEXIT_FN_CXA;
fn.fn_ptr.cxa_func = func;;
fn.fn_arg = arg;
fn.fn_dso = dso;
error = atexit_register(&fn);
return (error);
}
static bool
__cxa_in_range(const struct __cxa_range_t ranges[],
unsigned int count,
const void* fn)
{
uintptr_t addr = (uintptr_t)fn;
unsigned int i;
for (i = 0; i < count; ++i) {
const struct __cxa_range_t *r = &ranges[i];
if (addr < (uintptr_t)r->addr) {
continue;
}
if (addr < ((uintptr_t)r->addr + r->length)) {
return true;
}
}
return false;
}
void
__cxa_finalize_ranges(const struct __cxa_range_t ranges[], unsigned int count)
{
struct atexit *p;
struct atexit_fn *fn;
int n;
_MUTEX_LOCK(&atexit_mutex);
restart:
for (p = __atexit; p; p = p->next) {
for (n = p->ind; --n >= 0;) {
fn = &p->fns[n];
if (fn->fn_type == ATEXIT_FN_EMPTY) {
continue; }
if (count > 0) {
if (fn->fn_type == ATEXIT_FN_CXA) {
if (!__cxa_in_range(ranges, count, fn->fn_dso)) {
continue; }
} else if (fn->fn_type == ATEXIT_FN_STD) {
if (!__cxa_in_range(ranges, count, fn->fn_ptr.std_func)) {
continue; }
#ifdef __BLOCKS__
} else if (fn->fn_type == ATEXIT_FN_BLK) {
void *a = ((struct Block_layout *)fn->fn_ptr.block)->invoke;
if (!__cxa_in_range(ranges, count, a)) {
continue; }
#endif // __BLOCKS__
}
}
int fn_type = fn->fn_type;
fn->fn_type = ATEXIT_FN_EMPTY;
__atexit_new_registration = 0;
_MUTEX_UNLOCK(&atexit_mutex);
if (fn_type == ATEXIT_FN_CXA) {
fn->fn_ptr.cxa_func(fn->fn_arg);
} else if (fn_type == ATEXIT_FN_STD) {
fn->fn_ptr.std_func();
#ifdef __BLOCKS__
} else if (fn_type == ATEXIT_FN_BLK) {
fn->fn_ptr.block();
#endif // __BLOCKS__
}
_MUTEX_LOCK(&atexit_mutex);
if (__atexit_new_registration) {
goto restart;
}
}
}
_MUTEX_UNLOCK(&atexit_mutex);
}
void
__cxa_finalize(const void *dso)
{
if (dso != NULL) {
struct __cxa_range_t range;
range.addr = dso;
range.length = 1;
__cxa_finalize_ranges(&range, 1);
} else {
__cxa_finalize_ranges(NULL, 0);
}
}
#if !TARGET_IPHONE_SIMULATOR && (__i386__ || __x86_64__)
void _tlv_atexit(void(*f)(void*), void* arg);
void
__cxa_thread_atexit(void(*f)(void*), void* arg)
{
_tlv_atexit(f, arg);
}
#endif
#pragma clang diagnostic pop