Patchwork __cxa_atexit support for AIX

login
register
mail settings
Submitter David Edelsohn
Date Jan. 29, 2013, 7:03 p.m.
Message ID <CAGWvnynKgwGz7RAVcpkScJUAmJ3PMy5V=6g=nqHNecyqtfAmjg@mail.gmail.com>
Download mbox | patch
Permalink /patch/216629/
State New
Headers show

Comments

David Edelsohn - Jan. 29, 2013, 7:03 p.m.
Inspired by Perry's patch to add __cxa_atexit support, I have
implemented a similar patch.  This version adds __cxa_atexit and
__cxa_finalize support to libgcc on AIX and makes no changes to
collect2 processing.  Because AIX does not use crtstuff.c for
init/fini processing, I used an extremely low-priority destructor to
invoke __cxa_finalize very early in destructor processing.  I would
appreciate any feedback about that approach.

Also, most of the code liberally copies from GNU Libc cxa_atexit.c and
cxa_finalize.c.  I have submitted a request to Richard Stallman to
allow me to copy the code into GCC, modify it, and distribute it using
the GCC Runtime Exception license for use with GCC on AIX.  I will
update copyrights once the mechanism is decided.

I inserted the definition of __dso_handle and the call to
__cxa_finalize in cxa_atexit.c.  I could merge and rearrange the files
further, but I want to limit the amount of changes to the original GNU
Libc code. I have no expectation of sharing the code with GNU Libc,
but want to simplify the detection of any changes that need to be
imported.

Thanks, David

Patch

Index: libgcc/config.host
===================================================================
--- libgcc/config.host	(revision 195542)
+++ libgcc/config.host	(working copy)
@@ -899,7 +899,7 @@ 
 	;;
 rs6000-ibm-aix[56789].* | powerpc-ibm-aix[56789].*)
 	md_unwind_header=rs6000/aix-unwind.h
-	tmake_file="t-fdpbit rs6000/t-ppc64-fp rs6000/t-slibgcc-aix
rs6000/t-ibm-ldouble"
+	tmake_file="t-fdpbit rs6000/t-ppc64-fp rs6000/t-slibgcc-aix
rs6000/t-ibm-ldouble rs6000/t-aix-cxa"
 	;;
 rl78-*-elf)
 	tmake_file="$tm_file t-fdpbit rl78/t-rl78"
