This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[glibc/azanella/atexit-order] stdlib: Make atexit to not act as __cxa_atexit


https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=ba11e52665a79c545a51a871397143b5f7827492

commit ba11e52665a79c545a51a871397143b5f7827492
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Wed Jun 12 10:37:11 2019 -0300

    stdlib: Make atexit to not act as __cxa_atexit
    
    This is patch to addresses the issue brought in recent discussion
    regarding atexit and shared libraries [1] [2].  As indicated in the
    libc-alpha discussion, the issue is since atexit uses __cxa_atexit
    its interaction __cxa_finalize could lead to atexit handlers being
    executed in a different order than the expected one.  The github
    project gives a small example that triggers it [3].
    
    The changes I could come with changes slight the atexit semantic
    as described in my last email [4].  So basically the changes are:
    
      1. Add the __atexit symbol which is linked as __cxa_finalize in
         static mode (so __dso_handle is correctly set).  The __atexit
         symbol adds an ef_at exit_function entry on __exit_funcs,
         different than an ef_cxa one from __cxa_atexit.
    
         Old binaries would still call __cxa_atexit, so we do not actually
         need to add a compat symbol.
    
      2. Make __cxa_finalize to handle ef_at as well, similar to ef_cxa.
    
      3. Change how the internal exit handler are organized, so ef_at
         and ef_on handles (registered by atexit and on_exit) are executed
         before ef_cxa (registered by __cxa_atexit).
    
         Each entry set (struct exit_function_list) has on type associated
         (el_at or el_cxa) to represent the internal handle it contains.
         New insertions (done by the __atexit, __cxa_atexit, etc.) keep the
         node orders, with following constraints:
    
         3.1. el_at nodes should be prior el_cxa.
         3.2. el_at should contain only ef_at, ef_on, or ef_free elements.
         3.3. el_cxa should contain only ef_cxa or ef_free elements.
         3.4. new insertions on each node type should be be kept in lifo order.
         3.5. the original first element should be last one (since it is static
              allocated and 'exit' will deallocated the nodes in order.  */
    
         So the execution on both __cxa_finalize, exit, or quick_exit will
         iterate over the list by executing first atexit/on_exit handlers and
         then __cxa_atexit ones.  New handlers added by registered functions
         are handled as before, by using the ef_free entry and reseting the
         list iteration.
    
    [1] https://sourceware.org/ml/libc-alpha/2019-06/msg00229.html
    [2] https://sourceware.org/ml/libc-help/2019-06/msg00025.html
    [3] https://github.com/mulle-nat/ld-so-breakage
    [4] https://sourceware.org/ml/libc-alpha/2019-06/msg00231.html

Diff:
---
 dlfcn/tstatexit.c                              |  30 +---
 include/stdlib.h                               |   2 +
 stdlib/Makefile                                |   2 +-
 stdlib/Versions                                |   3 +
 stdlib/atexit.c                                |   5 +-
 stdlib/cxa_at_quick_exit.c                     |   6 +-
 stdlib/cxa_atexit.c                            | 114 +-------------
 stdlib/cxa_finalize.c                          | 131 ++++++++++-------
 stdlib/exit.c                                  |   9 +-
 stdlib/exit.h                                  |  33 +++--
 stdlib/exitfn.c                                | 196 +++++++++++++++++++++++++
 stdlib/old_atexit.c                            |  10 +-
 stdlib/on_exit.c                               |  24 +--
 sysdeps/unix/sysv/linux/i386/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/x86_64/64/libc.abilist |   1 +
 15 files changed, 332 insertions(+), 235 deletions(-)

diff --git a/dlfcn/tstatexit.c b/dlfcn/tstatexit.c
index c8cb272..1f6037c 100644
--- a/dlfcn/tstatexit.c
+++ b/dlfcn/tstatexit.c
@@ -19,6 +19,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <support/xdlfcn.h>
+#include <support/check.h>
 
 int
 main (void)
@@ -28,35 +30,15 @@ main (void)
   void (*fp) (void *);
   int v = 0;
 
-  h = dlopen (fname, RTLD_NOW);
-  if (h == NULL)
-    {
-      printf ("cannot open \"%s\": %s\n", fname, dlerror ());
-      exit (1);
-    }
+  h = xdlopen (fname, RTLD_NOW);
 
-  fp = dlsym (h, "foo");
-  if (fp == NULL)
-    {
-      printf ("cannot find \"foo\": %s\n", dlerror ());
-      exit (1);
-    }
+  fp = xdlsym (h, "foo");
 
   fp (&v);
 
-  if (dlclose (h) != 0)
-    {
-      printf ("cannot close \"%s\": %s\n", fname, dlerror ());
-      exit (1);
-    }
+  xdlclose (h);
 
-  if (v != 1)
-    {
-      puts ("module unload didn't change `v'");
-      exit (1);
-    }
-
-  puts ("finishing now");
+  TEST_COMPARE (v, 1);
 
   return 0;
 }
