diff mbox

[collect2] AIX depth-first shared object initializers

Message ID CAGWvnymwUcCmKq7=x+PnLDEE3nTjXMypLGVqfJ2-XM1qBs5xnw@mail.gmail.com
State New
Headers show

Commit Message

David Edelsohn Nov. 21, 2013, 12:14 a.m. UTC
The System V ABI specifies that the initialization functions are
called in depth-first dependence order. The AIX runtime loader does
not impose that ordering and generally initializes shared objects in
breadth-first order.  Yeah.  Moving on.

The attached patch enhances collect2 to initialize shared libraries in
depth-first dependency order.  The patch accomplishes this by adding
new special global init and fini symbols for AIX named GLOBAL__AIXI
and GLOBAL__AIXD.  The historical GLOBAL__FI and GLOBAL_FD symbols
remain and are initialized as usual for backward compatibility and
interoperability.

If the GLOBAL__AIXI and GLOBAL__AIXD symbols are found in a dependent
shared object while linking a shared object, those constructors are
added to the initializer list of the shared object being processed and
assigned highest priority. When an executable and shared objects with
this functionality are run and its initializers are invoked, the
initializer first will call the initializers of dependent shared
objects, which will call initializers of its dependent objects, etc.
The normal guard against multiple invocations prevents repeated
invocation from dependent invocations and from the normal AIX shared
object initializer mechanism.

The initializers of a shared object created without the new
functionality will be invoked with the normal mechanism and the
default AIX order.  And the backward compatible GCC shared object
symbols are provided if a shared object created with the new
functionality is linked by an older version of collect2 -- the
dependence ordering is not utilized and the previous, AIX loader-based
initialization mechanism is utilized.

Executables and shared objects built and linked with dependencies on
shared objects providing the new symbols will not function with an
older shared objects in which those symbols are missing. In other
words, if a user mixes versions of libraries, things will break, as
one would expect.

Andrew Dixie and I have been building and using GCC with this feature
for over six months.  Are the collect2 changes okay?

Thanks, David


2013-11-20  David Edelsohn  <dje.gcc@gmail.com>
                 Andrew Dixie  <andrewd@gentrack.com>

libgcc:

        * config/rs6000/aixinitfini.c: New file.
        * config/rs6000/t-aix-cxa (LIB2ADD_ST): Add aixinitfini.c.
        * config/rs6000/libgcc-aix-cxa.ver (GCC_4.9): Add libgcc initfini
        symbols.

gcc:

        * config/rs6000/aix.h (COLLECT_SHARED_INIT_FUNC): Define.
        (COLLECT_SHARED_FINI_FUNC): Define.

        * collect2.c (aix_shared_initname): Declare.
        (aix_shared_fininame): Declare.
        (symkind): Add SYM_AIXI and SYM_AIXD.
        (scanfilter_masks): Add SCAN_AIXI and SCAN_AIXD.
        (struct names special): Add GLOBAL__AIXI_ and GLOBAL__AIXD_.
        (aixlazy_flag): Parse.
        (extract_init_priority): SYM_AIXI and SYM_AIXD have highest priority.
        (scan_prog_file, COFF): Handle SYM_AIXI and SYM_AIXD.
libgcc:

	* config/rs6000/aixinitfini.c: New file.
	* config/rs6000/t-aix-cxa (LIB2ADD_ST): Add aixinitfini.c.
	* config/rs6000/libgcc-aix-cxa.ver (GCC_4.9): Add libgcc initfini
	symbols.

gcc:

	* config/rs6000/aix.h (COLLECT_SHARED_INIT_FUNC): Define.
	(COLLECT_SHARED_FINI_FUNC): Define.

	* collect2.c (aix_shared_initname): Declare.
	(aix_shared_fininame): Declare.
	(symkind): Add SYM_AIXI and SYM_AIXD.
	(scanfilter_masks): Add SCAN_AIXI and SCAN_AIXD.
	(struct names special): Add GLOBAL__AIXI_ and GLOBAL__AIXD_.
	(aixlazy_flag): Parse.
	(extract_init_priority): SYM_AIXI and SYM_AIXD have highest priority.
	(scan_prog_file, COFF): Handle SYM_AIXI and SYM_AIXD.

