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
@@ -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");
@@ -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)
+
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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. */
new file mode 100644
@@ -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;
+}
@@ -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