(root)/
glibc-2.38/
stdlib/
cxa_atexit.c
       1  /* Copyright (C) 1999-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <assert.h>
      19  #include <stdlib.h>
      20  #include <stdint.h>
      21  
      22  #include <libc-lock.h>
      23  #include "exit.h"
      24  #include <pointer_guard.h>
      25  
      26  #undef __cxa_atexit
      27  
      28  /* See concurrency notes in stdlib/exit.h where this lock is declared.  */
      29  __libc_lock_define_initialized (, __exit_funcs_lock)
      30  
      31  
      32  int
      33  attribute_hidden
      34  __internal_atexit (void (*func) (void *), void *arg, void *d,
      35  		   struct exit_function_list **listp)
      36  {
      37    struct exit_function *new;
      38  
      39    /* As a QoI issue we detect NULL early with an assertion instead
      40       of a SIGSEGV at program exit when the handler is run (bug 20544).  */
      41    assert (func != NULL);
      42  
      43    __libc_lock_lock (__exit_funcs_lock);
      44    new = __new_exitfn (listp);
      45  
      46    if (new == NULL)
      47      {
      48        __libc_lock_unlock (__exit_funcs_lock);
      49        return -1;
      50      }
      51  
      52    PTR_MANGLE (func);
      53    new->func.cxa.fn = (void (*) (void *, int)) func;
      54    new->func.cxa.arg = arg;
      55    new->func.cxa.dso_handle = d;
      56    new->flavor = ef_cxa;
      57    __libc_lock_unlock (__exit_funcs_lock);
      58    return 0;
      59  }
      60  
      61  
      62  /* Register a function to be called by exit or when a shared library
      63     is unloaded.  This function is only called from code generated by
      64     the C++ compiler.  */
      65  int
      66  __cxa_atexit (void (*func) (void *), void *arg, void *d)
      67  {
      68    return __internal_atexit (func, arg, d, &__exit_funcs);
      69  }
      70  libc_hidden_def (__cxa_atexit)
      71  
      72  
      73  static struct exit_function_list initial;
      74  struct exit_function_list *__exit_funcs = &initial;
      75  uint64_t __new_exitfn_called;
      76  
      77  /* Must be called with __exit_funcs_lock held.  */
      78  struct exit_function *
      79  __new_exitfn (struct exit_function_list **listp)
      80  {
      81    struct exit_function_list *p = NULL;
      82    struct exit_function_list *l;
      83    struct exit_function *r = NULL;
      84    size_t i = 0;
      85  
      86    if (__exit_funcs_done)
      87      /* Exit code is finished processing all registered exit functions,
      88         therefore we fail this registration.  */
      89      return NULL;
      90  
      91    for (l = *listp; l != NULL; p = l, l = l->next)
      92      {
      93        for (i = l->idx; i > 0; --i)
      94  	if (l->fns[i - 1].flavor != ef_free)
      95  	  break;
      96  
      97        if (i > 0)
      98  	break;
      99  
     100        /* This block is completely unused.  */
     101        l->idx = 0;
     102      }
     103  
     104    if (l == NULL || i == sizeof (l->fns) / sizeof (l->fns[0]))
     105      {
     106        /* The last entry in a block is used.  Use the first entry in
     107  	 the previous block if it exists.  Otherwise create a new one.  */
     108        if (p == NULL)
     109  	{
     110  	  assert (l != NULL);
     111  	  p = (struct exit_function_list *)
     112  	    calloc (1, sizeof (struct exit_function_list));
     113  	  if (p != NULL)
     114  	    {
     115  	      p->next = *listp;
     116  	      *listp = p;
     117  	    }
     118  	}
     119  
     120        if (p != NULL)
     121  	{
     122  	  r = &p->fns[0];
     123  	  p->idx = 1;
     124  	}
     125      }
     126    else
     127      {
     128        /* There is more room in the block.  */
     129        r = &l->fns[i];
     130        l->idx = i + 1;
     131      }
     132  
     133    /* Mark entry as used, but we don't know the flavor now.  */
     134    if (r != NULL)
     135      {
     136        r->flavor = ef_us;
     137        ++__new_exitfn_called;
     138      }
     139  
     140    return r;
     141  }