1*e4b17023SJohn Marino /* __cxa_atexit backwards-compatibility support for Darwin.
2*e4b17023SJohn Marino    Copyright (C) 2006, 2009, 2011 Free Software Foundation, Inc.
3*e4b17023SJohn Marino 
4*e4b17023SJohn Marino This file is part of GCC.
5*e4b17023SJohn Marino 
6*e4b17023SJohn Marino GCC is free software; you can redistribute it and/or modify it under
7*e4b17023SJohn Marino the terms of the GNU General Public License as published by the Free
8*e4b17023SJohn Marino Software Foundation; either version 3, or (at your option) any later
9*e4b17023SJohn Marino version.
10*e4b17023SJohn Marino 
11*e4b17023SJohn Marino GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12*e4b17023SJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*e4b17023SJohn Marino FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14*e4b17023SJohn Marino for more details.
15*e4b17023SJohn Marino 
16*e4b17023SJohn Marino Under Section 7 of GPL version 3, you are granted additional
17*e4b17023SJohn Marino permissions described in the GCC Runtime Library Exception, version
18*e4b17023SJohn Marino 3.1, as published by the Free Software Foundation.
19*e4b17023SJohn Marino 
20*e4b17023SJohn Marino You should have received a copy of the GNU General Public License and
21*e4b17023SJohn Marino a copy of the GCC Runtime Library Exception along with this program;
22*e4b17023SJohn Marino see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23*e4b17023SJohn Marino <http://www.gnu.org/licenses/>.  */
24*e4b17023SJohn Marino 
25*e4b17023SJohn Marino /* Don't do anything if we are compiling for a kext multilib. */
26*e4b17023SJohn Marino #ifdef __PIC__
27*e4b17023SJohn Marino 
28*e4b17023SJohn Marino #include "tconfig.h"
29*e4b17023SJohn Marino #include "tsystem.h"
30*e4b17023SJohn Marino 
31*e4b17023SJohn Marino #include <dlfcn.h>
32*e4b17023SJohn Marino #include <stdbool.h>
33*e4b17023SJohn Marino #include <stdlib.h>
34*e4b17023SJohn Marino #include <string.h>
35*e4b17023SJohn Marino 
36*e4b17023SJohn Marino /* This file works around two different problems.
37*e4b17023SJohn Marino 
38*e4b17023SJohn Marino    The first problem is that there is no __cxa_atexit on Mac OS versions
39*e4b17023SJohn Marino    before 10.4.  It fixes this by providing a complete atexit and
40*e4b17023SJohn Marino    __cxa_atexit emulation called from the regular atexit.
41*e4b17023SJohn Marino 
42*e4b17023SJohn Marino    The second problem is that on all shipping versions of Mac OS,
43*e4b17023SJohn Marino    __cxa_finalize and exit() don't work right: they don't run routines
44*e4b17023SJohn Marino    that were registered while other atexit routines are running.  This
45*e4b17023SJohn Marino    is worked around by wrapping each atexit/__cxa_atexit routine with
46*e4b17023SJohn Marino    our own routine which ensures that any __cxa_atexit calls while it
47*e4b17023SJohn Marino    is running are honoured.
48*e4b17023SJohn Marino 
49*e4b17023SJohn Marino    There are still problems which this does not solve.  Before 10.4,
50*e4b17023SJohn Marino    shared objects linked with previous compilers won't have their
51*e4b17023SJohn Marino    atexit calls properly interleaved with code compiled with newer
52*e4b17023SJohn Marino    compilers.  Also, atexit routines registered from shared objects
53*e4b17023SJohn Marino    linked with previous compilers won't get the bug fix.  */
54*e4b17023SJohn Marino 
55*e4b17023SJohn Marino typedef int (*cxa_atexit_p)(void (*func) (void*), void* arg, const void* dso);
56*e4b17023SJohn Marino typedef void (*cxa_finalize_p)(const void *dso);
57*e4b17023SJohn Marino typedef int (*atexit_p)(void (*func)(void));
58*e4b17023SJohn Marino 
59*e4b17023SJohn Marino /* These are from "keymgr.h".  */
60*e4b17023SJohn Marino extern void *_keymgr_get_and_lock_processwide_ptr (unsigned key);
61*e4b17023SJohn Marino extern int _keymgr_get_and_lock_processwide_ptr_2 (unsigned, void **);
62*e4b17023SJohn Marino extern int _keymgr_set_and_unlock_processwide_ptr (unsigned key, void *ptr);
63*e4b17023SJohn Marino 
64*e4b17023SJohn Marino extern void *__keymgr_global[];
65*e4b17023SJohn Marino typedef struct _Sinfo_Node {
66*e4b17023SJohn Marino         unsigned int size ;             /*size of this node*/
67*e4b17023SJohn Marino         unsigned short major_version ;  /*API major version.*/
68*e4b17023SJohn Marino         unsigned short minor_version ;  /*API minor version.*/
69*e4b17023SJohn Marino         } _Tinfo_Node ;
70*e4b17023SJohn Marino 
71*e4b17023SJohn Marino #ifdef __ppc__
72*e4b17023SJohn Marino #define CHECK_KEYMGR_ERROR(e) \
73*e4b17023SJohn Marino   (((_Tinfo_Node *)__keymgr_global[2])->major_version >= 4 ? (e) : 0)
74*e4b17023SJohn Marino #else
75*e4b17023SJohn Marino #define CHECK_KEYMGR_ERROR(e) (e)
76*e4b17023SJohn Marino #endif
77*e4b17023SJohn Marino 
78*e4b17023SJohn Marino /* Our globals are stored under this keymgr index.  */
79*e4b17023SJohn Marino #define KEYMGR_ATEXIT_LIST	14
80*e4b17023SJohn Marino 
81*e4b17023SJohn Marino /* The different kinds of callback routines.  */
82*e4b17023SJohn Marino typedef void (*atexit_callback)(void);
83*e4b17023SJohn Marino typedef void (*cxa_atexit_callback)(void *);
84*e4b17023SJohn Marino 
85*e4b17023SJohn Marino /* This structure holds a routine to call.  There may be extra fields
86*e4b17023SJohn Marino    at the end of the structure that this code doesn't know about.  */
87*e4b17023SJohn Marino struct one_atexit_routine
88*e4b17023SJohn Marino {
89*e4b17023SJohn Marino   union {
90*e4b17023SJohn Marino     atexit_callback ac;
91*e4b17023SJohn Marino     cxa_atexit_callback cac;
92*e4b17023SJohn Marino   } callback;
93*e4b17023SJohn Marino   /* has_arg is 0/2/4 if 'ac' is live, 1/3/5 if 'cac' is live.
94*e4b17023SJohn Marino      Higher numbers indicate a later version of the structure that this
95*e4b17023SJohn Marino      code doesn't understand and will ignore.  */
96*e4b17023SJohn Marino   int has_arg;
97*e4b17023SJohn Marino   void * arg;
98*e4b17023SJohn Marino };
99*e4b17023SJohn Marino 
100*e4b17023SJohn Marino struct atexit_routine_list
101*e4b17023SJohn Marino {
102*e4b17023SJohn Marino   struct atexit_routine_list * next;
103*e4b17023SJohn Marino   struct one_atexit_routine r;
104*e4b17023SJohn Marino };
105*e4b17023SJohn Marino 
106*e4b17023SJohn Marino /* The various possibilities for status of atexit().  */
107*e4b17023SJohn Marino enum atexit_status {
108*e4b17023SJohn Marino   atexit_status_unknown = 0,
109*e4b17023SJohn Marino   atexit_status_missing = 1,
110*e4b17023SJohn Marino   atexit_status_broken = 2,
111*e4b17023SJohn Marino   atexit_status_working = 16
112*e4b17023SJohn Marino };
113*e4b17023SJohn Marino 
114*e4b17023SJohn Marino struct keymgr_atexit_list
115*e4b17023SJohn Marino {
116*e4b17023SJohn Marino   /* Version of this list.  This code knows only about version 0.
117*e4b17023SJohn Marino      If the version is higher than 0, this code may add new atexit routines
118*e4b17023SJohn Marino      but should not attempt to run the list.  */
119*e4b17023SJohn Marino   short version;
120*e4b17023SJohn Marino   /* 1 if an atexit routine is currently being run by this code, 0
121*e4b17023SJohn Marino      otherwise.  */
122*e4b17023SJohn Marino   char running_routines;
123*e4b17023SJohn Marino   /* Holds a value from 'enum atexit_status'.  */
124*e4b17023SJohn Marino   unsigned char atexit_status;
125*e4b17023SJohn Marino   /* The list of atexit and cxa_atexit routines registered.  If
126*e4b17023SJohn Marino    atexit_status_missing it contains all routines registered while
127*e4b17023SJohn Marino    linked with this code.  If atexit_status_broken it contains all
128*e4b17023SJohn Marino    routines registered during cxa_finalize while linked with this
129*e4b17023SJohn Marino    code.  */
130*e4b17023SJohn Marino   struct atexit_routine_list *l;
131*e4b17023SJohn Marino   /* &__cxa_atexit; set if atexit_status >= atexit_status_broken.  */
132*e4b17023SJohn Marino   cxa_atexit_p cxa_atexit_f;
133*e4b17023SJohn Marino   /* &__cxa_finalize; set if atexit_status >= atexit_status_broken.  */
134*e4b17023SJohn Marino   cxa_finalize_p cxa_finalize_f;
135*e4b17023SJohn Marino   /* &atexit; set if atexit_status >= atexit_status_working
136*e4b17023SJohn Marino      or atexit_status == atexit_status_missing.  */
137*e4b17023SJohn Marino   atexit_p atexit_f;
138*e4b17023SJohn Marino };
139*e4b17023SJohn Marino 
140*e4b17023SJohn Marino /* Return 0 if __cxa_atexit has the bug it has in Mac OS 10.4: it
141*e4b17023SJohn Marino    fails to call routines registered while an atexit routine is
142*e4b17023SJohn Marino    running.  Return 1 if it works properly, and -1 if an error occurred.  */
143*e4b17023SJohn Marino 
144*e4b17023SJohn Marino struct atexit_data
145*e4b17023SJohn Marino {
146*e4b17023SJohn Marino   int result;
147*e4b17023SJohn Marino   cxa_atexit_p cxa_atexit;
148*e4b17023SJohn Marino };
149*e4b17023SJohn Marino 
cxa_atexit_check_2(void * arg)150*e4b17023SJohn Marino static void cxa_atexit_check_2 (void *arg)
151*e4b17023SJohn Marino {
152*e4b17023SJohn Marino   ((struct atexit_data *)arg)->result = 1;
153*e4b17023SJohn Marino }
154*e4b17023SJohn Marino 
cxa_atexit_check_1(void * arg)155*e4b17023SJohn Marino static void cxa_atexit_check_1 (void *arg)
156*e4b17023SJohn Marino {
157*e4b17023SJohn Marino   struct atexit_data * aed = arg;
158*e4b17023SJohn Marino   if (aed->cxa_atexit (cxa_atexit_check_2, arg, arg) != 0)
159*e4b17023SJohn Marino     aed->result = -1;
160*e4b17023SJohn Marino }
161*e4b17023SJohn Marino 
162*e4b17023SJohn Marino static int
check_cxa_atexit(cxa_atexit_p cxa_atexit,cxa_finalize_p cxa_finalize)163*e4b17023SJohn Marino check_cxa_atexit (cxa_atexit_p cxa_atexit, cxa_finalize_p cxa_finalize)
164*e4b17023SJohn Marino {
165*e4b17023SJohn Marino   struct atexit_data aed = { 0, cxa_atexit };
166*e4b17023SJohn Marino 
167*e4b17023SJohn Marino   /* We re-use &aed as the 'dso' parameter, since it's a unique address.  */
168*e4b17023SJohn Marino   if (cxa_atexit (cxa_atexit_check_1, &aed, &aed) != 0)
169*e4b17023SJohn Marino     return -1;
170*e4b17023SJohn Marino   cxa_finalize (&aed);
171*e4b17023SJohn Marino   if (aed.result == 0)
172*e4b17023SJohn Marino     {
173*e4b17023SJohn Marino       /* Call __cxa_finalize again to make sure that cxa_atexit_check_2
174*e4b17023SJohn Marino 	 is removed from the list before AED goes out of scope.  */
175*e4b17023SJohn Marino       cxa_finalize (&aed);
176*e4b17023SJohn Marino       aed.result = 0;
177*e4b17023SJohn Marino     }
178*e4b17023SJohn Marino   return aed.result;
179*e4b17023SJohn Marino }
180*e4b17023SJohn Marino 
181*e4b17023SJohn Marino #ifdef __ppc__
182*e4b17023SJohn Marino /* This comes from Csu.  It works only before 10.4.  The prototype has
183*e4b17023SJohn Marino    been altered a bit to avoid casting.  */
184*e4b17023SJohn Marino extern int _dyld_func_lookup(const char *dyld_func_name,
185*e4b17023SJohn Marino      void *address) __attribute__((visibility("hidden")));
186*e4b17023SJohn Marino 
187*e4b17023SJohn Marino static void our_atexit (void);
188*e4b17023SJohn Marino 
189*e4b17023SJohn Marino /* We're running on 10.3.9.  Find the address of the system atexit()
190*e4b17023SJohn Marino    function.  So easy to say, so hard to do.  */
191*e4b17023SJohn Marino static atexit_p
find_atexit_10_3(void)192*e4b17023SJohn Marino find_atexit_10_3 (void)
193*e4b17023SJohn Marino {
194*e4b17023SJohn Marino   unsigned int (*dyld_image_count_fn)(void);
195*e4b17023SJohn Marino   const char *(*dyld_get_image_name_fn)(unsigned int image_index);
196*e4b17023SJohn Marino   const void *(*dyld_get_image_header_fn)(unsigned int image_index);
197*e4b17023SJohn Marino   const void *(*NSLookupSymbolInImage_fn)(const void *image,
198*e4b17023SJohn Marino 					  const char *symbolName,
199*e4b17023SJohn Marino 					  unsigned int options);
200*e4b17023SJohn Marino   void *(*NSAddressOfSymbol_fn)(const void *symbol);
201*e4b17023SJohn Marino   unsigned i, count;
202*e4b17023SJohn Marino 
203*e4b17023SJohn Marino   /* Find some dyld functions.  */
204*e4b17023SJohn Marino   _dyld_func_lookup("__dyld_image_count", &dyld_image_count_fn);
205*e4b17023SJohn Marino   _dyld_func_lookup("__dyld_get_image_name", &dyld_get_image_name_fn);
206*e4b17023SJohn Marino   _dyld_func_lookup("__dyld_get_image_header", &dyld_get_image_header_fn);
207*e4b17023SJohn Marino   _dyld_func_lookup("__dyld_NSLookupSymbolInImage", &NSLookupSymbolInImage_fn);
208*e4b17023SJohn Marino   _dyld_func_lookup("__dyld_NSAddressOfSymbol", &NSAddressOfSymbol_fn);
209*e4b17023SJohn Marino 
210*e4b17023SJohn Marino   /* If any of these don't exist, that's an error.  */
211*e4b17023SJohn Marino   if (! dyld_image_count_fn || ! dyld_get_image_name_fn
212*e4b17023SJohn Marino       || ! dyld_get_image_header_fn || ! NSLookupSymbolInImage_fn
213*e4b17023SJohn Marino       || ! NSAddressOfSymbol_fn)
214*e4b17023SJohn Marino     return NULL;
215*e4b17023SJohn Marino 
216*e4b17023SJohn Marino   count = dyld_image_count_fn ();
217*e4b17023SJohn Marino   for (i = 0; i < count; i++)
218*e4b17023SJohn Marino     {
219*e4b17023SJohn Marino       const char * path = dyld_get_image_name_fn (i);
220*e4b17023SJohn Marino       const void * image;
221*e4b17023SJohn Marino       const void * symbol;
222*e4b17023SJohn Marino 
223*e4b17023SJohn Marino       if (strcmp (path, "/usr/lib/libSystem.B.dylib") != 0)
224*e4b17023SJohn Marino 	continue;
225*e4b17023SJohn Marino       image = dyld_get_image_header_fn (i);
226*e4b17023SJohn Marino       if (! image)
227*e4b17023SJohn Marino 	return NULL;
228*e4b17023SJohn Marino       /* '4' is NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR.  */
229*e4b17023SJohn Marino       symbol = NSLookupSymbolInImage_fn (image, "_atexit", 4);
230*e4b17023SJohn Marino       if (! symbol)
231*e4b17023SJohn Marino 	return NULL;
232*e4b17023SJohn Marino       return NSAddressOfSymbol_fn (symbol);
233*e4b17023SJohn Marino     }
234*e4b17023SJohn Marino   return NULL;
235*e4b17023SJohn Marino }
236*e4b17023SJohn Marino #endif
237*e4b17023SJohn Marino 
238*e4b17023SJohn Marino /* Create (if necessary), find, lock, fill in, and return our globals.
239*e4b17023SJohn Marino    Return NULL on error, in which case the globals will not be locked.
240*e4b17023SJohn Marino    The caller should call keymgr_set_and_unlock.  */
241*e4b17023SJohn Marino static struct keymgr_atexit_list *
get_globals(void)242*e4b17023SJohn Marino get_globals (void)
243*e4b17023SJohn Marino {
244*e4b17023SJohn Marino   struct keymgr_atexit_list * r;
245*e4b17023SJohn Marino 
246*e4b17023SJohn Marino #ifdef __ppc__
247*e4b17023SJohn Marino   /* 10.3.9 doesn't have _keymgr_get_and_lock_processwide_ptr_2 so the
248*e4b17023SJohn Marino      PPC side can't use it.  On 10.4 this just means the error gets
249*e4b17023SJohn Marino      reported a little later when
250*e4b17023SJohn Marino      _keymgr_set_and_unlock_processwide_ptr finds that the key was
251*e4b17023SJohn Marino      never locked.  */
252*e4b17023SJohn Marino   r = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
253*e4b17023SJohn Marino #else
254*e4b17023SJohn Marino   void * rr;
255*e4b17023SJohn Marino   if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ATEXIT_LIST, &rr))
256*e4b17023SJohn Marino     return NULL;
257*e4b17023SJohn Marino   r = rr;
258*e4b17023SJohn Marino #endif
259*e4b17023SJohn Marino 
260*e4b17023SJohn Marino   if (r == NULL)
261*e4b17023SJohn Marino     {
262*e4b17023SJohn Marino       r = calloc (sizeof (struct keymgr_atexit_list), 1);
263*e4b17023SJohn Marino       if (! r)
264*e4b17023SJohn Marino 	return NULL;
265*e4b17023SJohn Marino     }
266*e4b17023SJohn Marino 
267*e4b17023SJohn Marino   if (r->atexit_status == atexit_status_unknown)
268*e4b17023SJohn Marino     {
269*e4b17023SJohn Marino       void *handle;
270*e4b17023SJohn Marino 
271*e4b17023SJohn Marino       handle = dlopen ("/usr/lib/libSystem.B.dylib", RTLD_NOLOAD);
272*e4b17023SJohn Marino       if (!handle)
273*e4b17023SJohn Marino 	{
274*e4b17023SJohn Marino #ifdef __ppc__
275*e4b17023SJohn Marino 	  r->atexit_status = atexit_status_missing;
276*e4b17023SJohn Marino 	  r->atexit_f = find_atexit_10_3 ();
277*e4b17023SJohn Marino 	  if (! r->atexit_f)
278*e4b17023SJohn Marino 	    goto error;
279*e4b17023SJohn Marino 	  if (r->atexit_f (our_atexit))
280*e4b17023SJohn Marino 	    goto error;
281*e4b17023SJohn Marino #else
282*e4b17023SJohn Marino 	  goto error;
283*e4b17023SJohn Marino #endif
284*e4b17023SJohn Marino 	}
285*e4b17023SJohn Marino       else
286*e4b17023SJohn Marino 	{
287*e4b17023SJohn Marino 	  int chk_result;
288*e4b17023SJohn Marino 
289*e4b17023SJohn Marino 	  r->cxa_atexit_f = (cxa_atexit_p)dlsym (handle, "__cxa_atexit");
290*e4b17023SJohn Marino 	  r->cxa_finalize_f = (cxa_finalize_p)dlsym (handle, "__cxa_finalize");
291*e4b17023SJohn Marino 	  if (! r->cxa_atexit_f || ! r->cxa_finalize_f)
292*e4b17023SJohn Marino 	    goto error;
293*e4b17023SJohn Marino 
294*e4b17023SJohn Marino 	  chk_result = check_cxa_atexit (r->cxa_atexit_f, r->cxa_finalize_f);
295*e4b17023SJohn Marino 	  if (chk_result == -1)
296*e4b17023SJohn Marino 	    goto error;
297*e4b17023SJohn Marino 	  else if (chk_result == 0)
298*e4b17023SJohn Marino 	    r->atexit_status = atexit_status_broken;
299*e4b17023SJohn Marino 	  else
300*e4b17023SJohn Marino 	    {
301*e4b17023SJohn Marino 	      r->atexit_f = (atexit_p)dlsym (handle, "atexit");
302*e4b17023SJohn Marino 	      if (! r->atexit_f)
303*e4b17023SJohn Marino 		goto error;
304*e4b17023SJohn Marino 	      r->atexit_status = atexit_status_working;
305*e4b17023SJohn Marino 	    }
306*e4b17023SJohn Marino 	}
307*e4b17023SJohn Marino     }
308*e4b17023SJohn Marino 
309*e4b17023SJohn Marino   return r;
310*e4b17023SJohn Marino 
311*e4b17023SJohn Marino  error:
312*e4b17023SJohn Marino   _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, r);
313*e4b17023SJohn Marino   return NULL;
314*e4b17023SJohn Marino }
315*e4b17023SJohn Marino 
316*e4b17023SJohn Marino /* Add TO_ADD to ATEXIT_LIST.  ATEXIT_LIST may be NULL but is
317*e4b17023SJohn Marino    always the result of calling _keymgr_get_and_lock_processwide_ptr and
318*e4b17023SJohn Marino    so KEYMGR_ATEXIT_LIST is known to be locked; this routine is responsible
319*e4b17023SJohn Marino    for unlocking it.  */
320*e4b17023SJohn Marino 
321*e4b17023SJohn Marino static int
add_routine(struct keymgr_atexit_list * g,const struct one_atexit_routine * to_add)322*e4b17023SJohn Marino add_routine (struct keymgr_atexit_list * g,
323*e4b17023SJohn Marino 	     const struct one_atexit_routine * to_add)
324*e4b17023SJohn Marino {
325*e4b17023SJohn Marino   struct atexit_routine_list * s
326*e4b17023SJohn Marino     = malloc (sizeof (struct atexit_routine_list));
327*e4b17023SJohn Marino   int result;
328*e4b17023SJohn Marino 
329*e4b17023SJohn Marino   if (!s)
330*e4b17023SJohn Marino     {
331*e4b17023SJohn Marino       _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
332*e4b17023SJohn Marino       return -1;
333*e4b17023SJohn Marino     }
334*e4b17023SJohn Marino   s->r = *to_add;
335*e4b17023SJohn Marino   s->next = g->l;
336*e4b17023SJohn Marino   g->l = s;
337*e4b17023SJohn Marino   result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
338*e4b17023SJohn Marino   return CHECK_KEYMGR_ERROR (result) == 0 ? 0 : -1;
339*e4b17023SJohn Marino }
340*e4b17023SJohn Marino 
341*e4b17023SJohn Marino /* This runs the routines in G->L up to STOP.  */
342*e4b17023SJohn Marino static struct keymgr_atexit_list *
run_routines(struct keymgr_atexit_list * g,struct atexit_routine_list * stop)343*e4b17023SJohn Marino run_routines (struct keymgr_atexit_list *g,
344*e4b17023SJohn Marino 	      struct atexit_routine_list *stop)
345*e4b17023SJohn Marino {
346*e4b17023SJohn Marino   for (;;)
347*e4b17023SJohn Marino     {
348*e4b17023SJohn Marino       struct atexit_routine_list * cur = g->l;
349*e4b17023SJohn Marino       if (! cur || cur == stop)
350*e4b17023SJohn Marino 	break;
351*e4b17023SJohn Marino       g->l = cur->next;
352*e4b17023SJohn Marino       _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
353*e4b17023SJohn Marino 
354*e4b17023SJohn Marino       switch (cur->r.has_arg) {
355*e4b17023SJohn Marino       case 0: case 2: case 4:
356*e4b17023SJohn Marino 	cur->r.callback.ac ();
357*e4b17023SJohn Marino 	break;
358*e4b17023SJohn Marino       case 1: case 3: case 5:
359*e4b17023SJohn Marino 	cur->r.callback.cac (cur->r.arg);
360*e4b17023SJohn Marino 	break;
361*e4b17023SJohn Marino       default:
362*e4b17023SJohn Marino 	/* Don't understand, so don't call it.  */
363*e4b17023SJohn Marino 	break;
364*e4b17023SJohn Marino       }
365*e4b17023SJohn Marino       free (cur);
366*e4b17023SJohn Marino 
367*e4b17023SJohn Marino       g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
368*e4b17023SJohn Marino       if (! g)
369*e4b17023SJohn Marino 	break;
370*e4b17023SJohn Marino     }
371*e4b17023SJohn Marino   return g;
372*e4b17023SJohn Marino }
373*e4b17023SJohn Marino 
374*e4b17023SJohn Marino /* Call the routine described by ROUTINE_PARAM and then call any
375*e4b17023SJohn Marino    routines added to KEYMGR_ATEXIT_LIST while that routine was
376*e4b17023SJohn Marino    running, all with in_cxa_finalize set.  */
377*e4b17023SJohn Marino 
378*e4b17023SJohn Marino static void
cxa_atexit_wrapper(void * routine_param)379*e4b17023SJohn Marino cxa_atexit_wrapper (void* routine_param)
380*e4b17023SJohn Marino {
381*e4b17023SJohn Marino   struct one_atexit_routine * routine = routine_param;
382*e4b17023SJohn Marino   struct keymgr_atexit_list *g;
383*e4b17023SJohn Marino   struct atexit_routine_list * base = NULL;
384*e4b17023SJohn Marino   char prev_running = 0;
385*e4b17023SJohn Marino 
386*e4b17023SJohn Marino   g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
387*e4b17023SJohn Marino   if (g)
388*e4b17023SJohn Marino     {
389*e4b17023SJohn Marino       prev_running = g->running_routines;
390*e4b17023SJohn Marino       g->running_routines = 1;
391*e4b17023SJohn Marino       base = g->l;
392*e4b17023SJohn Marino       _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
393*e4b17023SJohn Marino     }
394*e4b17023SJohn Marino 
395*e4b17023SJohn Marino   if (routine->has_arg)
396*e4b17023SJohn Marino     routine->callback.cac (routine->arg);
397*e4b17023SJohn Marino   else
398*e4b17023SJohn Marino     routine->callback.ac ();
399*e4b17023SJohn Marino 
400*e4b17023SJohn Marino   if (g)
401*e4b17023SJohn Marino     g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
402*e4b17023SJohn Marino   if (g)
403*e4b17023SJohn Marino     g = run_routines (g, base);
404*e4b17023SJohn Marino   if (g)
405*e4b17023SJohn Marino     {
406*e4b17023SJohn Marino       g->running_routines = prev_running;
407*e4b17023SJohn Marino       _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
408*e4b17023SJohn Marino     }
409*e4b17023SJohn Marino }
410*e4b17023SJohn Marino 
411*e4b17023SJohn Marino #ifdef __ppc__
412*e4b17023SJohn Marino /* This code is used while running on 10.3.9, when __cxa_atexit doesn't
413*e4b17023SJohn Marino    exist in the system library.  10.3.9 only supported regular PowerPC,
414*e4b17023SJohn Marino    so this code isn't necessary on x86 or ppc64.  */
415*e4b17023SJohn Marino 
416*e4b17023SJohn Marino /* This routine is called from the system atexit(); it runs everything
417*e4b17023SJohn Marino    registered on the KEYMGR_ATEXIT_LIST.  */
418*e4b17023SJohn Marino 
419*e4b17023SJohn Marino static void
our_atexit(void)420*e4b17023SJohn Marino our_atexit (void)
421*e4b17023SJohn Marino {
422*e4b17023SJohn Marino   struct keymgr_atexit_list *g;
423*e4b17023SJohn Marino   char prev_running;
424*e4b17023SJohn Marino 
425*e4b17023SJohn Marino   g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST);
426*e4b17023SJohn Marino   if (! g || g->version != 0 || g->atexit_status != atexit_status_missing)
427*e4b17023SJohn Marino     return;
428*e4b17023SJohn Marino 
429*e4b17023SJohn Marino   prev_running = g->running_routines;
430*e4b17023SJohn Marino   g->running_routines = 1;
431*e4b17023SJohn Marino   g = run_routines (g, NULL);
432*e4b17023SJohn Marino   if (! g)
433*e4b17023SJohn Marino     return;
434*e4b17023SJohn Marino   g->running_routines = prev_running;
435*e4b17023SJohn Marino   _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
436*e4b17023SJohn Marino }
437*e4b17023SJohn Marino #endif
438*e4b17023SJohn Marino 
439*e4b17023SJohn Marino /* This is our wrapper around atexit and __cxa_atexit.  It will return
440*e4b17023SJohn Marino    nonzero if an error occurs, and otherwise:
441*e4b17023SJohn Marino    - if in_cxa_finalize is set, or running on 10.3.9, add R to
442*e4b17023SJohn Marino      KEYMGR_ATEXIT_LIST; or
443*e4b17023SJohn Marino    - call the system __cxa_atexit to add cxa_atexit_wrapper with an argument
444*e4b17023SJohn Marino      that indicates how cxa_atexit_wrapper should call R.  */
445*e4b17023SJohn Marino 
446*e4b17023SJohn Marino static int
atexit_common(const struct one_atexit_routine * r,const void * dso)447*e4b17023SJohn Marino atexit_common (const struct one_atexit_routine *r, const void *dso)
448*e4b17023SJohn Marino {
449*e4b17023SJohn Marino   struct keymgr_atexit_list *g = get_globals ();
450*e4b17023SJohn Marino 
451*e4b17023SJohn Marino   if (! g)
452*e4b17023SJohn Marino     return -1;
453*e4b17023SJohn Marino 
454*e4b17023SJohn Marino   if (g->running_routines || g->atexit_status == atexit_status_missing)
455*e4b17023SJohn Marino     return add_routine (g, r);
456*e4b17023SJohn Marino 
457*e4b17023SJohn Marino   if (g->atexit_status >= atexit_status_working)
458*e4b17023SJohn Marino     {
459*e4b17023SJohn Marino       int result;
460*e4b17023SJohn Marino       if (r->has_arg)
461*e4b17023SJohn Marino 	{
462*e4b17023SJohn Marino 	  cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
463*e4b17023SJohn Marino 	  result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
464*e4b17023SJohn Marino 							   g);
465*e4b17023SJohn Marino 	  if (CHECK_KEYMGR_ERROR (result))
466*e4b17023SJohn Marino 	    return -1;
467*e4b17023SJohn Marino 	  return cxa_atexit (r->callback.cac, r->arg, dso);
468*e4b17023SJohn Marino 	}
469*e4b17023SJohn Marino       else
470*e4b17023SJohn Marino 	{
471*e4b17023SJohn Marino 	  atexit_p atexit_f = g->atexit_f;
472*e4b17023SJohn Marino 	  result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST,
473*e4b17023SJohn Marino 							   g);
474*e4b17023SJohn Marino 	  if (CHECK_KEYMGR_ERROR (result))
475*e4b17023SJohn Marino 	    return -1;
476*e4b17023SJohn Marino 	  return atexit_f (r->callback.ac);
477*e4b17023SJohn Marino 	}
478*e4b17023SJohn Marino     }
479*e4b17023SJohn Marino   else
480*e4b17023SJohn Marino     {
481*e4b17023SJohn Marino       cxa_atexit_p cxa_atexit = g->cxa_atexit_f;
482*e4b17023SJohn Marino       struct one_atexit_routine *alloced;
483*e4b17023SJohn Marino       int result;
484*e4b17023SJohn Marino 
485*e4b17023SJohn Marino       result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g);
486*e4b17023SJohn Marino       if (CHECK_KEYMGR_ERROR (result))
487*e4b17023SJohn Marino 	return -1;
488*e4b17023SJohn Marino 
489*e4b17023SJohn Marino       alloced = malloc (sizeof (struct one_atexit_routine));
490*e4b17023SJohn Marino       if (! alloced)
491*e4b17023SJohn Marino 	return -1;
492*e4b17023SJohn Marino       *alloced = *r;
493*e4b17023SJohn Marino       return cxa_atexit (cxa_atexit_wrapper, alloced, dso);
494*e4b17023SJohn Marino     }
495*e4b17023SJohn Marino }
496*e4b17023SJohn Marino 
497*e4b17023SJohn Marino /* These are the actual replacement routines; they just funnel into
498*e4b17023SJohn Marino    atexit_common.  */
499*e4b17023SJohn Marino 
500*e4b17023SJohn Marino int __cxa_atexit (cxa_atexit_callback func, void* arg,
501*e4b17023SJohn Marino 		  const void* dso) __attribute__((visibility("hidden")));
502*e4b17023SJohn Marino 
503*e4b17023SJohn Marino int
__cxa_atexit(cxa_atexit_callback func,void * arg,const void * dso)504*e4b17023SJohn Marino __cxa_atexit (cxa_atexit_callback func, void* arg, const void* dso)
505*e4b17023SJohn Marino {
506*e4b17023SJohn Marino   struct one_atexit_routine r;
507*e4b17023SJohn Marino   r.callback.cac = func;
508*e4b17023SJohn Marino   r.has_arg = 1;
509*e4b17023SJohn Marino   r.arg = arg;
510*e4b17023SJohn Marino   return atexit_common (&r, dso);
511*e4b17023SJohn Marino }
512*e4b17023SJohn Marino 
513*e4b17023SJohn Marino int atexit (atexit_callback func) __attribute__((visibility("hidden")));
514*e4b17023SJohn Marino 
515*e4b17023SJohn Marino /* Use __dso_handle to allow even bundles that call atexit() to be unloaded
516*e4b17023SJohn Marino    on 10.4.  */
517*e4b17023SJohn Marino extern void __dso_handle;
518*e4b17023SJohn Marino 
519*e4b17023SJohn Marino int
atexit(atexit_callback func)520*e4b17023SJohn Marino atexit (atexit_callback func)
521*e4b17023SJohn Marino {
522*e4b17023SJohn Marino   struct one_atexit_routine r;
523*e4b17023SJohn Marino   r.callback.ac = func;
524*e4b17023SJohn Marino   r.has_arg = 0;
525*e4b17023SJohn Marino   return atexit_common (&r, &__dso_handle);
526*e4b17023SJohn Marino }
527*e4b17023SJohn Marino 
528*e4b17023SJohn Marino #endif /* __PIC__ */
529