Comments

Ian Lance Taylor Nov. 21, 2013, 1:23 a.m. UTC | #1
On Wed, Nov 20, 2013 at 4:14 PM, David Edelsohn <dje.gcc@gmail.com> wrote:
> Andrew Dixie and I have been building and using GCC with this feature
> for over six months.  Are the collect2 changes okay?
>
> Thanks, David
>
>
> 2013-11-20  David Edelsohn  <dje.gcc@gmail.com>
>                  Andrew Dixie  <andrewd@gentrack.com>
>
> libgcc:
>
>         * config/rs6000/aixinitfini.c: New file.
>         * config/rs6000/t-aix-cxa (LIB2ADD_ST): Add aixinitfini.c.
>         * config/rs6000/libgcc-aix-cxa.ver (GCC_4.9): Add libgcc initfini
>         symbols.
>
> gcc:
>
>         * config/rs6000/aix.h (COLLECT_SHARED_INIT_FUNC): Define.
>         (COLLECT_SHARED_FINI_FUNC): Define.
>
>         * collect2.c (aix_shared_initname): Declare.
>         (aix_shared_fininame): Declare.
>         (symkind): Add SYM_AIXI and SYM_AIXD.
>         (scanfilter_masks): Add SCAN_AIXI and SCAN_AIXD.
>         (struct names special): Add GLOBAL__AIXI_ and GLOBAL__AIXD_.
>         (aixlazy_flag): Parse.
>         (extract_init_priority): SYM_AIXI and SYM_AIXD have highest priority.
>         (scan_prog_file, COFF): Handle SYM_AIXI and SYM_AIXD.


The collect2.c changes are fine.

Thanks.

Ian
diff mbox

Patch

Index: libgcc/config/rs6000/aixinitfini.c
===================================================================
--- libgcc/config/rs6000/aixinitfini.c	(revision 0)
+++ libgcc/config/rs6000/aixinitfini.c	(revision 0)
@@ -0,0 +1,33 @@ 
+/* FIXME: rename this file */
+
+/*
+   Artificially create _GLOBAL_AIX[ID]_shr_o symbols in libgcc.a.
+
+   This means that libstdc++.a can invoke these symbols and they are resolved
+   regardless of whether libstdc++.a is linked against libgcc_s.a or libgcc.a.
+
+   The symbols are created in libgcc_s.a by collect2 as there are exception
+   frames to register for LIB2_DIVMOD_FUNCS.
+
+   The symbols are NOT created by collect2 for libgcc.a, because libgcc.a is
+   a 'real' archive containing objects and collect2 is not invoked.
+
+   libstdc++.a is linked against libgcc.a when handling the command line
+   options '-static-libgcc -static-libstdc++'.
+*/
+
+void _GLOBAL__AIXI_shr_o (void);
+void _GLOBAL__AIXD_shr_o (void);
+
+void
+_GLOBAL__AIXI_shr_o (void)
+{
+  return;
+}
+
+void
+_GLOBAL__AIXD_shr_o (void)
+{
+  return;
+}
+
Index: libgcc/config/rs6000/t-aix-cxa
===================================================================
--- libgcc/config/rs6000/t-aix-cxa	(revision 205145)
+++ libgcc/config/rs6000/t-aix-cxa	(working copy)
@@ -1,6 +1,8 @@ 
 LIB2ADDEH += $(srcdir)/config/rs6000/cxa_atexit.c \
 	$(srcdir)/config/rs6000/cxa_finalize.c
 
+LIB2ADD_ST += $(srcdir)/config/rs6000/aixinitfini.c
+
 SHLIB_MAPFILES += $(srcdir)/config/rs6000/libgcc-aix-cxa.ver
 
 crtcxa.o: $(srcdir)/config/rs6000/crtcxa.c
Index: libgcc/config/rs6000/libgcc-aix-cxa.ver
===================================================================
--- libgcc/config/rs6000/libgcc-aix-cxa.ver	(revision 205145)
+++ libgcc/config/rs6000/libgcc-aix-cxa.ver	(working copy)
@@ -2,3 +2,8 @@ 
   __cxa_atexit
   __cxa_finalize
 }
