xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/darwin-crt3.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
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