gcc: implement AIX-style constructors

Message ID PA4PR02MB6686D2D6132441155E4B0CF9EABC9@PA4PR02MB6686.eurprd02.prod.outlook.com
State New
Headers
Series gcc: implement AIX-style constructors |

Commit Message

Li, Pan2 via Gcc-patches Oct. 18, 2021, 7:55 a.m. UTC
  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.chigot@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.chigot@atos.net>

        * gcc.target/powerpc/constructor-aix.c: New test.
  

Comments

David Edelsohn Oct. 19, 2021, 8:14 p.m. UTC | #1
Clement,

+      /* 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.  */

Why are you emitting the special startup function call in collect2.c
instead of placing it in libgcc.  The comment mentions that the
special startup function should be defined in libgcc.

Yes, the AIX ld bcdtors mechanism is not the default, but what is the
harm? The symbol will be defined and exported by libgcc. If the AIX
linker -bcdtors functionality is not invoked, the symbol is not used.
And if a user does invoke the AIX linker with -bcdtors, the behavior
will be the same (either the program was compiled to use AIX cdtors or
not, which is the same if the startup function is emitted by
collect2.c.

Also, the patch should include documentation of the option.  The
documentation should mention that this is for interoperability with
IBM XL Compiler, and the option will not operate correctly unless the
application and the GCC runtime are built with the option.

Thanks, David

On Mon, Oct 18, 2021 at 3:55 AM CHIGOT, CLEMENT <clement.chigot@atos.net> wrote:
>
> 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.chigot@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.chigot@atos.net>
>
>         * gcc.target/powerpc/constructor-aix.c: New test.
>
>
  
Li, Pan2 via Gcc-patches Oct. 21, 2021, 12:39 p.m. UTC | #2
Hi David,

The problem is that cdtors is created by the linker only when -bcdtors
flag is provided. Thus, if we add "extern void (* _cdtors[]) (void);" to
the "crtcxa.c", we can't used it without using the new constructor types.
One solution would be to create another crtcxa (eg crtcxa_cdtors.o) which will
be replacing the default crtcxa.o when needed. I didn't thought about that
when creating the patch. But it might be a better approach. What do you think ?

About documentation, I wasn't aware of that. I'll do it.

Clément
  
David Edelsohn Oct. 21, 2021, 1:59 p.m. UTC | #3
On Thu, Oct 21, 2021 at 8:39 AM CHIGOT, CLEMENT <clement.chigot@atos.net> wrote:
>
> Hi David,
>
> The problem is that cdtors is created by the linker only when the -bcdtors
> flag is provided. Thus, if we add "extern void (* _cdtors[]) (void);" to
> the "crtcxa.c", we can't use it without using the new constructor types.
> One solution would be to create another crtcxa (eg crtcxa_cdtors.o) which will
> be replacing the default crtcxa.o when needed. I didn't think about that
> when creating the patch. But it might be a better approach. What do you think ?

Hi, Clement

Another, optional object file in libgcc seems like a cleaner solution
than collect2.c emitting all of the code.

The file should not be called crtcxa_cdtors.o because "cxa" came from
the name of the libstdc++ support functions.  Maybe crtcdtors.o or
crt_cdtors.o or aixcdtors.o or aix_cdtors.o .

Thanks, David
  
Li, Pan2 via Gcc-patches Nov. 2, 2021, 1:28 p.m. UTC | #4
Hi David,

Here is the new version of the patch.
I've moved the startup function in crtcdtors files.

I'm just wondering if the part dealing with the
__init_aix_libgcc_cxa_atexit is needed. I'm adding it because
the destructor created in crtcxa.o is following GCC format and
thus won't be launched if the flag "-mcdtors=aix" is passed.
However, as you said, this option might not operate correctly
if the GCC runtime isn't rebuild with it.

Thanks,
Clément
  
Li, Pan2 via Gcc-patches Nov. 16, 2021, 8:42 a.m. UTC | #5
> Hi David, 
> 
> Here is the new version of the patch. 
> I've moved the startup function in crtcdtors files.
> 
> I'm just wondering if the part dealing with the
> __init_aix_libgcc_cxa_atexit is needed. I'm adding it because
> the destructor created in crtcxa.o is following GCC format and
> thus won't be launched if the flag "-mcdtors=aix" is passed.
> However, as you said, this option might not operate correctly
> if the GCC runtime isn't rebuild with it.

Gentle Ping. 

Thanks, 
Clément
  

Patch

From e1297880a2abe53db6422bcf25dcd883a2658260 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= <clement.chigot@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.chigot@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.chigot@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