+
+GCC_4.9 {
+  _GLOBAL__AIXI_shr_o
+  _GLOBAL__AIXD_shr_o
+}
Index: gcc/config/rs6000/aix.h
===================================================================
--- gcc/config/rs6000/aix.h	(revision 205145)
+++ gcc/config/rs6000/aix.h	(working copy)
@@ -47,6 +47,35 @@ 
    collect has a chance to see them, so scan the object files directly.  */
 #define COLLECT_EXPORT_LIST
 
+/* On AIX, initialisers specified with -binitfini are called in breadth-first
+   order.
+   e.g. if a.out depends on lib1.so, the init function for a.out is called before
+   the init function for lib1.so.
+
+   To ensure global C++ constructors in linked libraries are run before global
+   C++ constructors from the current module, there is additional symbol scanning
+   logic in collect2.
+
+   The global initialiser/finaliser functions are named __GLOBAL_AIXI_{libname}
+   and __GLOBAL_AIXD_{libname} and are exported from each shared library.
+
+   collect2 will detect these symbols when they exist in shared libraries that
+   the current program is being linked against.  All such initiliser functions
+   will be called prior to the constructors of the current program, and
+   finaliser functions called after destructors.
+
+   Reference counting generated by collect2 will ensure that constructors are
+   only invoked once in the case of multiple dependencies on a library.
+
+   -binitfini is still used in parallel to this solution.
+   This handles the case where a library is loaded through dlopen(), and also
+   handles the option -blazy.
+*/
+#define COLLECT_SHARED_INIT_FUNC(STREAM, FUNC) \
+	  fprintf ((STREAM), "void %s() {\n\t%s();\n}\n", aix_shared_initname, (FUNC))
+#define COLLECT_SHARED_FINI_FUNC(STREAM, FUNC) \
+	  fprintf ((STREAM), "void %s() {\n\t%s();\n}\n", aix_shared_fininame, (FUNC))
+
 #if HAVE_AS_REF
 /* Issue assembly directives that create a reference to the given DWARF table
    identifier label from the current function section.  This is defined to
Index: gcc/collect2.c
===================================================================
--- gcc/collect2.c	(revision 205145)
+++ gcc/collect2.c	(working copy)
@@ -182,6 +182,7 @@ 
 static int export_flag;                 /* true if -bE */
 static int aix64_flag;			/* true if -b64 */
 static int aixrtl_flag;			/* true if -brtl */
+static int aixlazy_flag;               /* true if -blazy */
 #endif
 
 enum lto_mode_d {
@@ -215,6 +216,13 @@ 
 const char *c_file_name;		/* pathname of gcc */
 static char *initname, *fininame;	/* names of init and fini funcs */
 
+
+#ifdef TARGET_AIX_VERSION
+static char *aix_shared_initname;
+static char *aix_shared_fininame;       /* init/fini names as per the scheme
+					   described in config/rs6000/aix.h */
+#endif
+
 static struct head constructors;	/* list of constructors found */
 static struct head destructors;		/* list of destructors found */
 #ifdef COLLECT_EXPORT_LIST
@@ -279,7 +287,9 @@ 
   SYM_DTOR = 2,  /* destructor  */
   SYM_INIT = 3,  /* shared object routine that calls all the ctors  */
   SYM_FINI = 4,  /* shared object routine that calls all the dtors  */
-  SYM_DWEH = 5   /* DWARF exception handling table  */
+  SYM_DWEH = 5,  /* DWARF exception handling table  */
+  SYM_AIXI = 6,
+  SYM_AIXD = 7
 } symkind;
 
 static symkind is_ctor_dtor (const char *);
@@ -340,6 +350,8 @@ 
   SCAN_INIT = 1 << SYM_INIT,
   SCAN_FINI = 1 << SYM_FINI,
   SCAN_DWEH = 1 << SYM_DWEH,
+  SCAN_AIXI = 1 << SYM_AIXI,
+  SCAN_AIXD = 1 << SYM_AIXD,
   SCAN_ALL  = ~0
 };
 
