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