diff --git a/include/stdlib.h b/include/stdlib.h
index 114e12d..9489afd 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -103,6 +103,8 @@ extern int __on_exit (void (*__func) (int __status, void *__arg), void *__arg);
 extern int __cxa_atexit (void (*func) (void *), void *arg, void *d);
 libc_hidden_proto (__cxa_atexit);
 
+extern int __atexit (void (*func) (void), void *);
+
 extern int __cxa_thread_atexit_impl (void (*func) (void *), void *arg,
 				     void *d);
 extern void __call_tls_dtors (void)
diff --git a/stdlib/Makefile b/stdlib/Makefile
index fe5b39a..4a9d62f 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -36,7 +36,7 @@ routines	:=							      \
 	abort								      \
 	bsearch qsort msort						      \
 	getenv putenv setenv secure-getenv				      \
-	exit on_exit atexit cxa_atexit cxa_finalize old_atexit		      \
+	exit on_exit atexit cxa_atexit cxa_finalize old_atexit exitfn	      \
 	quick_exit at_quick_exit cxa_at_quick_exit cxa_thread_atexit_impl     \
 	abs labs llabs							      \
 	div ldiv lldiv							      \
diff --git a/stdlib/Versions b/stdlib/Versions
index 9e665d4..14cf150 100644
--- a/stdlib/Versions
+++ b/stdlib/Versions
@@ -136,6 +136,9 @@ libc {
     strtof32; strtof64; strtof32x;
     strtof32_l; strtof64_l; strtof32x_l;
   }