@@ -589,6 +601,10 @@ 
     { "GLOBAL__F_", sizeof ("GLOBAL__F_")-1, SYM_DWEH, 0 },
     { "GLOBAL__FI_", sizeof ("GLOBAL__FI_")-1, SYM_INIT, 0 },
     { "GLOBAL__FD_", sizeof ("GLOBAL__FD_")-1, SYM_FINI, 0 },
+#ifdef TARGET_AIX_VERSION
+    { "GLOBAL__AIXI_", sizeof ("GLOBAL__AIXI_")-1, SYM_AIXI, 0 },
+    { "GLOBAL__AIXD_", sizeof ("GLOBAL__AIXD_")-1, SYM_AIXD, 0 },
+#endif
     { NULL, 0, SYM_REGULAR, 0 }
   };
 
@@ -1034,6 +1050,8 @@ 
 	    aixrtl_flag = 1;
 	else if (strcmp (argv[i], "-bnortl") == 0)
 	    aixrtl_flag = 0;
+	else if (strcmp (argv[i], "-blazy") == 0)
+	    aixlazy_flag = 1;
 #endif
       }
     vflag = debug;
@@ -1728,6 +1746,9 @@ 
       if (! exports.first)
 	*ld2++ = concat ("-bE:", export_file, NULL);
 
+      add_to_list (&exports, aix_shared_initname);
+      add_to_list (&exports, aix_shared_fininame);
+
 #ifndef LD_INIT_SWITCH
       add_to_list (&exports, initname);
       add_to_list (&exports, fininame);
@@ -2020,6 +2041,19 @@ 
 {
   int pos = 0, pri;
 
+#ifdef TARGET_AIX_VERSION
+  /* Run dependent module initializers before any constructors in this
+     module.  */
+  switch (is_ctor_dtor (name))
+    {
+    case SYM_AIXI:
+    case SYM_AIXD:
+      return INT_MIN;
+    default:
+      break;
+    }
+#endif
+
   while (name[pos] == '_')
     ++pos;
   pos += 10; /* strlen ("GLOBAL__X_") */
@@ -2180,11 +2214,22 @@ 
 
   initname = concat ("_GLOBAL__FI_", prefix, NULL);
   fininame = concat ("_GLOBAL__FD_", prefix, NULL);
+#ifdef TARGET_AIX_VERSION
+  aix_shared_initname = concat ("_GLOBAL__AIXI_", prefix, NULL);
+  aix_shared_fininame = concat ("_GLOBAL__AIXD_", prefix, NULL);
+#endif
 
   free (prefix);
 
   /* Write the tables as C code.  */
 
+  /* This count variable is used to prevent multiple calls to the
+     constructors/destructors.
+     This guard against multiple calls is important on AIX as the initfini
+     functions are deliberately invoked multiple times as part of the
+     mechanisms GCC uses to order constructors across different dependent
+     shared libraries (see config/rs6000/aix.h).
+   */
   fprintf (stream, "static int count;\n");
   fprintf (stream, "typedef void entry_pt();\n");
   write_list_with_asm (stream, "extern entry_pt ", constructors.first);
@@ -2531,6 +2576,7 @@ 
 
 
       *end = '\0';
+
       switch (is_ctor_dtor (name))
 	{
 	case SYM_CTOR:
@@ -2892,6 +2938,25 @@ 
 
 		      switch (is_ctor_dtor (name))
 			{
+#if TARGET_AIX_VERSION
+		      /* Add AIX shared library initalisers/finalisers
+			 to the constructors/destructors list of the
+			 current module.  */
+			case SYM_AIXI:
+			  if (! (filter & SCAN_CTOR))
+			    break;
+			  if (is_shared && !aixlazy_flag)
+			    add_to_list (&constructors, name);
+			  break;
+
+			case SYM_AIXD:
+			  if (! (filter & SCAN_DTOR))
+			    break;
+			  if (is_shared && !aixlazy_flag)
+			    add_to_list (&destructors, name);
+			  break;
+#endif
+
 			case SYM_CTOR:
 			  if (! (filter & SCAN_CTOR))
 			    break;