11debfc3dSmrg /* __cxa_atexit backwards-compatibility support for Darwin.
2*8feb0f0bSmrg Copyright (C) 2006-2020 Free Software Foundation, Inc.
31debfc3dSmrg
41debfc3dSmrg This file is part of GCC.
51debfc3dSmrg
61debfc3dSmrg GCC is free software; you can redistribute it and/or modify it under
71debfc3dSmrg the terms of the GNU General Public License as published by the Free
81debfc3dSmrg Software Foundation; either version 3, or (at your option) any later
91debfc3dSmrg version.
101debfc3dSmrg
111debfc3dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
121debfc3dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
131debfc3dSmrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
141debfc3dSmrg for more details.
151debfc3dSmrg
161debfc3dSmrg Under Section 7 of GPL version 3, you are granted additional
171debfc3dSmrg permissions described in the GCC Runtime Library Exception, version
181debfc3dSmrg 3.1, as published by the Free Software Foundation.
191debfc3dSmrg
201debfc3dSmrg You should have received a copy of the GNU General Public License and
211debfc3dSmrg a copy of the GCC Runtime Library Exception along with this program;
221debfc3dSmrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
231debfc3dSmrg <http://www.gnu.org/licenses/>. */
241debfc3dSmrg
251debfc3dSmrg /* Don't do anything if we are compiling for a kext multilib. */
261debfc3dSmrg #ifdef __PIC__
271debfc3dSmrg
281debfc3dSmrg #include "tconfig.h"
291debfc3dSmrg #include "tsystem.h"
301debfc3dSmrg
311debfc3dSmrg #include <dlfcn.h>
321debfc3dSmrg #include <stdbool.h>
331debfc3dSmrg #include <stdlib.h>
341debfc3dSmrg #include <string.h>
351debfc3dSmrg
361debfc3dSmrg /* This file works around two different problems.
371debfc3dSmrg
381debfc3dSmrg The first problem is that there is no __cxa_atexit on Mac OS versions
391debfc3dSmrg before 10.4. It fixes this by providing a complete atexit and
401debfc3dSmrg __cxa_atexit emulation called from the regular atexit.
411debfc3dSmrg
421debfc3dSmrg The second problem is that on all shipping versions of Mac OS,
431debfc3dSmrg __cxa_finalize and exit() don't work right: they don't run routines
441debfc3dSmrg that were registered while other atexit routines are running. This
451debfc3dSmrg is worked around by wrapping each atexit/__cxa_atexit routine with
461debfc3dSmrg our own routine which ensures that any __cxa_atexit calls while it
471debfc3dSmrg is running are honoured.
481debfc3dSmrg
491debfc3dSmrg There are still problems which this does not solve. Before 10.4,
501debfc3dSmrg shared objects linked with previous compilers won't have their
511debfc3dSmrg atexit calls properly interleaved with code compiled with newer
521debfc3dSmrg compilers. Also, atexit routines registered from shared objects
531debfc3dSmrg linked with previous compilers won't get the bug fix. */
541debfc3dSmrg
551debfc3dSmrg typedef int (*cxa_atexit_p)(void (*func) (void*), void* arg, const void* dso);
561debfc3dSmrg typedef void (*cxa_finalize_p)(const void *dso);
571debfc3dSmrg typedef int (*atexit_p)(void (*func)(void));
581debfc3dSmrg
591debfc3dSmrg /* These are from "keymgr.h". */
601debfc3dSmrg extern void *_keymgr_get_and_lock_processwide_ptr (unsigned key);
611debfc3dSmrg extern int _keymgr_get_and_lock_processwide_ptr_2 (unsigned, void **);
621debfc3dSmrg extern int _keymgr_set_and_unlock_processwide_ptr (unsigned key, void *ptr);
631debfc3dSmrg
641debfc3dSmrg extern void *__keymgr_global[];
651debfc3dSmrg typedef struct _Sinfo_Node {
661debfc3dSmrg unsigned int size ; /*size of this node*/
671debfc3dSmrg unsigned short major_version ; /*API major version.*/
681debfc3dSmrg unsigned short minor_version ; /*API minor version.*/
691debfc3dSmrg } _Tinfo_Node ;
701debfc3dSmrg
711debfc3dSmrg #ifdef __ppc__
721debfc3dSmrg #define CHECK_KEYMGR_ERROR(e) \
731debfc3dSmrg (((_Tinfo_Node *)__keymgr_global[2])->major_version >= 4 ? (e) : 0)
741debfc3dSmrg #else
751debfc3dSmrg #define CHECK_KEYMGR_ERROR(e) (e)
761debfc3dSmrg #endif
771debfc3dSmrg
781debfc3dSmrg /* Our globals are stored under this keymgr index. */
791debfc3dSmrg #define KEYMGR_ATEXIT_LIST 14
801debfc3dSmrg
811debfc3dSmrg /* The different kinds of callback routines. */
821debfc3dSmrg typedef void (*atexit_callback)(void);
831debfc3dSmrg typedef void (*cxa_atexit_callback)(void *);
841debfc3dSmrg
851debfc3dSmrg /* This structure holds a routine to call. There may be extra fields
861debfc3dSmrg at the end of the structure that this code doesn't know about. */
871debfc3dSmrg struct one_atexit_routine
881debfc3dSmrg {
891debfc3dSmrg union {
901debfc3dSmrg atexit_callback ac;
911debfc3dSmrg cxa_atexit_callback cac;
921debfc3dSmrg } callback;
931debfc3dSmrg /* has_arg is 0/2/4 if 'ac' is live, 1/3/5 if 'cac' is live.
941debfc3dSmrg Higher numbers indicate a later version of the structure that this
951debfc3dSmrg code doesn't understand and will ignore. */
961debfc3dSmrg int has_arg;
971debfc3dSmrg void * arg;
981debfc3dSmrg };
991debfc3dSmrg
1001debfc3dSmrg struct atexit_routine_list
1011debfc3dSmrg {
1021debfc3dSmrg struct atexit_routine_list * next;
1031debfc3dSmrg struct one_atexit_routine r;
1041debfc3dSmrg };
1051debfc3dSmrg
1061debfc3dSmrg /* The various possibilities for status of atexit(). */
1071debfc3dSmrg enum atexit_status {
1081debfc3dSmrg atexit_status_unknown = 0,
1091debfc3dSmrg atexit_status_missing = 1,
1101debfc3dSmrg atexit_status_broken = 2,
1111debfc3dSmrg atexit_status_working = 16
1121debfc3dSmrg };
1131debfc3dSmrg
1141debfc3dSmrg struct keymgr_atexit_list
1151debfc3dSmrg {
1161debfc3dSmrg /* Version of this list. This code knows only about version 0.
1171debfc3dSmrg If the version is higher than 0, this code may add new atexit routines
1181debfc3dSmrg but should not attempt to run the list. */
1191debfc3dSmrg short version;
1201debfc3dSmrg /* 1 if an atexit routine is currently being run by this code, 0
1211debfc3dSmrg otherwise. */
1221debfc3dSmrg char running_routines;
1231debfc3dSmrg /* Holds a value from 'enum atexit_status'. */
1241debfc3dSmrg unsigned char atexit_status;
1251debfc3dSmrg /* The list of atexit and cxa_atexit routines registered. If
1261debfc3dSmrg atexit_status_missing it contains all routines registered while
1271debfc3dSmrg linked with this code. If atexit_status_broken it contains all
1281debfc3dSmrg routines registered during cxa_finalize while linked with this
1291debfc3dSmrg code. */
1301debfc3dSmrg struct atexit_routine_list *l;
1311debfc3dSmrg /* &__cxa_atexit; set if atexit_status >= atexit_status_broken. */
1321debfc3dSmrg cxa_atexit_p cxa_atexit_f;
1331debfc3dSmrg /* &__cxa_finalize; set if atexit_status >= atexit_status_broken. */
1341debfc3dSmrg cxa_finalize_p cxa_finalize_f;
1351debfc3dSmrg /* &atexit; set if atexit_status >= atexit_status_working
1361debfc3dSmrg or atexit_status == atexit_status_missing. */
1371debfc3dSmrg atexit_p atexit_f;
1381debfc3dSmrg };
1391debfc3dSmrg
1401debfc3dSmrg /* Return 0 if __cxa_atexit has the bug it has in Mac OS 10.4: it
1411debfc3dSmrg fails to call routines registered while an atexit routine is
1421debfc3dSmrg running. Return 1 if it works properly, and -1 if an error occurred. */
1431debfc3dSmrg
1441debfc3dSmrg struct atexit_data
1451debfc3dSmrg {
1461debfc3dSmrg int result;
1471debfc3dSmrg cxa_atexit_p cxa_atexit;
1481debfc3dSmrg };
1491debfc3dSmrg
cxa_atexit_check_2(void * arg)1501debfc3dSmrg static void cxa_atexit_check_2 (void *arg)
1511debfc3dSmrg {
1521debfc3dSmrg ((struct atexit_data *)arg)->result = 1;
1531debfc3dSmrg }
1541debfc3dSmrg
cxa_atexit_check_1(void * arg)1551debfc3dSmrg static void cxa_atexit_check_1 (void *arg)
1561debfc3dSmrg {
1571debfc3dSmrg struct atexit_data * aed = arg;
1581debfc3dSmrg if (aed->cxa_atexit (cxa_atexit_check_2, arg, arg) != 0)
1591debfc3dSmrg aed->result = -1;
1601debfc3dSmrg }
1611debfc3dSmrg
1621debfc3dSmrg static int
check_cxa_atexit(cxa_atexit_p cxa_atexit,cxa_finalize_p cxa_finalize)1631debfc3dSmrg check_cxa_atexit (cxa_atexit_p cxa_atexit, cxa_finalize_p cxa_finalize)
1641debfc3dSmrg {
1651debfc3dSmrg struct atexit_data aed = { 0, cxa_atexit };
1661debfc3dSmrg
1671debfc3dSmrg /* We re-use &aed as the 'dso' parameter, since it's a unique address. */
1681debfc3dSmrg if (cxa_atexit (cxa_atexit_check_1, &aed, &aed) != 0)
1691debfc3dSmrg return -1;
1701debfc3dSmrg cxa_finalize (&aed);
1711debfc3dSmrg if (aed.result == 0)
1721debfc3dSmrg {
1731debfc3dSmrg /* Call __cxa_finalize again to make sure that cxa_atexit_check_2
1741debfc3dSmrg is removed from the list before AED goes out of scope. */
1751debfc3dSmrg cxa_finalize (&aed);
1761debfc3dSmrg aed.result = 0;
1771debfc3dSmrg }
1781debfc3dSmrg return aed.result;
1791debfc3dSmrg }
1801debfc3dSmrg
1811debfc3dSmrg #ifdef __ppc__
1821debfc3dSmrg /* This comes from Csu. It works only before 10.4. The prototype has
1831debfc3dSmrg been altered a bit to avoid casting. */
1841debfc3dSmrg extern int _dyld_func_lookup(const char *dyld_func_name,
1851debfc3dSmrg void *address) __attribute__((visibility("hidden")));
1861debfc3dSmrg
1871debfc3dSmrg static void our_atexit (void);
1881debfc3dSmrg
1891debfc3dSmrg /* We're running on 10.3.9. Find the address of the system atexit()
1901debfc3dSmrg function. So easy to say, so hard to do. */
1911debfc3dSmrg static atexit_p
find_atexit_10_3(void)1921debfc3dSmrg find_atexit_10_3 (void)
1931debfc3dSmrg {
1941debfc3dSmrg unsigned int (*dyld_image_count_fn)(void);
1951debfc3dSmrg const char *(*dyld_get_image_name_fn)(unsigned int image_index);
1961debfc3dSmrg const void *(*dyld_get_image_header_fn)(unsigned int image_index);
1971debfc3dSmrg const void *(*NSLookupSymbolInImage_fn)(const void *image,
1981debfc3dSmrg const char *symbolName,
1991debfc3dSmrg unsigned int options);
2001debfc3dSmrg void *(*NSAddressOfSymbol_fn)(const void *symbol);
2011debfc3dSmrg unsigned i, count;
2021debfc3dSmrg
2031debfc3dSmrg /* Find some dyld functions. */
2041debfc3dSmrg _dyld_func_lookup("__dyld_image_count", &dyld_image_count_fn);
2051debfc3dSmrg _dyld_func_lookup("__dyld_get_image_name", &dyld_get_image_name_fn);
2061debfc3dSmrg _dyld_func_lookup("__dyld_get_image_header", &dyld_get_image_header_fn);
2071debfc3dSmrg _dyld_func_lookup("__dyld_NSLookupSymbolInImage", &NSLookupSymbolInImage_fn);
2081debfc3dSmrg _dyld_func_lookup("__dyld_NSAddressOfSymbol", &NSAddressOfSymbol_fn);
2091debfc3dSmrg
2101debfc3dSmrg /* If any of these don't exist, that's an error. */
2111debfc3dSmrg if (! dyld_image_count_fn || ! dyld_get_image_name_fn
2121debfc3dSmrg || ! dyld_get_image_header_fn || ! NSLookupSymbolInImage_fn
2131debfc3dSmrg || ! NSAddressOfSymbol_fn)
2141debfc3dSmrg return NULL;
2151debfc3dSmrg
2161debfc3dSmrg count = dyld_image_count_fn ();
2171debfc3dSmrg for (i = 0; i < count; i++)
2181debfc3dSmrg {
2191debfc3dSmrg const char * path = dyld_get_image_name_fn (i);
2201debfc3dSmrg const void * image;
2211debfc3dSmrg const void * symbol;
2221debfc3dSmrg
2231debfc3dSmrg if (strcmp (path, "/usr/lib/libSystem.B.dylib") != 0)
2241debfc3dSmrg continue;
2251debfc3dSmrg image = dyld_get_image_header_fn (i);
2261debfc3dSmrg if (! image)
2271debfc3dSmrg return NULL;
2281debfc3dSmrg /* '4' is NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR. */
2291debfc3dSmrg symbol = NSLookupSymbolInImage_fn (image, "_atexit", 4);
2301debfc3dSmrg if (! symbol)
2311debfc3dSmrg return NULL;
2321debfc3dSmrg return NSAddressOfSymbol_fn (symbol);
2331debfc3dSmrg }
2341debfc3dSmrg return NULL;
2351debfc3dSmrg }
2361debfc3dSmrg #endif
2371debfc3dSmrg
2381debfc3dSmrg /* Create (if necessary), find, lock, fill in, and return our globals.
2391debfc3dSmrg Return NULL on error, in which case the globals will not be locked.
2401debfc3dSmrg The caller should call keymgr_set_and_unlock. */
2411debfc3dSmrg static struct keymgr_atexit_list *
get_globals(void)2421debfc3dSmrg get_globals (void)
2431debfc3dSmrg {
2441debfc3dSmrg struct keymgr_atexit_list * r;
2451debfc3dSmrg
2461debfc3dSmrg #ifdef __ppc__
2471debfc3dSmrg /* 10.3.9 doesn't have _keymgr_get_and_lock_processwide_ptr_2 so the
2481debfc3dSmrg PPC side can't use it. On 10.4 this just means the error gets
2491debfc3dSmrg reported a little later when
2501debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr finds that the key was
2511debfc3dSmrg never locked. */
2521debfc3dSmrg r = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
2531debfc3dSmrg #else
2541debfc3dSmrg void * rr;
2551debfc3dSmrg if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ATEXIT_LIST, &rr))
2561debfc3dSmrg return NULL;
2571debfc3dSmrg r = rr;
2581debfc3dSmrg #endif
2591debfc3dSmrg
2601debfc3dSmrg if (r == NULL)
2611debfc3dSmrg {
2621debfc3dSmrg r = calloc (sizeof (struct keymgr_atexit_list), 1);
2631debfc3dSmrg if (! r)
2641debfc3dSmrg return NULL;
2651debfc3dSmrg }
2661debfc3dSmrg
2671debfc3dSmrg if (r->atexit_status == atexit_status_unknown)
2681debfc3dSmrg {
2691debfc3dSmrg void *handle;
2701debfc3dSmrg
2711debfc3dSmrg handle = dlopen ("/usr/lib/libSystem.B.dylib", RTLD_NOLOAD);
2721debfc3dSmrg if (!handle)
2731debfc3dSmrg {
2741debfc3dSmrg #ifdef __ppc__
2751debfc3dSmrg r->atexit_status = atexit_status_missing;
2761debfc3dSmrg r->atexit_f = find_atexit_10_3 ();
2771debfc3dSmrg if (! r->atexit_f)
2781debfc3dSmrg goto error;
2791debfc3dSmrg if (r->atexit_f (our_atexit))
2801debfc3dSmrg goto error;
2811debfc3dSmrg #else
2821debfc3dSmrg goto error;
2831debfc3dSmrg #endif
2841debfc3dSmrg }
2851debfc3dSmrg else
2861debfc3dSmrg {
2871debfc3dSmrg int chk_result;
2881debfc3dSmrg
2891debfc3dSmrg r->cxa_atexit_f = (cxa_atexit_p)dlsym (handle, "__cxa_atexit");
2901debfc3dSmrg r->cxa_finalize_f = (cxa_finalize_p)dlsym (handle, "__cxa_finalize");
2911debfc3dSmrg if (! r->cxa_atexit_f || ! r->cxa_finalize_f)
2921debfc3dSmrg goto error;
2931debfc3dSmrg
2941debfc3dSmrg chk_result = check_cxa_atexit (r->cxa_atexit_f, r->cxa_finalize_f);
2951debfc3dSmrg if (chk_result == -1)
2961debfc3dSmrg goto error;
2971debfc3dSmrg else if (chk_result == 0)
2981debfc3dSmrg r->atexit_status = atexit_status_broken;
2991debfc3dSmrg else
3001debfc3dSmrg {
3011debfc3dSmrg r->atexit_f = (atexit_p)dlsym (handle, "atexit");
3021debfc3dSmrg if (! r->atexit_f)
3031debfc3dSmrg goto error;
3041debfc3dSmrg r->atexit_status = atexit_status_working;
3051debfc3dSmrg }
3061debfc3dSmrg }
3071debfc3dSmrg }
3081debfc3dSmrg
3091debfc3dSmrg return r;
3101debfc3dSmrg
3111debfc3dSmrg error:
3121debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, r);
3131debfc3dSmrg return NULL;
3141debfc3dSmrg }
3151debfc3dSmrg
3161debfc3dSmrg /* Add TO_ADD to ATEXIT_LIST. ATEXIT_LIST may be NULL but is
3171debfc3dSmrg always the result of calling _keymgr_get_and_lock_processwide_ptr and
3181debfc3dSmrg so KEYMGR_ATEXIT_LIST is known to be locked; this routine is responsible
3191debfc3dSmrg for unlocking it. */
3201debfc3dSmrg
3211debfc3dSmrg static int
add_routine(struct keymgr_atexit_list * g,const struct one_atexit_routine * to_add)3221debfc3dSmrg add_routine (struct keymgr_atexit_list * g,
3231debfc3dSmrg const struct one_atexit_routine * to_add)
3241debfc3dSmrg {
3251debfc3dSmrg struct atexit_routine_list * s
3261debfc3dSmrg = malloc (sizeof (struct atexit_routine_list));
3271debfc3dSmrg int result;
3281debfc3dSmrg
3291debfc3dSmrg if (!s)
3301debfc3dSmrg {
3311debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
3321debfc3dSmrg return -1;
3331debfc3dSmrg }
3341debfc3dSmrg s->r = *to_add;
3351debfc3dSmrg s->next = g->l;
3361debfc3dSmrg g->l = s;
3371debfc3dSmrg result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
3381debfc3dSmrg return CHECK_KEYMGR_ERROR (result) == 0 ? 0 : -1;
3391debfc3dSmrg }
3401debfc3dSmrg
3411debfc3dSmrg /* This runs the routines in G->L up to STOP. */
3421debfc3dSmrg static struct keymgr_atexit_list *
run_routines(struct keymgr_atexit_list * g,struct atexit_routine_list * stop)3431debfc3dSmrg run_routines (struct keymgr_atexit_list *g,
3441debfc3dSmrg struct atexit_routine_list *stop)
3451debfc3dSmrg {
3461debfc3dSmrg for (;;)
3471debfc3dSmrg {
3481debfc3dSmrg struct atexit_routine_list * cur = g->l;
3491debfc3dSmrg if (! cur || cur == stop)
3501debfc3dSmrg break;
3511debfc3dSmrg g->l = cur->next;
3521debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
3531debfc3dSmrg
3541debfc3dSmrg switch (cur->r.has_arg) {
3551debfc3dSmrg case 0: case 2: case 4:
3561debfc3dSmrg cur->r.callback.ac ();
3571debfc3dSmrg break;
3581debfc3dSmrg case 1: case 3: case 5:
3591debfc3dSmrg cur->r.callback.cac (cur->r.arg);
3601debfc3dSmrg break;
3611debfc3dSmrg default:
3621debfc3dSmrg /* Don't understand, so don't call it. */
3631debfc3dSmrg break;
3641debfc3dSmrg }
3651debfc3dSmrg free (cur);
3661debfc3dSmrg
3671debfc3dSmrg g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
3681debfc3dSmrg if (! g)
3691debfc3dSmrg break;
3701debfc3dSmrg }
3711debfc3dSmrg return g;
3721debfc3dSmrg }
3731debfc3dSmrg
3741debfc3dSmrg /* Call the routine described by ROUTINE_PARAM and then call any
3751debfc3dSmrg routines added to KEYMGR_ATEXIT_LIST while that routine was
3761debfc3dSmrg running, all with in_cxa_finalize set. */
3771debfc3dSmrg
3781debfc3dSmrg static void
cxa_atexit_wrapper(void * routine_param)3791debfc3dSmrg cxa_atexit_wrapper (void* routine_param)
3801debfc3dSmrg {
3811debfc3dSmrg struct one_atexit_routine * routine = routine_param;
3821debfc3dSmrg struct keymgr_atexit_list *g;
3831debfc3dSmrg struct atexit_routine_list * base = NULL;
3841debfc3dSmrg char prev_running = 0;
3851debfc3dSmrg
3861debfc3dSmrg g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
3871debfc3dSmrg if (g)
3881debfc3dSmrg {
3891debfc3dSmrg prev_running = g->running_routines;
3901debfc3dSmrg g->running_routines = 1;
3911debfc3dSmrg base = g->l;
3921debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
3931debfc3dSmrg }
3941debfc3dSmrg
3951debfc3dSmrg if (routine->has_arg)
3961debfc3dSmrg routine->callback.cac (routine->arg);
3971debfc3dSmrg else
3981debfc3dSmrg routine->callback.ac ();
3991debfc3dSmrg
4001debfc3dSmrg if (g)
4011debfc3dSmrg g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
4021debfc3dSmrg if (g)
4031debfc3dSmrg g = run_routines (g, base);
4041debfc3dSmrg if (g)
4051debfc3dSmrg {
4061debfc3dSmrg g->running_routines = prev_running;
4071debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
4081debfc3dSmrg }
4091debfc3dSmrg }
4101debfc3dSmrg
4111debfc3dSmrg #ifdef __ppc__
4121debfc3dSmrg /* This code is used while running on 10.3.9, when __cxa_atexit doesn't
4131debfc3dSmrg exist in the system library. 10.3.9 only supported regular PowerPC,
4141debfc3dSmrg so this code isn't necessary on x86 or ppc64. */
4151debfc3dSmrg
4161debfc3dSmrg /* This routine is called from the system atexit(); it runs everything
4171debfc3dSmrg registered on the KEYMGR_ATEXIT_LIST. */
4181debfc3dSmrg
4191debfc3dSmrg static void
our_atexit(void)4201debfc3dSmrg our_atexit (void)
4211debfc3dSmrg {
4221debfc3dSmrg struct keymgr_atexit_list *g;
4231debfc3dSmrg char prev_running;
4241debfc3dSmrg
4251debfc3dSmrg g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
4261debfc3dSmrg if (! g || g->version != 0 || g->atexit_status != atexit_status_missing)
4271debfc3dSmrg return;
4281debfc3dSmrg
4291debfc3dSmrg prev_running = g->running_routines;
4301debfc3dSmrg g->running_routines = 1;
4311debfc3dSmrg g = run_routines (g, NULL);
4321debfc3dSmrg if (! g)
4331debfc3dSmrg return;
4341debfc3dSmrg g->running_routines = prev_running;
4351debfc3dSmrg _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
4361debfc3dSmrg }
4371debfc3dSmrg #endif
4381debfc3dSmrg
4391debfc3dSmrg /* This is our wrapper around atexit and __cxa_atexit. It will return
4401debfc3dSmrg nonzero if an error occurs, and otherwise:
4411debfc3dSmrg - if in_cxa_finalize is set, or running on 10.3.9, add R to
4421debfc3dSmrg KEYMGR_ATEXIT_LIST; or
4431debfc3dSmrg - call the system __cxa_atexit to add cxa_atexit_wrapper with an argument
4441debfc3dSmrg that indicates how cxa_atexit_wrapper should call R. */
4451debfc3dSmrg
4461debfc3dSmrg static int
atexit_common(const struct one_atexit_routine * r,const void * dso)4471debfc3dSmrg atexit_common (const struct one_atexit_routine *r, const void *dso)
4481debfc3dSmrg {
4491debfc3dSmrg struct keymgr_atexit_list *g = get_globals ();
4501debfc3dSmrg
4511debfc3dSmrg if (! g)
4521debfc3dSmrg return -1;
4531debfc3dSmrg
4541debfc3dSmrg if (g->running_routines || g->atexit_status == atexit_status_missing)
4551debfc3dSmrg return add_routine (g, r);
4561debfc3dSmrg
4571debfc3dSmrg if (g->atexit_status >= atexit_status_working)
4581debfc3dSmrg {
4591debfc3dSmrg int result;
4601debfc3dSmrg if (r->has_arg)
4611debfc3dSmrg {
4621debfc3dSmrg cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
4631debfc3dSmrg result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
4641debfc3dSmrg g);
4651debfc3dSmrg if (CHECK_KEYMGR_ERROR (result))
4661debfc3dSmrg return -1;
4671debfc3dSmrg return cxa_atexit (r->callback.cac, r->arg, dso);
4681debfc3dSmrg }
4691debfc3dSmrg else
4701debfc3dSmrg {
4711debfc3dSmrg atexit_p atexit_f = g->atexit_f;
4721debfc3dSmrg result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
4731debfc3dSmrg g);
4741debfc3dSmrg if (CHECK_KEYMGR_ERROR (result))
4751debfc3dSmrg return -1;
4761debfc3dSmrg return atexit_f (r->callback.ac);
4771debfc3dSmrg }
4781debfc3dSmrg }
4791debfc3dSmrg else
4801debfc3dSmrg {
4811debfc3dSmrg cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
4821debfc3dSmrg struct one_atexit_routine *alloced;
4831debfc3dSmrg int result;
4841debfc3dSmrg
4851debfc3dSmrg result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
4861debfc3dSmrg if (CHECK_KEYMGR_ERROR (result))
4871debfc3dSmrg return -1;
4881debfc3dSmrg
4891debfc3dSmrg alloced = malloc (sizeof (struct one_atexit_routine));
4901debfc3dSmrg if (! alloced)
4911debfc3dSmrg return -1;
4921debfc3dSmrg *alloced = *r;
4931debfc3dSmrg return cxa_atexit (cxa_atexit_wrapper, alloced, dso);
4941debfc3dSmrg }
4951debfc3dSmrg }
4961debfc3dSmrg
4971debfc3dSmrg /* These are the actual replacement routines; they just funnel into
4981debfc3dSmrg atexit_common. */
4991debfc3dSmrg
5001debfc3dSmrg int __cxa_atexit (cxa_atexit_callback func, void* arg,
5011debfc3dSmrg const void* dso) __attribute__((visibility("hidden")));
5021debfc3dSmrg
5031debfc3dSmrg int
__cxa_atexit(cxa_atexit_callback func,void * arg,const void * dso)5041debfc3dSmrg __cxa_atexit (cxa_atexit_callback func, void* arg, const void* dso)
5051debfc3dSmrg {
5061debfc3dSmrg struct one_atexit_routine r;
5071debfc3dSmrg r.callback.cac = func;
5081debfc3dSmrg r.has_arg = 1;
5091debfc3dSmrg r.arg = arg;
5101debfc3dSmrg return atexit_common (&r, dso);
5111debfc3dSmrg }
5121debfc3dSmrg
5131debfc3dSmrg int atexit (atexit_callback func) __attribute__((visibility("hidden")));
5141debfc3dSmrg
5151debfc3dSmrg /* Use __dso_handle to allow even bundles that call atexit() to be unloaded
5161debfc3dSmrg on 10.4. */
5171debfc3dSmrg extern void __dso_handle;
5181debfc3dSmrg
5191debfc3dSmrg int
atexit(atexit_callback func)5201debfc3dSmrg atexit (atexit_callback func)
5211debfc3dSmrg {
5221debfc3dSmrg struct one_atexit_routine r;
5231debfc3dSmrg r.callback.ac = func;
5241debfc3dSmrg r.has_arg = 0;
5251debfc3dSmrg return atexit_common (&r, &__dso_handle);
5261debfc3dSmrg }
5271debfc3dSmrg
5281debfc3dSmrg #endif /* __PIC__ */
529