#ifdef __PIC__
#include "tconfig.h"
#include "tsystem.h"
#include <dlfcn.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef int (*cxa_atexit_p)(void (*func) (void*), void* arg, const void* dso);
typedef void (*cxa_finalize_p)(const void *dso);
typedef int (*atexit_p)(void (*func)(void));
extern void *_keymgr_get_and_lock_processwide_ptr (unsigned key);
extern int _keymgr_get_and_lock_processwide_ptr_2 (unsigned, void **);
extern int _keymgr_set_and_unlock_processwide_ptr (unsigned key, void *ptr);
extern void *__keymgr_global[];
typedef struct _Sinfo_Node {
unsigned int size ;
unsigned short major_version ;
unsigned short minor_version ;
} _Tinfo_Node ;
#ifdef __ppc__
#define CHECK_KEYMGR_ERROR(e) \
(((_Tinfo_Node *)__keymgr_global[2])->major_version >= 4 ? (e) : 0)
#else
#define CHECK_KEYMGR_ERROR(e) (e)
#endif
#define KEYMGR_ATEXIT_LIST 14
typedef void (*atexit_callback)(void);
typedef void (*cxa_atexit_callback)(void *);
struct one_atexit_routine
{
union {
atexit_callback ac;
cxa_atexit_callback cac;
} callback;
int has_arg;
void * arg;
};
struct atexit_routine_list
{
struct atexit_routine_list * next;
struct one_atexit_routine r;
};
enum atexit_status {
atexit_status_unknown = 0,
atexit_status_missing = 1,
atexit_status_broken = 2,
atexit_status_working = 16
};
struct keymgr_atexit_list
{
short version;
char running_routines;
unsigned char atexit_status;
struct atexit_routine_list *l;
cxa_atexit_p cxa_atexit_f;
cxa_finalize_p cxa_finalize_f;
atexit_p atexit_f;
};
struct atexit_data
{
int result;
cxa_atexit_p cxa_atexit;
};
static void cxa_atexit_check_2 (void *arg)
{
((struct atexit_data *)arg)->result = 1;
}
static void cxa_atexit_check_1 (void *arg)
{
struct atexit_data * aed = arg;
if (aed->cxa_atexit (cxa_atexit_check_2, arg, arg) != 0)
aed->result = -1;
}
static int
check_cxa_atexit (cxa_atexit_p cxa_atexit, cxa_finalize_p cxa_finalize)
{
struct atexit_data aed = { 0, cxa_atexit };
if (cxa_atexit (cxa_atexit_check_1, &aed, &aed) != 0)
return -1;
cxa_finalize (&aed);
if (aed.result == 0)
{
cxa_finalize (&aed);
aed.result = 0;
}
return aed.result;
}
#ifdef __ppc__
extern int _dyld_func_lookup(const char *dyld_func_name,
void *address) __attribute__((visibility("hidden")));
static void our_atexit (void);
static atexit_p
find_atexit_10_3 (void)
{
unsigned int (*dyld_image_count_fn)(void);
const char *(*dyld_get_image_name_fn)(unsigned int image_index);
const void *(*dyld_get_image_header_fn)(unsigned int image_index);
const void *(*NSLookupSymbolInImage_fn)(const void *image,
const char *symbolName,
unsigned int options);
void *(*NSAddressOfSymbol_fn)(const void *symbol);
unsigned i, count;
_dyld_func_lookup("__dyld_image_count", &dyld_image_count_fn);
_dyld_func_lookup("__dyld_get_image_name", &dyld_get_image_name_fn);
_dyld_func_lookup("__dyld_get_image_header", &dyld_get_image_header_fn);
_dyld_func_lookup("__dyld_NSLookupSymbolInImage", &NSLookupSymbolInImage_fn);
_dyld_func_lookup("__dyld_NSAddressOfSymbol", &NSAddressOfSymbol_fn);
if (! dyld_image_count_fn || ! dyld_get_image_name_fn
|| ! dyld_get_image_header_fn || ! NSLookupSymbolInImage_fn
|| ! NSAddressOfSymbol_fn)
return NULL;
count = dyld_image_count_fn ();
for (i = 0; i < count; i++)
{
const char * path = dyld_get_image_name_fn (i);
const void * image;
const void * symbol;
if (strcmp (path, "/usr/lib/libSystem.B.dylib") != 0)
continue;
image = dyld_get_image_header_fn (i);
if (! image)
return NULL;
symbol = NSLookupSymbolInImage_fn (image, "_atexit", 4);
if (! symbol)
return NULL;
return NSAddressOfSymbol_fn (symbol);
}
return NULL;
}
#endif
static struct keymgr_atexit_list *
get_globals (void)
{
struct keymgr_atexit_list * r;
#ifdef __ppc__
r = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
#else
void * rr;
if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ATEXIT_LIST, &rr))
return NULL;
r = rr;
#endif
if (r == NULL)
{
r = calloc (sizeof (struct keymgr_atexit_list), 1);
if (! r)
return NULL;
}
if (r->atexit_status == atexit_status_unknown)
{
void *handle;
handle = dlopen ("/usr/lib/libSystem.B.dylib", RTLD_NOLOAD);
if (!handle)
{
#ifdef __ppc__
r->atexit_status = atexit_status_missing;
r->atexit_f = find_atexit_10_3 ();
if (! r->atexit_f)
goto error;
if (r->atexit_f (our_atexit))
goto error;
#else
goto error;
#endif
}
else
{
int chk_result;
r->cxa_atexit_f = (cxa_atexit_p)dlsym (handle, "__cxa_atexit");
r->cxa_finalize_f = (cxa_finalize_p)dlsym (handle, "__cxa_finalize");
if (! r->cxa_atexit_f || ! r->cxa_finalize_f)
goto error;
chk_result = check_cxa_atexit (r->cxa_atexit_f, r->cxa_finalize_f);
if (chk_result == -1)
goto error;
else if (chk_result == 0)
r->atexit_status = atexit_status_broken;
else
{
r->atexit_f = (atexit_p)dlsym (handle, "atexit");
if (! r->atexit_f)
goto error;
r->atexit_status = atexit_status_working;
}
}
}
return r;
error:
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, r);
return NULL;
}
static int
add_routine (struct keymgr_atexit_list * g,
const struct one_atexit_routine * to_add)
{
struct atexit_routine_list * s
= malloc (sizeof (struct atexit_routine_list));
int result;
if (!s)
{
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
return -1;
}
s->r = *to_add;
s->next = g->l;
g->l = s;
result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
return CHECK_KEYMGR_ERROR (result) == 0 ? 0 : -1;
}
static struct keymgr_atexit_list *
run_routines (struct keymgr_atexit_list *g,
struct atexit_routine_list *stop)
{
for (;;)
{
struct atexit_routine_list * cur = g->l;
if (! cur || cur == stop)
break;
g->l = cur->next;
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
switch (cur->r.has_arg) {
case 0: case 2: case 4:
cur->r.callback.ac ();
break;
case 1: case 3: case 5:
cur->r.callback.cac (cur->r.arg);
break;
default:
break;
}
free (cur);
g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
if (! g)
break;
}
return g;
}
static void
cxa_atexit_wrapper (void* routine_param)
{
struct one_atexit_routine * routine = routine_param;
struct keymgr_atexit_list *g;
struct atexit_routine_list * base = NULL;
char prev_running = 0;
g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
if (g)
{
prev_running = g->running_routines;
g->running_routines = 1;
base = g->l;
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
}
if (routine->has_arg)
routine->callback.cac (routine->arg);
else
routine->callback.ac ();
if (g)
g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
if (g)
g = run_routines (g, base);
if (g)
{
g->running_routines = prev_running;
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
}
}
#ifdef __ppc__
static void
our_atexit (void)
{
struct keymgr_atexit_list *g;
char prev_running;
g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
if (! g || g->version != 0 || g->atexit_status != atexit_status_missing)
return;
prev_running = g->running_routines;
g->running_routines = 1;
g = run_routines (g, NULL);
if (! g)
return;
g->running_routines = prev_running;
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
}
#endif
static int
atexit_common (const struct one_atexit_routine *r, const void *dso)
{
struct keymgr_atexit_list *g = get_globals ();
if (! g)
return -1;
if (g->running_routines || g->atexit_status == atexit_status_missing)
return add_routine (g, r);
if (g->atexit_status >= atexit_status_working)
{
int result;
if (r->has_arg)
{
cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
g);
if (CHECK_KEYMGR_ERROR (result))
return -1;
return cxa_atexit (r->callback.cac, r->arg, dso);
}
else
{
atexit_p atexit_f = g->atexit_f;
result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
g);
if (CHECK_KEYMGR_ERROR (result))
return -1;
return atexit_f (r->callback.ac);
}
}
else
{
cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
struct one_atexit_routine *alloced;
int result;
result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
if (CHECK_KEYMGR_ERROR (result))
return -1;
alloced = malloc (sizeof (struct one_atexit_routine));
if (! alloced)
return -1;
*alloced = *r;
return cxa_atexit (cxa_atexit_wrapper, alloced, dso);
}
}
int __cxa_atexit (cxa_atexit_callback func, void* arg,
const void* dso) __attribute__((visibility("hidden")));
int
__cxa_atexit (cxa_atexit_callback func, void* arg, const void* dso)
{
struct one_atexit_routine r;
r.callback.cac = func;
r.has_arg = 1;
r.arg = arg;
return atexit_common (&r, dso);
}
int atexit (atexit_callback func) __attribute__((visibility("hidden")));
extern void __dso_handle;
int
atexit (atexit_callback func)
{
struct one_atexit_routine r;
r.callback.ac = func;
r.has_arg = 0;
return atexit_common (&r, &__dso_handle);
}
#endif