Index: libgcc/config/rs6000/exit.h
===================================================================
--- libgcc/config/rs6000/exit.h	(revision 0)
+++ libgcc/config/rs6000/exit.h	(revision 0)
@@ -0,0 +1,85 @@ 
+/* Copyright (C) 1991,1996,1997,1999,2001,2002,2006,2009
+   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/>.  */
+
+#ifndef	_EXIT_H
+#define _EXIT_H 1
+
+#define attribute_hidden
+#define INTDEF(name)
+
+#include <stdbool.h>
+#include <stdint.h>
+
+enum
+{
+  ef_free,	/* `ef_free' MUST be zero!  */
+  ef_us,
+  ef_on,
+  ef_at,
+  ef_cxa
+};
+
+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;
+    union
+      {
+	void (*at) (void);
+	struct
+	  {
+	    void (*fn) (int status, void *arg);
+	    void *arg;
+	  } on;
+	struct
+	  {
+	    void (*fn) (void *arg, int status);
+	    void *arg;
+	    void *dso_handle;
+	  } cxa;
+      } func;
+  };
+struct exit_function_list
+  {
+    struct exit_function_list *next;
+    size_t idx;
+    struct exit_function fns[32];
+  };
+extern struct exit_function_list *__exit_funcs attribute_hidden;
+extern struct exit_function_list *__quick_exit_funcs attribute_hidden;
+
+extern struct exit_function *__new_exitfn (struct exit_function_list **listp);
+extern uint64_t __new_exitfn_called attribute_hidden;
+
+extern void __run_exit_handlers (int status, struct exit_function_list **listp,
+				 bool run_list_atexit)
+  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);
+
+extern int __cxa_atexit (void (*func) (void *), void *arg, void *d);
+extern int __cxa_atexit_internal (void (*func) (void *), void *arg, void *d)
+     attribute_hidden;
+
+extern void __cxa_finalize (void *d);
+
+#endif	/* exit.h  */
Index: libgcc/config/rs6000/cxa_atexit.c
===================================================================
--- libgcc/config/rs6000/cxa_atexit.c	(revision 0)
+++ libgcc/config/rs6000/cxa_atexit.c	(revision 0)
@@ -0,0 +1,137 @@ 
+/* Copyright (C) 1999,2001,2002,2005,2006,2009 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 <assert.h>
+#include <stdlib.h>
+
+#include "exit.h"
+
+#undef __cxa_atexit
+
+#ifdef SHARED
+void *__dso_handle = &__dso_handle;
+#else
+void *__dso_handle = 0;
+#endif
+
+/* Add __cxa_finalize call to beginning of destructors list.  */
+void __init_aix_libgcc_cxa_atexit (void) __attribute__ ((destructor (65535)));
+
+void
+__init_aix_libgcc_cxa_atexit (void)
+{
+  __cxa_finalize (__dso_handle);
+}
+
+#define atomic_write_barrier() __asm__ ("eieio" ::: "memory")
+
+int
+attribute_hidden
+__internal_atexit (void (*func) (void *), void *arg, void *d,
+		   struct exit_function_list **listp)
+{
+  struct exit_function *new = __new_exitfn (listp);
+
+  if (new == NULL)
+    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;
+  atomic_write_barrier ();
+  new->flavor = ef_cxa;
+  return 0;
+}
+
+
+/* Register a function to be called by exit or when a shared library
+   is unloaded.  This function is only called from code generated by
+   the C++ compiler.  */
+int
+__cxa_atexit (void (*func) (void *), void *arg, void *d)
+{
+  return __internal_atexit (func, arg, d, &__exit_funcs);
+}
+INTDEF(__cxa_atexit)
+
+
+static struct exit_function_list initial;
+struct exit_function_list *__exit_funcs = &initial;
+uint64_t __new_exitfn_called;
+
+struct exit_function *
+__new_exitfn (struct exit_function_list **listp)
+{
+  struct exit_function_list *p = NULL;
+  struct exit_function_list *l;
+  struct exit_function *r = NULL;
+  size_t i = 0;
+
+  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;
+}
Index: libgcc/config/rs6000/cxa_finalize.c
===================================================================
--- libgcc/config/rs6000/cxa_finalize.c	(revision 0)
+++ libgcc/config/rs6000/cxa_finalize.c	(revision 0)
@@ -0,0 +1,87 @@ 
+/* Copyright (C) 1999,2001-2003,2005,2006,2009 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 <assert.h>
+#include <stdlib.h>
+#include "exit.h"
+
+static boolean_t
+catomic_compare_and_exchange_bool_acq (long *mem, long newval, long oldval)
+{
+  return __atomic_compare_exchange (mem, &oldval, &newval, 0,
+				    __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
+}
+
+/* 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.  */
+void
+__cxa_finalize (void *d)
+{
+  struct exit_function_list *funcs;
+
+ 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)
+	{
+	  void (*cxafn) (void *arg, int status);
+	  void *cxaarg;
+
+	  if ((d == NULL || d == f->func.cxa.dso_handle)
+	      /* We don't want to run this cleanup more than once.  */
+	      && (cxafn = f->func.cxa.fn,
+		  cxaarg = f->func.cxa.arg,
+		  ! catomic_compare_and_exchange_bool_acq (&f->flavor, ef_free,
+							   ef_cxa)))
+	    {
+	      uint64_t check = __new_exitfn_called;
+
+#ifdef PTR_DEMANGLE
+	      PTR_DEMANGLE (cxafn);
+#endif
+	      cxafn (cxaarg, 0);
+
+	      /* It is possible that that last exit function registered
+		 more exit functions.  Start the loop over.  */
+	      if (__builtin_expect (check != __new_exitfn_called, 0))
+		goto restart;
+	    }
+	}
+    }
+
+#if 0
+  /* Also remove the quick_exit handlers, but do not call them.  */
+  for (funcs = __quick_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_free;
+    }
+#endif
+
+  /* Remove the registered fork handlers.  We do not have to
+     unregister anything if the program is going to terminate anyway.  */
+#ifdef UNREGISTER_ATFORK
+  if (d != NULL)
+    UNREGISTER_ATFORK (d);
+#endif
+}