+  GLIBC_2.29 {
+    __atexit;
+  }
   GLIBC_PRIVATE {
     # functions which have an additional interface since they are
     # are cancelable.
diff --git a/stdlib/atexit.c b/stdlib/atexit.c
index 5671a43..03b0b4b 100644
--- a/stdlib/atexit.c
+++ b/stdlib/atexit.c
@@ -32,9 +32,8 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <stdlib.h>
+#include <exit.h>
 #include <dso_handle.h>
-#include "exit.h"
 
 /* Register FUNC to be executed by `exit'.  */
 int
@@ -43,5 +42,5 @@ attribute_hidden
 #endif
 atexit (void (*func) (void))
 {
-  return __cxa_atexit ((void (*) (void *)) func, NULL, __dso_handle);
+  return __atexit (func, __dso_handle);
 }
diff --git a/stdlib/cxa_at_quick_exit.c b/stdlib/cxa_at_quick_exit.c
index ce3cfc5..32acbd5 100644
--- a/stdlib/cxa_at_quick_exit.c
+++ b/stdlib/cxa_at_quick_exit.c
@@ -16,8 +16,8 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <stdlib.h>
-#include "exit.h"
-
+#include <exit.h>
+#include <assert.h>
 
 static struct exit_function_list initial_quick;
 struct exit_function_list *__quick_exit_funcs = &initial_quick;
@@ -26,5 +26,5 @@ struct exit_function_list *__quick_exit_funcs = &initial_quick;
 int
 __cxa_at_quick_exit (void (*func) (void *), void *d)
 {
-  return __internal_atexit (func, NULL, d, &__quick_exit_funcs);
+  return __new_exitfn (&__quick_exit_funcs, ef_cxa, func, NULL, d);
 }
diff --git a/stdlib/cxa_atexit.c b/stdlib/cxa_atexit.c
index 5a39778..01f5474 100644
--- a/stdlib/cxa_atexit.c
+++ b/stdlib/cxa_atexit.c
@@ -20,46 +20,11 @@
 #include <stdint.h>
 
 #include <libc-lock.h>
-#include "exit.h"
+#include <exit.h>
 #include <sysdep.h>
 
-#undef __cxa_atexit
-
-/* See concurrency notes in stdlib/exit.h where this lock is declared.  */
-__libc_lock_define_initialized (, __exit_funcs_lock)
-
-
-int
-attribute_hidden
-__internal_atexit (void (*func) (void *), void *arg, void *d,
-		   struct exit_function_list **listp)
-{
-  struct exit_function *new;
-
-  /* As a QoI issue we detect NULL early with an assertion instead
-     of a SIGSEGV at program exit when the handler is run (bug 20544).  */
-  assert (func != NULL);
-
-  __libc_lock_lock (__exit_funcs_lock);
-  new = __new_exitfn (listp);
-
-  if (new == NULL)
-    {
-      __libc_lock_unlock (__exit_funcs_lock);
-      return -1;
-    }
-
-#ifdef PTR_MANGLE
-  PTR_MANGLE (func);
-#endif
-  new->func.cxa.fn = (void (*) (void *, int)) func;
-  new->func.cxa.arg = arg;
-  new->func.cxa.dso_handle = d;
-  new->flavor = ef_cxa;
-  __libc_lock_unlock (__exit_funcs_lock);
-  return 0;
-}
-
+static struct exit_function_list initial;
+struct exit_function_list *__exit_funcs = &initial;
 
 /* Register a function to be called by exit or when a shared library
    is unloaded.  This function is only called from code generated by
@@ -67,77 +32,12 @@ __internal_atexit (void (*func) (void *), void *arg, void *d,
 int
 __cxa_atexit (void (*func) (void *), void *arg, void *d)
 {
-  return __internal_atexit (func, arg, d, &__exit_funcs);
+  return __new_exitfn (&__exit_funcs, ef_cxa, func, arg, d);
 }
 libc_hidden_def (__cxa_atexit)
 
-
-static struct exit_function_list initial;
-struct exit_function_list *__exit_funcs = &initial;
-uint64_t __new_exitfn_called;
-
-/* Must be called with __exit_funcs_lock held.  */
-struct exit_function *
-__new_exitfn (struct exit_function_list **listp)
+int
+__atexit (void (*func) (void), void *d)
 {
-  struct exit_function_list *p = NULL;
-  struct exit_function_list *l;
-  struct exit_function *r = NULL;
-  size_t i = 0;
-
-  if (__exit_funcs_done)
-    /* Exit code is finished processing all registered exit functions,
-       therefore we fail this registration.  */
-    return NULL;
-
-  for (l = *listp; l != NULL; p = l, l = l->next)
-    {
-      for (i = l->idx; i > 0; --i)
-	if (l->fns[i - 1].flavor != ef_free)
-	  break;
-
-      if (i > 0)
-	break;
-
-      /* This block is completely unused.  */
-      l->idx = 0;
-    }
-
-  if (l == NULL || i == sizeof (l->fns) / sizeof (l->fns[0]))
-    {
-      /* The last entry in a block is used.  Use the first entry in
-	 the previous block if it exists.  Otherwise create a new one.  */
-      if (p == NULL)
-	{
-	  assert (l != NULL);
-	  p = (struct exit_function_list *)
-	    calloc (1, sizeof (struct exit_function_list));
-	  if (p != NULL)
-	    {
-	      p->next = *listp;
-	      *listp = p;
-	    }
-	}
-
-      if (p != NULL)
-	{
-	  r = &p->fns[0];
-	  p->idx = 1;
-	}
-    }
-  else
-    {
-      /* There is more room in the block.  */
-      r = &l->fns[i];
-      l->idx = i + 1;
-    }
-
-  /* Mark entry as used, but we don't know the flavor now.  */
-  if (r != NULL)
-    {
-      r->flavor = ef_us;
-      ++__new_exitfn_called;
-    }
-
-  return r;
+  return __new_exitfn (&__exit_funcs, ef_at, func, NULL, d);
 }
diff --git a/stdlib/cxa_finalize.c b/stdlib/cxa_finalize.c
index e31b234..670d1a8 100644
--- a/stdlib/cxa_finalize.c
+++ b/stdlib/cxa_finalize.c
@@ -22,6 +22,77 @@
 #include <sysdep.h>
 #include <stdint.h>
 
+static bool
+handle_ef (struct exit_function *f)
+{
+  /* We don't want to run this cleanup more than once.  The Itanium
+     C++ ABI requires that multiple calls to __cxa_finalize not
+     result in calling termination functions more than once.  One
+     potential scenario where that could happen is with a concurrent
+     dlclose and exit, where the running dlclose must at some point
+     release the list lock, an exiting thread may acquire it, and
+     without setting flavor to ef_free, might re-run this destructor
+     which could result in undefined behaviour.  Therefore we must
+     set flavor to ef_free to avoid calling this destructor again.
+     Note that the concurrent exit must also take the dynamic loader
+     lock (for library finalizer processing) and therefore will
+     block while dlclose completes the processing of any in-progress
+     exit functions. Lastly, once we release the list lock for the
+     entry marked ef_free, we must not read from that entry again
+     since it may have been reused by the time we take the list lock
+     again.  Lastly the detection of new registered exit functions is
+     based on a monotonically incrementing counter, and there is an
+     ABA if between the unlock to run the exit function and the
+     re-lock after completion the user registers 2^64 exit functions,
+     the implementation will not detect this and continue without
+     executing any more functions.
+
+     One minor issue remains: A registered exit function that is in
+     progress by a call to dlclose() may not completely finish before
+     the next registered exit function is run. This may, according to
+     some readings of POSIX violate the requirement that functions
+     run in effective LIFO order.  This should probably be fixed in a
+     future implementation to ensure the functions do not run in
+     parallel.  */
+  const uint64_t check = __new_exitfn_called;
+
+  __libc_lock_unlock (__exit_funcs_lock);
+  switch (f->flavor)
+    {
+      void (*atfct) (void);
+      void (*cxafct) (void *arg, int status);
+
+      case ef_free:
+      case ef_on:
+        break;
+
+      case ef_at:
+        f->flavor = ef_free;
+        atfct = f->func.at.fn;
+#ifdef PTR_DEMANGLE
+        PTR_DEMANGLE (atfct);
+#endif
+	atfct ();
+	break;
+
+      case ef_cxa:
+	f->flavor = ef_free;
+	cxafct = f->func.cxa.fn;
+#ifdef PTR_DEMANGLE
+	PTR_DEMANGLE (cxafct);
+#endif
+	cxafct (f->func.cxa.arg, 0);
+	break;
+    }
+  __libc_lock_lock (__exit_funcs_lock);
+
+  /* It is possible that that last exit function registered
+     more exit functions.  Start the loop over.  */
+  if (__glibc_unlikely (check != __new_exitfn_called))
+    return true;
+  return false;
+}
+
 /* If D is non-NULL, call all functions registered with `__cxa_atexit'
    with the same dso handle.  Otherwise, if D is NULL, call all of the
    registered handlers.  */
@@ -35,59 +106,13 @@ __cxa_finalize (void *d)
  restart:
   for (funcs = __exit_funcs; funcs; funcs = funcs->next)
     {
-      struct exit_function *f;
-
-      for (f = &funcs->fns[funcs->idx - 1]; f >= &funcs->fns[0]; --f)
-	if ((d == NULL || d == f->func.cxa.dso_handle) && f->flavor == ef_cxa)
-	  {
-	    const uint64_t check = __new_exitfn_called;
-	    void (*cxafn) (void *arg, int status) = f->func.cxa.fn;
-	    void *cxaarg = f->func.cxa.arg;
-
-	    /* We don't want to run this cleanup more than once.  The Itanium
-	       C++ ABI requires that multiple calls to __cxa_finalize not
-	       result in calling termination functions more than once.  One
-	       potential scenario where that could happen is with a concurrent
-	       dlclose and exit, where the running dlclose must at some point
-	       release the list lock, an exiting thread may acquire it, and
-	       without setting flavor to ef_free, might re-run this destructor
-	       which could result in undefined behaviour.  Therefore we must
-	       set flavor to ef_free to avoid calling this destructor again.
-	       Note that the concurrent exit must also take the dynamic loader
-	       lock (for library finalizer processing) and therefore will
-	       block while dlclose completes the processing of any in-progress
-	       exit functions. Lastly, once we release the list lock for the
-	       entry marked ef_free, we must not read from that entry again
-	       since it may have been reused by the time we take the list lock
-	       again.  Lastly the detection of new registered exit functions is
-	       based on a monotonically incrementing counter, and there is an
-	       ABA if between the unlock to run the exit function and the
-	       re-lock after completion the user registers 2^64 exit functions,
-	       the implementation will not detect this and continue without
-	       executing any more functions.
-
-	       One minor issue remains: A registered exit function that is in
-	       progress by a call to dlclose() may not completely finish before
-	       the next registered exit function is run. This may, according to
-	       some readings of POSIX violate the requirement that functions
-	       run in effective LIFO order.  This should probably be fixed in a
-	       future implementation to ensure the functions do not run in
-	       parallel.  */
-	    f->flavor = ef_free;
-
-#ifdef PTR_DEMANGLE
-	    PTR_DEMANGLE (cxafn);
-#endif
-	    /* Unlock the list while we call a foreign function.  */
-	    __libc_lock_unlock (__exit_funcs_lock);
-	    cxafn (cxaarg, 0);
-	    __libc_lock_lock (__exit_funcs_lock);
-
-	    /* It is possible that that last exit function registered
-	       more exit functions.  Start the loop over.  */
-	    if (__glibc_unlikely (check != __new_exitfn_called))
+      for (struct exit_function *f = &funcs->fns[funcs->idx - 1];
+	   f >= &funcs->fns[0]; --f)
+	{
+	  if ((d == NULL || d == f->dso_handle))
+	    if (handle_ef (f))
 	      goto restart;
-	  }
+	}
     }
 
   /* Also remove the quick_exit handlers, but do not call them.  */
@@ -96,7 +121,7 @@ __cxa_finalize (void *d)
       struct exit_function *f;
 
       for (f = &funcs->fns[funcs->idx - 1]; f >= &funcs->fns[0]; --f)
-	if (d == NULL || d == f->func.cxa.dso_handle)
+	if (d == NULL || d == f->dso_handle)
 	  f->flavor = ef_free;
     }
 
