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....@gmail.com> Andrew Dixie <andr...@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. 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;