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

Reply via email to