diff --git a/stdlib/exit.c b/stdlib/exit.c
index 49d12d9..b194c15 100644
--- a/stdlib/exit.c
+++ b/stdlib/exit.c
@@ -25,10 +25,6 @@
 #include "set-hooks.h"
 DEFINE_HOOK (__libc_atexit, (void))
 
-/* Initialize the flag that indicates exit function processing
-   is complete. See concurrency notes in stdlib/exit.h where
-   __exit_funcs_lock is declared.  */
-bool __exit_funcs_done = false;
 
 /* Call all functions registered with `atexit' and `on_exit',
    in the reverse of the order in which they were registered
@@ -81,9 +77,9 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
 	      void (*cxafct) (void *arg, int status);
 
 	    case ef_free:
-	    case ef_us:
 	      break;
 	    case ef_on:
+	      f->flavor = ef_free;
 	      onfct = f->func.on.fn;
 #ifdef PTR_DEMANGLE
 	      PTR_DEMANGLE (onfct);
@@ -91,7 +87,8 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
 	      onfct (status, f->func.on.arg);
 	      break;
 	    case ef_at:
-	      atfct = f->func.at;
+	      f->flavor = ef_free;
+	      atfct = f->func.at.fn;
 #ifdef PTR_DEMANGLE
 	      PTR_DEMANGLE (atfct);
 #endif
diff --git a/stdlib/exit.h b/stdlib/exit.h
index 6fab49c..aac3f4f 100644
--- a/stdlib/exit.h
+++ b/stdlib/exit.h
@@ -20,12 +20,12 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <assert.h>
 #include <libc-lock.h>
 
-enum
+enum ef_flavor
 {
-  ef_free,	/* `ef_free' MUST be zero!  */
-  ef_us,
+  ef_free = 0,	/* 'ef_free' MUST be zero!  */
   ef_on,
   ef_at,
   ef_cxa
