AIX linker now supports constructors and destructors detection. For such functions to be detected, their name must starts with __sinit or __sterm. and -bcdtors must be passed to linker calls. It will create "_cdtors" symbol which can be used to launch the initialization.
This patch creates a new RS6000 flag "-mcdtors=". With "-mcdtors=aix", gcc will generate these new constructors/destructors. With "-mcdtors=gcc", which is currently the default, gcc will continue to generate "gcc" format for constructors (ie _GLOBAL__I and _GLOBAL__D symbols). Ideally, it would have been better to enable the AIX format by default instead of using collect2. However, the compatibility between the previously-built binaries and the new ones is too complex to be done. gcc/ChangeLog: 2021-10-04 Clément Chigot <clement.chi...@atos.net> * collect2.c (aixbcdtors_flags): New variable. (main): Use it to detect -bcdtors and remove -binitfini flag. (write_c_file_stat): Adapt to new AIX format. * config/rs6000/aix.h (FILE_SINIT_FORMAT): New define. (FILE_STERM_FORMAT): New define. (TARGET_FILE_FUNCTION_FORMAT): New define. * config/rs6000/aix64.opt: Add -mcdtors flag. * config/rs6000/aix71.h (LINK_SPEC_COMMON): Pass -bcdtors when -mcdtors=aix is passed. * config/rs6000/aix72.h (LINK_SPEC_COMMON): Likewise. * config/rs6000/aix73.h (LINK_SPEC_COMMON): Likewise. * config/rs6000/rs6000-opts.h (enum rs6000_cdtors): New enum. * tree.c (get_file_function_name): Add TARGET_FILE_FUNCTION_FORMAT support. gcc/testsuite/ChangeLog: 2021-10-04 Clément Chigot <clement.chi...@atos.net> * gcc.target/powerpc/constructor-aix.c: New test.
From e1297880a2abe53db6422bcf25dcd883a2658260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= <clement.chi...@atos.net> Date: Mon, 4 Oct 2021 09:24:43 +0200 Subject: [PATCH] gcc: implement AIX-style constructors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AIX linker now supports constructors and destructors detection. For such functions to be detected, their name must starts with __sinit or __sterm. and -bcdtors must be passed to linker calls. It will create "_cdtors" symbol which can be used to launch the initialization. This patch creates a new RS6000 flag "-mcdtors=". With "-mcdtors=aix", gcc will generate these new constructors/destructors. With "-mcdtors=gcc", which is currently the default, gcc will continue to generate "gcc" format for constructors (ie _GLOBAL__I and _GLOBAL__D symbols). Ideally, it would have been better to enable the AIX format by default instead of using collect2. However, the compatibility between the previously-built binaries and the new ones is too complex to be done. gcc/ChangeLog: 2021-10-04 Clément Chigot <clement.chi...@atos.net> * collect2.c (aixbcdtors_flags): New variable. (main): Use it to detect -bcdtors and remove -binitfini flag. (write_c_file_stat): Adapt to new AIX format. * config/rs6000/aix.h (FILE_SINIT_FORMAT): New define. (FILE_STERM_FORMAT): New define. (TARGET_FILE_FUNCTION_FORMAT): New define. * config/rs6000/aix64.opt: Add -mcdtors flag. * config/rs6000/aix71.h (LINK_SPEC_COMMON): Pass -bcdtors when -mcdtors=aix is passed. * config/rs6000/aix72.h (LINK_SPEC_COMMON): Likewise. * config/rs6000/aix73.h (LINK_SPEC_COMMON): Likewise. * config/rs6000/rs6000-opts.h (enum rs6000_cdtors): New enum. * tree.c (get_file_function_name): Add TARGET_FILE_FUNCTION_FORMAT support. gcc/testsuite/ChangeLog: 2021-10-04 Clément Chigot <clement.chi...@atos.net> * gcc.target/powerpc/constructor-aix.c: New test. --- gcc/collect2.c | 91 +++++++++++++++++-- gcc/config/rs6000/aix.h | 56 ++++++++++++ gcc/config/rs6000/aix64.opt | 17 ++++ gcc/config/rs6000/aix71.h | 2 +- gcc/config/rs6000/aix72.h | 2 +- gcc/config/rs6000/aix73.h | 2 +- gcc/config/rs6000/rs6000-opts.h | 8 ++ .../gcc.target/powerpc/constructor-aix.c | 12 +++ gcc/tree.c | 5 + 9 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/gcc.target/powerpc/constructor-aix.c diff --git a/gcc/collect2.c b/gcc/collect2.c index 6f913041f26..59658cbadb7 100644 --- a/gcc/collect2.c +++ b/gcc/collect2.c @@ -186,6 +186,7 @@ static int aix64_flag; /* true if -b64 */ static int aixrtl_flag; /* true if -brtl */ static int aixlazy_flag; /* true if -blazy */ static int visibility_flag; /* true if -fvisibility */ +static int aixbcdtors_flag; /* True if -bcdtors */ #endif enum lto_mode_d { @@ -984,6 +985,8 @@ main (int argc, char **argv) aixrtl_flag = 0; else if (strcmp (argv[i], "-blazy") == 0) aixlazy_flag = 1; + else if (strcmp (argv[i], "-bcdtors") == 0) + aixbcdtors_flag = 1; #endif } @@ -1731,7 +1734,9 @@ main (int argc, char **argv) /* Tell the linker that we have initializer and finalizer functions. */ #ifdef LD_INIT_SWITCH #ifdef COLLECT_EXPORT_LIST - *ld2++ = concat (LD_INIT_SWITCH, ":", initname, ":", fininame, NULL); + /* Do not emit -binitfini when -bcdtors is enabled. */ + if (!aixbcdtors_flag) + *ld2++ = concat (LD_INIT_SWITCH, ":", initname, ":", fininame, NULL); #else *ld2++ = LD_INIT_SWITCH; *ld2++ = initname; @@ -2020,6 +2025,7 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) { const char *p, *q; char *prefix, *r; + char *regframe_name, *deregframe_name; int frames = (frame_tables.number > 0); /* Figure out name of output_file, stripping off .so version. */ @@ -2062,6 +2068,22 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) aix_shared_fininame = concat ("_GLOBAL__AIXD_", prefix, NULL); #endif + regframe_name = concat ("reg_frame", NULL, NULL); + deregframe_name = concat ("dereg_frame", NULL, NULL); +#ifdef COLLECT_EXPORT_LIST + /* In order to be detected by the linker, sinit/sterm symbols + must be external. Thus, reg_frame and dereg_frame can't + be static anymore and their name needs to be unique. + In order to ensure that frames are initialized before any + constructors, their constructor must have the highest priority + 0. */ + if (aixbcdtors_flag) + { + regframe_name = concat ("__sinit0_reg_frame_", prefix, NULL); + deregframe_name = concat ("__sterm0_dereg_frame_", prefix, NULL); + } +#endif + free (prefix); /* Write the tables as C code. */ @@ -2073,9 +2095,44 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) 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); +#ifdef COLLECT_EXPORT_LIST + if (!aixbcdtors_flag) + { +#endif + fprintf (stream, "static int count;\n"); + fprintf (stream, "typedef void entry_pt();\n"); + write_list_with_asm (stream, "extern entry_pt ", constructors.first); +#ifdef COLLECT_EXPORT_LIST + } +#endif + +#ifdef COLLECT_EXPORT_LIST + if (aixbcdtors_flag && !shared_obj) + { + /* Use __C_runtime_pstartup to run ctors and register dtors. + This whole part should normally be in libgcc but as + AIX cdtors format is currently not the default, managed + that in collect2. */ + fprintf (stream, "extern void (* _cdtors[]) (void);\n"); + fprintf (stream, "extern void __run_initial_ctors (void (**) (void));\n"); + fprintf (stream, "extern void __run_final_dtors (void);\n"); + fprintf (stream, "void _AIX_init(void)\n"); + fprintf (stream, "{\n"); + fprintf (stream, " __run_initial_ctors(&_cdtors[0]);\n"); + fprintf (stream, " __run_final_dtors();\n"); + fprintf (stream, "}\n"); + fprintf (stream, "void (*__C_runtime_pstartup) (void) = _AIX_init;\n"); + fprintf (stream, "\n"); + + /* crtcxa is compiled without -mcdtors=aix flag thus we must + manually ensure that __init_aix_libgcc_cxa_atexit is called + with AIX format. */ + fprintf (stream, "void __sterm65535_0__init_aix_libgcc_cxa_atexit (void)\n"); + fprintf (stream, "{\n"); + fprintf (stream, " __init_aix_libgcc_cxa_atexit ();\n"); + fprintf (stream, "}\n"); + } +#endif if (frames) { @@ -2102,7 +2159,12 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) fprintf (stream, "extern void *__gcc_unwind_dbase;\n"); #endif - fprintf (stream, "static void reg_frame () {\n"); +#ifdef COLLECT_EXPORT_LIST + if (aixbcdtors_flag) + fprintf (stream, "void %s () {\n", regframe_name); + else +#endif + fprintf (stream, "static void reg_frame () {\n"); fprintf (stream, "\tstatic struct object ob;\n"); #ifdef TARGET_AIX_VERSION /* Use __gcc_unwind_dbase as the base address for data on AIX. @@ -2114,11 +2176,24 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) #endif fprintf (stream, "\t}\n"); - fprintf (stream, "static void dereg_frame () {\n"); +#ifdef COLLECT_EXPORT_LIST + if (aixbcdtors_flag) + fprintf (stream, "void %s () {\n", deregframe_name); + else +#endif + fprintf (stream, "static void dereg_frame () {\n"); fprintf (stream, "\t__deregister_frame_info (frame_table);\n"); fprintf (stream, "\t}\n"); } +#ifdef COLLECT_EXPORT_LIST + /* Files built with the new AIX cdtors format don't need to + explicitly call them. + NOTE: This breaks compatibility with previously-built files. */ + if (aixbcdtors_flag) + return; +#endif + #ifdef COLLECT_EXPORT_LIST /* Set visibility of initializers to default. */ if (visibility_flag) @@ -2130,7 +2205,7 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) fprintf (stream, "\tstatic entry_pt *ctors[] = {\n"); write_list (stream, "\t\t", constructors.first); if (frames) - fprintf (stream, "\treg_frame,\n"); + fprintf (stream, "\t%s,\n", regframe_name); fprintf (stream, "\t};\n"); fprintf (stream, "\tentry_pt **p;\n"); fprintf (stream, "\tif (count++ != 0) return;\n"); @@ -2147,7 +2222,7 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED) fprintf (stream, "\tstatic entry_pt *dtors[] = {\n"); write_list (stream, "\t\t", destructors.first); if (frames) - fprintf (stream, "\tdereg_frame,\n"); + fprintf (stream, "\t%s,\n", deregframe_name); fprintf (stream, "\t};\n"); fprintf (stream, "\tentry_pt **p;\n"); fprintf (stream, "\tif (--count != 0) return;\n"); diff --git a/gcc/config/rs6000/aix.h b/gcc/config/rs6000/aix.h index 0f4d8cb2dc8..d9ed88cb459 100644 --- a/gcc/config/rs6000/aix.h +++ b/gcc/config/rs6000/aix.h @@ -284,3 +284,59 @@ #define SUBTARGET_DRIVER_SELF_SPECS \ "%{m64:-maix64} %<m64", \ "%{m32:-maix32} %<m32" + +/* Support for cdtors detected by AIX linker. + With -bcdtors flag, AIX linker is able to handle initialisers + by itself. For that, these initalisers must be named with the + following schema: __sinit{priority}_{name}. For destructors, + __sinit is replaced by __sterm. + + For now, this part is enabled only when -mcdtors=aix flag is + passed to gcc. + + The TARGET_FILE_FUNCTION_FORMAT allows to change the default name of + gcc constructors and destructors to one which will be understand by + AIX linker. + + NOTE: + sinit/sterm functions will be triggered only if -bcdtors is passed + to the linker when building the binary. Even libraries built with + -bcdtors won't triggered those functions by themselves. + Ideally, if one day AIX way become the default, we would like + to ensure full compatibility between previously-made libraries + and new ones. However, this is hardly possible. + The main reason why this isn't working is that old libraries need + to be able to call constructors of new libraries using the previous + way, ie using GLOBAL_AIXI symbols. But if an old library loading a + new library is called by a new binary (thus with -bcdtors enabled), + it will trigger the new library's cdtors twice. + TODO: The only solution is to modify the wrapper created by gcc + around constructors and destructors so that it ensures that they + are called only once. +*/ +#define FILE_SINIT_FORMAT "__sinit%s_%s" +#define FILE_STERM_FORMAT "__sterm%s_%s" +#define TARGET_FILE_FUNCTION_FORMAT(TYPE, BUF, SUBNAME) \ + do \ + { \ + if (rs6000_current_cdtors == CDTORS_AIX && (TYPE[0] == 'I')) \ + { \ + BUF = (char *) alloca (sizeof (FILE_SINIT_FORMAT) \ + + strlen (SUBNAME) + strlen (TYPE) - 2); \ + sprintf (BUF, FILE_SINIT_FORMAT, TYPE + 2, SUBNAME); \ + } \ + else if (rs6000_current_cdtors == CDTORS_AIX && (TYPE[0] == 'D')) \ + { \ + BUF = (char *) alloca (sizeof (FILE_STERM_FORMAT) \ + + strlen (SUBNAME) + strlen (TYPE) - 2); \ + sprintf (BUF, FILE_STERM_FORMAT, TYPE + 2, SUBNAME); \ + } \ + else \ + { \ + BUF = (char *) alloca (sizeof (FILE_FUNCTION_FORMAT) \ + + strlen (SUBNAME) + strlen (TYPE)); \ + sprintf (BUF, FILE_FUNCTION_FORMAT, TYPE, SUBNAME); \ + } \ + } \ + while(0) + diff --git a/gcc/config/rs6000/aix64.opt b/gcc/config/rs6000/aix64.opt index 15d863fa0a2..508e79f17f9 100644 --- a/gcc/config/rs6000/aix64.opt +++ b/gcc/config/rs6000/aix64.opt @@ -59,3 +59,20 @@ Driver m32 Driver + +mcdtors= +Target RejectNegative Joined Enum(rs6000_cdtors) Var(rs6000_current_cdtors) +Select constructors format. + +Enum +Name(rs6000_cdtors) Type(enum rs6000_cdtors) +Known constructors format (for use with the -mcdtors= option): + +EnumValue +Enum(rs6000_cdtors) String(aix) Value(CDTORS_AIX) + +EnumValue +Enum(rs6000_cdtors) String(gcc) Value(CDTORS_GCC) + +TargetVariable +enum rs6000_cdtors rs6000_current_cdtors = CDTORS_GCC \ No newline at end of file diff --git a/gcc/config/rs6000/aix71.h b/gcc/config/rs6000/aix71.h index 5785e673d4b..517bfc020f0 100644 --- a/gcc/config/rs6000/aix71.h +++ b/gcc/config/rs6000/aix71.h @@ -176,7 +176,7 @@ do { \ #define LINK_SPEC64 "-b64" #define LINK_SPEC_COMMON "-bpT:0x10000000 -bpD:0x20000000 %{!r:-btextro}\ %{static:-bnso %(link_syscalls) } %{shared:-bM:SRE %{!e:-bnoentry}}\ - %{mpe:-binitfini:poe_remote_main} " + %{mpe:-binitfini:poe_remote_main} %{mcdtors=aix:-bcdtors}" #undef STARTFILE_SPEC #if DEFAULT_ARCH64_P diff --git a/gcc/config/rs6000/aix72.h b/gcc/config/rs6000/aix72.h index 5b73f058318..75983b1a979 100644 --- a/gcc/config/rs6000/aix72.h +++ b/gcc/config/rs6000/aix72.h @@ -177,7 +177,7 @@ do { \ #define LINK_SPEC64 "-b64" #define LINK_SPEC_COMMON "-bpT:0x10000000 -bpD:0x20000000 %{!r:-btextro}\ %{static:-bnso %(link_syscalls) } %{shared:-bM:SRE %{!e:-bnoentry}}\ - %{mpe:-binitfini:poe_remote_main} " + %{mpe:-binitfini:poe_remote_main} %{mcdtors=aix:-bcdtors}" #undef STARTFILE_SPEC #if DEFAULT_ARCH64_P diff --git a/gcc/config/rs6000/aix73.h b/gcc/config/rs6000/aix73.h index a29ba8b6cfa..5f87a29b49f 100644 --- a/gcc/config/rs6000/aix73.h +++ b/gcc/config/rs6000/aix73.h @@ -178,7 +178,7 @@ do { \ #define LINK_SPEC64 "-b64" #define LINK_SPEC_COMMON "-bpT:0x10000000 -bpD:0x20000000 %{!r:-btextro}\ %{static:-bnso %(link_syscalls) } %{shared:-bM:SRE %{!e:-bnoentry}}\ - %{mpe:-binitfini:poe_remote_main} " + %{mpe:-binitfini:poe_remote_main} %{mcdtors=aix:-bcdtors}" #undef STARTFILE_SPEC #if DEFAULT_ARCH64_P diff --git a/gcc/config/rs6000/rs6000-opts.h b/gcc/config/rs6000/rs6000-opts.h index 51d6c654842..ab78f31dc9d 100644 --- a/gcc/config/rs6000/rs6000-opts.h +++ b/gcc/config/rs6000/rs6000-opts.h @@ -147,6 +147,14 @@ enum stack_protector_guard { SSP_GLOBAL /* global canary */ }; +/* Constructors format for AIX32 + aix: AIX format (__sinit/__sterm) + gcc: GCC format (_GLOBAL__I/_GLOBAL__F) */ +enum rs6000_cdtors { + CDTORS_AIX, + CDTORS_GCC +}; + /* No enumeration is defined to index the -mcpu= values (entries in processor_target_table), with the type int being used instead, but we need to distinguish the special "native" value. */ diff --git a/gcc/testsuite/gcc.target/powerpc/constructor-aix.c b/gcc/testsuite/gcc.target/powerpc/constructor-aix.c new file mode 100644 index 00000000000..65222a5e239 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/constructor-aix.c @@ -0,0 +1,12 @@ +/* { dg-do run { target powerpc*-*-aix* } } */ +/* { dg-options "-mcdtors=aix" } */ + +int i; + +void hello (void) __attribute__ ((constructor)); +void hello (void) { i = 1; } + +int main (void) { + if (i != 1) + return 1; +} diff --git a/gcc/tree.c b/gcc/tree.c index 486cdb0e7c7..70bac0b1943 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -8633,6 +8633,10 @@ get_file_function_name (const char *type) } clean_symbol_name (q); + +#ifdef TARGET_FILE_FUNCTION_FORMAT + TARGET_FILE_FUNCTION_FORMAT(type, buf, p); +#else buf = (char *) alloca (sizeof (FILE_FUNCTION_FORMAT) + strlen (p) + strlen (type)); @@ -8641,6 +8645,7 @@ get_file_function_name (const char *type) the program) rather than the file name (which imposes extra constraints). */ sprintf (buf, FILE_FUNCTION_FORMAT, type, p); +#endif return get_identifier (buf); } -- 2.33.0