@@ -35,10 +35,13 @@ struct exit_function
   {
     /* `flavour' should be of type of the `enum' above but since we need
        this element in an atomic operation we have to use `long int'.  */
-    long int flavor;
+    enum ef_flavor flavor;
     union
       {
-	void (*at) (void);
+	struct
+	  {
+	    void (*fn) (void);
+	  } at;
 	struct
 	  {
 	    void (*fn) (int status, void *arg);
@@ -48,12 +51,21 @@ struct exit_function
 	  {
 	    void (*fn) (void *arg, int status);
 	    void *arg;
-	    void *dso_handle;
 	  } cxa;
       } func;
+    void *dso_handle;
   };
+
+enum el_type
+{
+  el_none = 0,
+  el_at,
+  el_cxa
+};
+
 struct exit_function_list
   {
+    enum el_type type;
     struct exit_function_list *next;
     size_t idx;
     struct exit_function fns[32];
@@ -76,18 +88,15 @@ extern bool __exit_funcs_done attribute_hidden;
    lock.  */
 __libc_lock_define (extern, __exit_funcs_lock);
 
-
-extern struct exit_function *__new_exitfn (struct exit_function_list **listp)
-  attribute_hidden;
+extern int __new_exitfn (struct exit_function_list **listp,
+			 enum ef_flavor flavor, void *func, void *arg,
+			 void *dso_handle) attribute_hidden;
 
 extern void __run_exit_handlers (int status,
 				 struct exit_function_list **listp,
 				 bool run_list_atexit, bool run_dtors)
   attribute_hidden __attribute__ ((__noreturn__));
 
-extern int __internal_atexit (void (*func) (void *), void *arg, void *d,
-			      struct exit_function_list **listp)
-  attribute_hidden;
 extern int __cxa_at_quick_exit (void (*func) (void *), void *d);
 
 
diff --git a/stdlib/exitfn.c b/stdlib/exitfn.c
new file mode 100644
index 0000000..fec080b
--- /dev/null
+++ b/stdlib/exitfn.c
@@ -0,0 +1,196 @@
+/* Internal function to manipulate exit handler lists.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <assert.h>
+#include <exit.h>
+#include <array_length.h>
+
+/* Declared at stdlib/exit.h.  */
+__libc_lock_define_initialized (, __exit_funcs_lock)
+/* Updated each time a new entry is added on any list.  */
+uint64_t __new_exitfn_called;
+/* Set to indicate exit function processing is complete.  */
+bool __exit_funcs_done = false;
+
+/* Allocate a new entry of flavor FLAVOR on list LISTP.  The list must be kept
+   in order with following constraints:
+
+   1. el_at nodes should be prior el_cxa.
+   2. el_at should contain only ef_at, ef_on, or ef_free elements.
+   3. el_cxa should contain only ef_cxa or ef_free elements.
+   4. new insertions on each node type should be be kept in lifo order.
+   5. the original first element should be last one (since it is static allocated
+      and 'exit' will deallocated the nodes in order.  */
+static struct exit_function *
+__new_exitfn_entry (struct exit_function_list **listp, enum ef_flavor flavor)
+{
+  enum el_type type = flavor == ef_cxa ? el_cxa : el_at;
+
+  struct exit_function_list *pp = NULL;
+  struct exit_function_list *p = NULL;
+  struct exit_function_list *l;
+  struct exit_function_list *n;
+  struct exit_function *r = NULL;
+  size_t i = 0;
+
+  if (__exit_funcs_done)
+    /* Exit code is finished processing all registered exit functions,
+       therefore we fail this registration.  */
+    return NULL;
+
+  /* We keep track of currentl 'l', previous 'p', and before previous 'pp'
+     nodes.  */
+  for (l = *listp; l != NULL;
+       pp = (p != NULL && l != NULL) ? p : pp, p = l, l = l->next)
+    {
+      if (l->type != el_none && l->type != type)
+	continue;
+
+      for (i = l->idx; i > 0; --i)
+	if (l->fns[i - 1].flavor != ef_free)
+	  break;
+
+      if (i > 0)
+	break;
+
+      /* This block is completely unused.  */
+      l->idx = 0;
+      l->type = type;
+    }
+
+  if (l == NULL || i == array_length (l->fns))
+    {
+      /* The last entry in a block is used.  Use the first entry in the
+	 previous block if it exists.  Otherwise create a new one.  */
+      if (p == NULL || p->type != type)
+        {
+          n = calloc (1, sizeof (struct exit_function_list));
+	  if (n == NULL)
+	    return NULL;
+
+	  /* The el_cxa nodes should be after el_at one in the list.  */
+	  if (type == el_cxa)
+	    {
+	      /* It is the first element in the list, set the sentinel
+		 pointer.  */
+	      if (p == NULL)
+		{
+		  n->next = *listp;
+		  *listp = n;
+		  p = n;
+		}
+	      /* There is already an set of current type, add the new one
+		 at the front.  */
+	      else if (l && l->type == type)
+		{
+		  p->next = n;
+		  n->next = l;
+		  p = n;
+		}
+	      /* There is already an set, but with different type, and it is
+		 the sentinel set.  We need to keep as the last one, since it
+		 is static allocated and 'exit' deallocate all elements
+		 except the last one.  */
+	      else
+		{
+		  /* It copies the contents of the old element over the newly
+		     allocated one, and uses it the new set.  */
+		  *n = *p;
+		  /* Clear the initial element or its prior contents.  */
+		  memset (p, 0, sizeof (struct exit_function_list));
+
+		  n->next = p;
+
+		  /* Sets the sentinel points or update the list.  */
+		  if (pp != NULL)
+		    pp->next = n;
+		  else
+		    *listp = n;
+		}
+	    }
+	  /* el_at blocks are always on before el_cxa, so just adds it on the
+	     list.  */
+	  else
+	    {
+              n->next = *listp;
+              *listp = n;
+	      p = n;
+	    }
+        }
+
+      r = &p->fns[0];
+      p->idx = 1;
+      p->type = type;
+    }
+  else
+    {
+      /* There is more room in the block.  */
+      r = &l->fns[i];
+      l->idx = i + 1;
+    }
+
+  ++__new_exitfn_called;
+
+  return r;
+}
+
+int __new_exitfn (struct exit_function_list **listp, enum ef_flavor flavor,
+		  void *func, void *arg, void *dso_handle)
+{
+  struct exit_function *new;
+
+  /* As a QoI issue we detect NULL early with an assertion instead of a
+     SIGSEGV at program exit when the handler is run (bug 20544).  */
+  assert (func != NULL);
+
+  __libc_lock_lock (__exit_funcs_lock);
+
+  new = __new_exitfn_entry (listp, flavor);
+  if (new == NULL)
+    {
+      __libc_lock_unlock (__exit_funcs_lock);
+      return -1;
+    }
+
+#ifdef PTR_MANGLE
+  PTR_MANGLE (func);
+#endif
+
+  new->flavor = flavor;
+  switch (flavor)
+    {
+      case ef_on:
+	new->func.on.fn = func;
+	new->func.on.arg = arg;
+      break;
+      case ef_at:
+	new->func.at.fn = func;
+      break;
+      case ef_cxa:
+	new->func.cxa.fn = func;
+	new->func.cxa.arg = arg;
+      break;
+      default:
+	assert (!"invalid flavor");
+    }
+  new->dso_handle = dso_handle;
+
+  __libc_lock_unlock (__exit_funcs_lock);
+  return 0;
+}
diff --git a/stdlib/old_atexit.c b/stdlib/old_atexit.c
index 44bbd69..cb70114 100644
--- a/stdlib/old_atexit.c
+++ b/stdlib/old_atexit.c
@@ -1,8 +1,12 @@
+#include <exit.h>
+#include <dso_handle.h>
 #include <shlib-compat.h>
 
 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2_2)
-# define atexit attribute_compat_text_section __dyn_atexit
-# include "atexit.c"
-# undef atexit
+int attribute_compat_text_section
+__dyn_atexit (void (*func) (void))
+{
+  return __cxa_atexit ((void (*) (void *)) func, NULL, __dso_handle);
+}
 compat_symbol (libc, __dyn_atexit, atexit, GLIBC_2_0);
 #endif
diff --git a/stdlib/on_exit.c b/stdlib/on_exit.c
index 59e249f..81f9b5a 100644
--- a/stdlib/on_exit.c
+++ b/stdlib/on_exit.c
@@ -24,28 +24,6 @@
 int
 __on_exit (void (*func) (int status, void *arg), void *arg)
 {
-  struct exit_function *new;
-
-  /* As a QoI issue we detect NULL early with an assertion instead
-     of a SIGSEGV at program exit when the handler is run (bug 20544).  */
-  assert (func != NULL);
-
-   __libc_lock_lock (__exit_funcs_lock);
-  new = __new_exitfn (&__exit_funcs);
-
-  if (new == NULL)
-    {
-      __libc_lock_unlock (__exit_funcs_lock);
-      return -1;
-    }
-
-#ifdef PTR_MANGLE
-  PTR_MANGLE (func);
-#endif
-  new->func.on.fn = func;
-  new->func.on.arg = arg;
-  new->flavor = ef_on;
-  __libc_lock_unlock (__exit_funcs_lock);
-  return 0;
+  return __new_exitfn (&__exit_funcs, ef_on, func, arg, NULL);
 }
 weak_alias (__on_exit, on_exit)
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index edeaf8e..2aab737 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2045,6 +2045,7 @@ GLIBC_2.28 thrd_current F
 GLIBC_2.28 thrd_equal F
 GLIBC_2.28 thrd_sleep F
 GLIBC_2.28 thrd_yield F
+GLIBC_2.29 __atexit F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 59f85d9..699fd10 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1895,6 +1895,7 @@ GLIBC_2.28 thrd_current F
 GLIBC_2.28 thrd_equal F
 GLIBC_2.28 thrd_sleep F
 GLIBC_2.28 thrd_yield F
+GLIBC_2.29 __atexit F
 GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]