Index: gcc/gcc/collect2.c |
diff --git a/gcc/gcc/collect2.c b/gcc/gcc/collect2.c |
index 3f62dfe40b3abd5b3186cfb813b2afa7731507fe..f5498aa5e469630bb9d4e07fce70cbb85b3e2edc 100644 |
--- a/gcc/gcc/collect2.c |
+++ b/gcc/gcc/collect2.c |
@@ -1,7 +1,7 @@ |
/* Collect static initialization info into data structures that can be |
traversed by C++ initialization and finalization routines. |
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, |
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008 |
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010 |
Free Software Foundation, Inc. |
Contributed by Chris Smith (csmith@convex.com). |
Heavily modified by Michael Meissner (meissner@cygnus.com), |
@@ -35,6 +35,10 @@ along with GCC; see the file COPYING3. If not see |
# define SIGCHLD SIGCLD |
#endif |
+/* TARGET_64BIT may be defined to use driver specific functionality. */ |
+#undef TARGET_64BIT |
+#define TARGET_64BIT TARGET_64BIT_DEFAULT |
+ |
#ifndef LIBRARY_PATH_ENV |
#define LIBRARY_PATH_ENV "LIBRARY_PATH" |
#endif |
@@ -42,6 +46,7 @@ along with GCC; see the file COPYING3. If not see |
#define COLLECT |
#include "collect2.h" |
+#include "collect2-aix.h" |
#include "demangle.h" |
#include "obstack.h" |
#include "intl.h" |
@@ -54,7 +59,9 @@ along with GCC; see the file COPYING3. If not see |
cross-versions are in the proper directories. */ |
#ifdef CROSS_DIRECTORY_STRUCTURE |
+#ifndef CROSS_AIX_SUPPORT |
#undef OBJECT_FORMAT_COFF |
+#endif |
#undef MD_EXEC_PREFIX |
#undef REAL_LD_FILE_NAME |
#undef REAL_NM_FILE_NAME |
@@ -72,6 +79,7 @@ along with GCC; see the file COPYING3. If not see |
#ifdef OBJECT_FORMAT_COFF |
+#ifndef CROSS_DIRECTORY_STRUCTURE |
#include <a.out.h> |
#include <ar.h> |
@@ -86,6 +94,7 @@ along with GCC; see the file COPYING3. If not see |
#endif |
#include <ldfcn.h> |
+#endif |
/* Some systems have an ISCOFF macro, but others do not. In some cases |
the macro may be wrong. MY_ISCOFF is defined in tm.h files for machines |
@@ -140,6 +149,15 @@ int do_collecting = 1; |
int do_collecting = 0; |
#endif |
+/* Cook up an always defined indication of whether we proceed the |
+ "EXPORT_LIST" way. */ |
+ |
+#ifdef COLLECT_EXPORT_LIST |
+#define DO_COLLECT_EXPORT_LIST 1 |
+#else |
+#define DO_COLLECT_EXPORT_LIST 0 |
+#endif |
+ |
/* Nonzero if we should suppress the automatic demangling of identifiers |
in linker error messages. Set from COLLECT_NO_DEMANGLE. */ |
int no_demangle; |
@@ -160,15 +178,6 @@ struct head |
int number; |
}; |
-/* Enumeration giving which pass this is for scanning the program file. */ |
- |
-enum pass { |
- PASS_FIRST, /* without constructors */ |
- PASS_OBJ, /* individual objects */ |
- PASS_LIB, /* looking for shared libraries */ |
- PASS_SECOND /* with constructors linked in */ |
-}; |
- |
int vflag; /* true if -v */ |
static int rflag; /* true if -r */ |
static int strip_flag; /* true if -s */ |
@@ -179,6 +188,15 @@ static int aix64_flag; /* true if -b64 */ |
static int aixrtl_flag; /* true if -brtl */ |
#endif |
+enum lto_mode_d { |
+ LTO_MODE_NONE, /* Not doing LTO. */ |
+ LTO_MODE_LTO, /* Normal LTO. */ |
+ LTO_MODE_WHOPR /* WHOPR. */ |
+}; |
+ |
+/* Current LTO mode. */ |
+static enum lto_mode_d lto_mode = LTO_MODE_NONE; |
+ |
int debug; /* true if -debug */ |
static int shared_obj; /* true if -shared */ |
@@ -188,6 +206,7 @@ static const char *o_file; /* <xxx>.o for constructor/destructor list. */ |
#ifdef COLLECT_EXPORT_LIST |
static const char *export_file; /* <xxx>.x for AIX export list. */ |
#endif |
+static char **lto_o_files; /* Output files for LTO. */ |
const char *ldout; /* File for ld stdout. */ |
const char *lderrout; /* File for ld stderr. */ |
static const char *output_file; /* Output file for ld. */ |
@@ -212,6 +231,14 @@ static char *response_file; /* Name of any current response file */ |
struct obstack temporary_obstack; |
char * temporary_firstobj; |
+/* A string that must be prepended to a target OS path in order to find |
+ it on the host system. */ |
+#ifdef TARGET_SYSTEM_ROOT |
+static const char *target_system_root = TARGET_SYSTEM_ROOT; |
+#else |
+static const char *target_system_root = ""; |
+#endif |
+ |
/* Structure to hold all the directories in which to search for files to |
execute. */ |
@@ -237,6 +264,25 @@ static struct path_prefix *libpaths[3] = {&cmdline_lib_dirs, |
&libpath_lib_dirs, NULL}; |
#endif |
+/* List of names of object files containing LTO information. |
+ These are a subset of the object file names appearing on the |
+ command line, and must be identical, in the sense of pointer |
+ equality, with the names passed to maybe_run_lto_and_relink(). */ |
+ |
+struct lto_object |
+{ |
+ const char *name; /* Name of object file. */ |
+ struct lto_object *next; /* Next in linked list. */ |
+}; |
+ |
+struct lto_object_list |
+{ |
+ struct lto_object *first; /* First list element. */ |
+ struct lto_object *last; /* Last list element. */ |
+}; |
+ |
+static struct lto_object_list lto_objects; |
+ |
/* Special kinds of symbols that a name may denote. */ |
typedef enum { |
@@ -259,6 +305,7 @@ static void prefix_from_string (const char *, struct path_prefix *); |
static void do_wait (const char *, struct pex_obj *); |
static void fork_execute (const char *, char **); |
static void maybe_unlink (const char *); |
+static void maybe_unlink_list (char **); |
static void add_to_list (struct head *, const char *); |
static int extract_init_priority (const char *); |
static void sort_ids (struct head *); |
@@ -275,7 +322,6 @@ static void write_c_file_stat (FILE *, const char *); |
#ifndef LD_INIT_SWITCH |
static void write_c_file_glob (FILE *, const char *); |
#endif |
-static void scan_prog_file (const char *, enum pass); |
#ifdef SCAN_LIBRARIES |
static void scan_libraries (const char *); |
#endif |
@@ -290,6 +336,51 @@ static void write_aix_file (FILE *, struct id *); |
static char *resolve_lib_name (const char *); |
#endif |
static char *extract_string (const char **); |
+ |
+/* Enumerations describing which pass this is for scanning the |
+ program file ... */ |
+ |
+typedef enum { |
+ PASS_FIRST, /* without constructors */ |
+ PASS_OBJ, /* individual objects */ |
+ PASS_LIB, /* looking for shared libraries */ |
+ PASS_SECOND, /* with constructors linked in */ |
+ PASS_LTOINFO /* looking for objects with LTO info */ |
+} scanpass; |
+ |
+/* ... and which kinds of symbols are to be considered. */ |
+ |
+enum scanfilter_masks { |
+ SCAN_NOTHING = 0, |
+ |
+ SCAN_CTOR = 1 << SYM_CTOR, |
+ SCAN_DTOR = 1 << SYM_DTOR, |
+ SCAN_INIT = 1 << SYM_INIT, |
+ SCAN_FINI = 1 << SYM_FINI, |
+ SCAN_DWEH = 1 << SYM_DWEH, |
+ SCAN_ALL = ~0 |
+}; |
+ |
+/* This type is used for parameters and variables which hold |
+ combinations of the flags in enum scanfilter_masks. */ |
+typedef int scanfilter; |
+ |
+/* Scan the name list of the loaded program for the symbols g++ uses for |
+ static constructors and destructors. |
+ |
+ The SCANPASS argument tells which collect processing pass this is for and |
+ the SCANFILTER argument tells which kinds of symbols to consider in this |
+ pass. Symbols of a special kind not in the filter mask are considered as |
+ regular ones. |
+ |
+ The constructor table begins at __CTOR_LIST__ and contains a count of the |
+ number of pointers (or -1 if the constructors are built in a separate |
+ section by the linker), followed by the pointers to the constructor |
+ functions, terminated with a null pointer. The destructor table has the |
+ same format, and begins at __DTOR_LIST__. */ |
+ |
+static void scan_prog_file (const char *, scanpass, scanfilter); |
+ |
/* Delete tempfiles and exit function. */ |
@@ -307,6 +398,9 @@ collect_exit (int status) |
maybe_unlink (export_file); |
#endif |
+ if (lto_o_files) |
+ maybe_unlink_list (lto_o_files); |
+ |
if (ldout != 0 && ldout[0]) |
{ |
dump_file (ldout, stdout); |
@@ -340,6 +434,17 @@ notice (const char *cmsgid, ...) |
va_end (ap); |
} |
+/* Notify user of a non-error, without translating the format string. */ |
+void |
+notice_translated (const char *cmsgid, ...) |
+{ |
+ va_list ap; |
+ |
+ va_start (ap, cmsgid); |
+ vfprintf (stderr, cmsgid, ap); |
+ va_end (ap); |
+} |
+ |
/* Die when sys call fails. */ |
void |
@@ -416,6 +521,9 @@ handler (int signo) |
maybe_unlink (export_file); |
#endif |
+ if (lto_o_files) |
+ maybe_unlink_list (lto_o_files); |
+ |
if (response_file) |
maybe_unlink (response_file); |
@@ -538,7 +646,7 @@ dump_file (const char *name, FILE *to) |
static symkind |
is_ctor_dtor (const char *s) |
{ |
- struct names { const char *const name; const int len; const int ret; |
+ struct names { const char *const name; const int len; symkind ret; |
const int two_underscores; }; |
const struct names *p; |
@@ -759,6 +867,247 @@ prefix_from_string (const char *p, struct path_prefix *pprefix) |
} |
free (nstore); |
} |
+ |
+#ifdef OBJECT_FORMAT_NONE |
+ |
+/* Add an entry for the object file NAME to object file list LIST. |
+ New entries are added at the end of the list. The original pointer |
+ value of NAME is preserved, i.e., no string copy is performed. */ |
+ |
+static void |
+add_lto_object (struct lto_object_list *list, const char *name) |
+{ |
+ struct lto_object *n = XNEW (struct lto_object); |
+ n->name = name; |
+ n->next = NULL; |
+ |
+ if (list->last) |
+ list->last->next = n; |
+ else |
+ list->first = n; |
+ |
+ list->last = n; |
+} |
+#endif /* OBJECT_FORMAT_NONE */ |
+ |
+ |
+/* Perform a link-time recompilation and relink if any of the object |
+ files contain LTO info. The linker command line LTO_LD_ARGV |
+ represents the linker command that would produce a final executable |
+ without the use of LTO. OBJECT_LST is a vector of object file names |
+ appearing in LTO_LD_ARGV that are to be considerd for link-time |
+ recompilation, where OBJECT is a pointer to the last valid element. |
+ (This awkward convention avoids an impedance mismatch with the |
+ usage of similarly-named variables in main().) The elements of |
+ OBJECT_LST must be identical, i.e., pointer equal, to the |
+ corresponding arguments in LTO_LD_ARGV. |
+ |
+ Upon entry, at least one linker run has been performed without the |
+ use of any LTO info that might be present. Any recompilations |
+ necessary for template instantiations have been performed, and |
+ initializer/finalizer tables have been created if needed and |
+ included in the linker command line LTO_LD_ARGV. If any of the |
+ object files contain LTO info, we run the LTO back end on all such |
+ files, and perform the final link with the LTO back end output |
+ substituted for the LTO-optimized files. In some cases, a final |
+ link with all link-time generated code has already been performed, |
+ so there is no need to relink if no LTO info is found. In other |
+ cases, our caller has not produced the final executable, and is |
+ relying on us to perform the required link whether LTO info is |
+ present or not. In that case, the FORCE argument should be true. |
+ Note that the linker command line argument LTO_LD_ARGV passed into |
+ this function may be modified in place. */ |
+ |
+static void |
+maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst, |
+ const char **object, bool force) |
+{ |
+ const char **object_file = CONST_CAST2 (const char **, char **, object_lst); |
+ |
+ int num_lto_c_args = 1; /* Allow space for the terminating NULL. */ |
+ |
+ while (object_file < object) |
+ { |
+ /* If file contains LTO info, add it to the list of LTO objects. */ |
+ scan_prog_file (*object_file++, PASS_LTOINFO, SCAN_ALL); |
+ |
+ /* Increment the argument count by the number of object file arguments |
+ we will add. An upper bound suffices, so just count all of the |
+ object files regardless of whether they contain LTO info. */ |
+ num_lto_c_args++; |
+ } |
+ |
+ if (lto_objects.first) |
+ { |
+ const char *opts; |
+ char **lto_c_argv; |
+ const char **lto_c_ptr; |
+ const char *cp; |
+ const char **p, **q, **r; |
+ const char **lto_o_ptr; |
+ struct lto_object *list; |
+ char *lto_wrapper = getenv ("COLLECT_LTO_WRAPPER"); |
+ struct pex_obj *pex; |
+ const char *prog = "lto-wrapper"; |
+ |
+ if (!lto_wrapper) |
+ fatal ("COLLECT_LTO_WRAPPER must be set."); |
+ |
+ /* There is at least one object file containing LTO info, |
+ so we need to run the LTO back end and relink. */ |
+ |
+ /* Get compiler options passed down from the parent `gcc' command. |
+ These must be passed to the LTO back end. */ |
+ opts = getenv ("COLLECT_GCC_OPTIONS"); |
+ |
+ /* Increment the argument count by the number of inherited options. |
+ Some arguments may be filtered out later. Again, an upper bound |
+ suffices. */ |
+ |
+ cp = opts; |
+ |
+ while (cp && *cp) |
+ { |
+ extract_string (&cp); |
+ num_lto_c_args++; |
+ } |
+ obstack_free (&temporary_obstack, temporary_firstobj); |
+ |
+ if (debug) |
+ num_lto_c_args++; |
+ |
+ /* Increment the argument count by the number of initial |
+ arguments added below. */ |
+ num_lto_c_args += 9; |
+ |
+ lto_c_argv = (char **) xcalloc (sizeof (char *), num_lto_c_args); |
+ lto_c_ptr = CONST_CAST2 (const char **, char **, lto_c_argv); |
+ |
+ *lto_c_ptr++ = lto_wrapper; |
+ *lto_c_ptr++ = c_file_name; |
+ |
+ cp = opts; |
+ |
+ while (cp && *cp) |
+ { |
+ const char *s = extract_string (&cp); |
+ |
+ /* Pass the option or argument to the wrapper. */ |
+ *lto_c_ptr++ = xstrdup (s); |
+ } |
+ obstack_free (&temporary_obstack, temporary_firstobj); |
+ |
+ if (debug) |
+ *lto_c_ptr++ = xstrdup ("-debug"); |
+ |
+ /* Add LTO objects to the wrapper command line. */ |
+ for (list = lto_objects.first; list; list = list->next) |
+ *lto_c_ptr++ = list->name; |
+ |
+ *lto_c_ptr = NULL; |
+ |
+ /* Save intermediate WPA files in lto1 if debug. */ |
+ if (debug) |
+ putenv (xstrdup ("WPA_SAVE_LTRANS=1")); |
+ |
+ /* Run the LTO back end. */ |
+ pex = collect_execute (prog, lto_c_argv, NULL, NULL, PEX_SEARCH); |
+ { |
+ int c; |
+ FILE *stream; |
+ size_t i, num_files; |
+ char *start, *end; |
+ |
+ stream = pex_read_output (pex, 0); |
+ gcc_assert (stream); |
+ |
+ num_files = 0; |
+ while ((c = getc (stream)) != EOF) |
+ { |
+ obstack_1grow (&temporary_obstack, c); |
+ if (c == '\n') |
+ ++num_files; |
+ } |
+ |
+ lto_o_files = XNEWVEC (char *, num_files + 1); |
+ lto_o_files[num_files] = NULL; |
+ start = XOBFINISH (&temporary_obstack, char *); |
+ for (i = 0; i < num_files; ++i) |
+ { |
+ end = start; |
+ while (*end != '\n') |
+ ++end; |
+ *end = '\0'; |
+ |
+ lto_o_files[i] = xstrdup (start); |
+ |
+ start = end + 1; |
+ } |
+ |
+ obstack_free (&temporary_obstack, temporary_firstobj); |
+ } |
+ do_wait (prog, pex); |
+ pex = NULL; |
+ |
+ /* After running the LTO back end, we will relink, substituting |
+ the LTO output for the object files that we submitted to the |
+ LTO. Here, we modify the linker command line for the relink. */ |
+ p = CONST_CAST2 (const char **, char **, lto_ld_argv); |
+ lto_o_ptr = CONST_CAST2 (const char **, char **, lto_o_files); |
+ |
+ while (*p != NULL) |
+ { |
+ for (list = lto_objects.first; list; list = list->next) |
+ { |
+ if (*p == list->name) /* Note test for pointer equality! */ |
+ { |
+ /* Excise argument from linker command line. */ |
+ if (*lto_o_ptr) |
+ { |
+ /* Replace first argument with LTO output file. */ |
+ *p++ = *lto_o_ptr++; |
+ } |
+ else |
+ { |
+ /* Move following arguments one position earlier, |
+ overwriting the current argument. */ |
+ q = p; |
+ r = p + 1; |
+ while (*r != NULL) |
+ *q++ = *r++; |
+ *q = NULL; |
+ } |
+ |
+ /* No need to continue searching the LTO object list. */ |
+ break; |
+ } |
+ } |
+ |
+ /* If we didn't find a match, move on to the next argument. |
+ Otherwise, P has been set to the correct argument position |
+ at which to continue. */ |
+ if (!list) ++p; |
+ } |
+ |
+ /* The code above assumes we will never have more lto output files than |
+ input files. Otherwise, we need to resize lto_ld_argv. Check this |
+ assumption. */ |
+ if (*lto_o_ptr) |
+ fatal ("too many lto output files"); |
+ |
+ /* Run the linker again, this time replacing the object files |
+ optimized by the LTO with the temporary file generated by the LTO. */ |
+ fork_execute ("ld", lto_ld_argv); |
+ |
+ maybe_unlink_list (lto_o_files); |
+ } |
+ else if (force) |
+ { |
+ /* Our caller is relying on us to do the link |
+ even though there is no LTO back end work to be done. */ |
+ fork_execute ("ld", lto_ld_argv); |
+ } |
+} |
/* Main program. */ |
@@ -766,6 +1115,7 @@ int |
main (int argc, char **argv) |
{ |
static const char *const ld_suffix = "ld"; |
+ static const char *const plugin_ld_suffix = PLUGIN_LD; |
static const char *const real_ld_suffix = "real-ld"; |
static const char *const collect_ld_suffix = "collect-ld"; |
static const char *const nm_suffix = "nm"; |
@@ -784,6 +1134,8 @@ main (int argc, char **argv) |
const char *const full_ld_suffix = |
concat(target_machine, "-", ld_suffix, NULL); |
+ const char *const full_plugin_ld_suffix = |
+ concat(target_machine, "-", plugin_ld_suffix, NULL); |
const char *const full_nm_suffix = |
concat (target_machine, "-", nm_suffix, NULL); |
const char *const full_gnm_suffix = |
@@ -798,6 +1150,7 @@ main (int argc, char **argv) |
concat (target_machine, "-", gstrip_suffix, NULL); |
#else |
const char *const full_ld_suffix = ld_suffix; |
+ const char *const full_plugin_ld_suffix = plugin_ld_suffix; |
const char *const full_nm_suffix = nm_suffix; |
const char *const full_gnm_suffix = gnm_suffix; |
#ifdef LDD_SUFFIX |
@@ -818,6 +1171,16 @@ main (int argc, char **argv) |
const char **c_ptr; |
char **ld1_argv; |
const char **ld1; |
+ bool use_plugin = false; |
+ |
+ /* The kinds of symbols we will have to consider when scanning the |
+ outcome of a first pass link. This is ALL to start with, then might |
+ be adjusted before getting to the first pass link per se, typically on |
+ AIX where we perform an early scan of objects and libraries to fetch |
+ the list of global ctors/dtors and make sure they are not garbage |
+ collected. */ |
+ scanfilter ld1_filter = SCAN_ALL; |
+ |
char **ld2_argv; |
const char **ld2; |
char **object_lst; |
@@ -826,6 +1189,8 @@ main (int argc, char **argv) |
int num_c_args; |
char **old_argv; |
+ bool use_verbose = false; |
+ |
old_argv = argv; |
expandargv (&argc, &argv); |
if (argv != old_argv) |
@@ -857,9 +1222,12 @@ main (int argc, char **argv) |
/* Do not invoke xcalloc before this point, since locale needs to be |
set first, in case a diagnostic is issued. */ |
- ld1 = (const char **)(ld1_argv = XCNEWVEC (char *, argc+4)); |
- ld2 = (const char **)(ld2_argv = XCNEWVEC (char *, argc+11)); |
- object = (const char **)(object_lst = XCNEWVEC (char *, argc)); |
+ ld1_argv = XCNEWVEC (char *, argc + 4); |
+ ld1 = CONST_CAST2 (const char **, char **, ld1_argv); |
+ ld2_argv = XCNEWVEC (char *, argc + 11); |
+ ld2 = CONST_CAST2 (const char **, char **, ld2_argv); |
+ object_lst = XCNEWVEC (char *, argc); |
+ object = CONST_CAST2 (const char **, char **, object_lst); |
#ifdef DEBUG |
debug = 1; |
@@ -867,7 +1235,8 @@ main (int argc, char **argv) |
/* Parse command line early for instances of -debug. This allows |
the debug flag to be set before functions like find_a_file() |
- are called. */ |
+ are called. We also look for the -flto or -fwhopr flag to know |
+ what LTO mode we are in. */ |
{ |
int i; |
@@ -875,6 +1244,38 @@ main (int argc, char **argv) |
{ |
if (! strcmp (argv[i], "-debug")) |
debug = 1; |
+ else if (! strcmp (argv[i], "-flto") && ! use_plugin) |
+ { |
+ use_verbose = true; |
+ lto_mode = LTO_MODE_LTO; |
+ } |
+ else if (! strcmp (argv[i], "-fwhopr") && ! use_plugin) |
+ { |
+ use_verbose = true; |
+ lto_mode = LTO_MODE_WHOPR; |
+ } |
+ else if (! strcmp (argv[i], "-plugin")) |
+ { |
+ use_plugin = true; |
+ use_verbose = true; |
+ lto_mode = LTO_MODE_NONE; |
+ } |
+#ifdef COLLECT_EXPORT_LIST |
+ /* since -brtl, -bexport, -b64 are not position dependent |
+ also check for them here */ |
+ if ((argv[i][0] == '-') && (argv[i][1] == 'b')) |
+ { |
+ arg = argv[i]; |
+ /* We want to disable automatic exports on AIX when user |
+ explicitly puts an export list in command line */ |
+ if (arg[2] == 'E' || strncmp (&arg[2], "export", 6) == 0) |
+ export_flag = 1; |
+ else if (arg[2] == '6' && arg[3] == '4') |
+ aix64_flag = 1; |
+ else if (arg[2] == 'r' && arg[3] == 't' && arg[4] == 'l') |
+ aixrtl_flag = 1; |
+ } |
+#endif |
} |
vflag = debug; |
} |
@@ -901,10 +1302,11 @@ main (int argc, char **argv) |
obstack_free (&temporary_obstack, temporary_firstobj); |
/* -fno-profile-arcs -fno-test-coverage -fno-branch-probabilities |
- -fno-exceptions -w */ |
- num_c_args += 5; |
+ -fno-exceptions -w -fno-whole-program */ |
+ num_c_args += 6; |
- c_ptr = (const char **) (c_argv = XCNEWVEC (char *, num_c_args)); |
+ c_argv = XCNEWVEC (char *, num_c_args); |
+ c_ptr = CONST_CAST2 (const char **, char **, c_argv); |
if (argc < 2) |
fatal ("no arguments"); |
@@ -955,11 +1357,17 @@ main (int argc, char **argv) |
/* Search the compiler directories for `ld'. We have protection against |
recursive calls in find_a_file. */ |
if (ld_file_name == 0) |
- ld_file_name = find_a_file (&cpath, ld_suffix); |
+ ld_file_name = find_a_file (&cpath, |
+ use_plugin |
+ ? plugin_ld_suffix |
+ : ld_suffix); |
/* Search the ordinary system bin directories |
for `ld' (if native linking) or `TARGET-ld' (if cross). */ |
if (ld_file_name == 0) |
- ld_file_name = find_a_file (&path, full_ld_suffix); |
+ ld_file_name = find_a_file (&path, |
+ use_plugin |
+ ? full_plugin_ld_suffix |
+ : full_ld_suffix); |
#ifdef REAL_NM_FILE_NAME |
nm_file_name = find_a_file (&path, REAL_NM_FILE_NAME); |
@@ -1062,6 +1470,11 @@ main (int argc, char **argv) |
*c_ptr++ = xstrdup (q); |
} |
} |
+ if (use_verbose && *q == '-' && q[1] == 'v' && q[2] == 0) |
+ { |
+ /* Turn on trace in collect2 if needed. */ |
+ vflag = 1; |
+ } |
} |
obstack_free (&temporary_obstack, temporary_firstobj); |
*c_ptr++ = "-fno-profile-arcs"; |
@@ -1069,6 +1482,7 @@ main (int argc, char **argv) |
*c_ptr++ = "-fno-branch-probabilities"; |
*c_ptr++ = "-fno-exceptions"; |
*c_ptr++ = "-w"; |
+ *c_ptr++ = "-fno-whole-program"; |
/* !!! When GCC calls collect2, |
it does not know whether it is calling collect2 or ld. |
@@ -1095,19 +1509,6 @@ main (int argc, char **argv) |
{ |
switch (arg[1]) |
{ |
-#ifdef COLLECT_EXPORT_LIST |
- /* We want to disable automatic exports on AIX when user |
- explicitly puts an export list in command line */ |
- case 'b': |
- if (arg[2] == 'E' || strncmp (&arg[2], "export", 6) == 0) |
- export_flag = 1; |
- else if (arg[2] == '6' && arg[3] == '4') |
- aix64_flag = 1; |
- else if (arg[2] == 'r' && arg[3] == 't' && arg[4] == 'l') |
- aixrtl_flag = 1; |
- break; |
-#endif |
- |
case 'd': |
if (!strcmp (arg, "-debug")) |
{ |
@@ -1122,6 +1523,20 @@ main (int argc, char **argv) |
} |
break; |
+ case 'f': |
+ if (strcmp (arg, "-flto") == 0 || strcmp (arg, "-fwhopr") == 0) |
+ { |
+#ifdef ENABLE_LTO |
+ /* Do not pass LTO flag to the linker. */ |
+ ld1--; |
+ ld2--; |
+#else |
+ error ("LTO support has not been enabled in this " |
+ "configuration"); |
+#endif |
+ } |
+ break; |
+ |
case 'l': |
if (first_file) |
{ |
@@ -1150,7 +1565,9 @@ main (int argc, char **argv) |
#else |
#if LINK_ELIMINATE_DUPLICATE_LDIRECTORIES |
case 'L': |
- if (is_in_args (arg, (const char **) ld1_argv, ld1-1)) |
+ if (is_in_args (arg, |
+ CONST_CAST2 (const char **, char **, ld1_argv), |
+ ld1 - 1)) |
--ld1; |
break; |
#endif /* LINK_ELIMINATE_DUPLICATE_LDIRECTORIES */ |
@@ -1215,6 +1632,8 @@ main (int argc, char **argv) |
ld1--; |
ld2--; |
} |
+ else if (strncmp (arg, "--sysroot=", 10) == 0) |
+ target_system_root = arg + 10; |
break; |
} |
} |
@@ -1258,18 +1677,34 @@ main (int argc, char **argv) |
} |
/* The AIX linker will discard static constructors in object files if |
- nothing else in the file is referenced, so look at them first. */ |
+ nothing else in the file is referenced, so look at them first. Unless |
+ we are building a shared object, ignore the eh frame tables, as we |
+ would otherwise reference them all, hence drag all the corresponding |
+ objects even if nothing else is referenced. */ |
{ |
- const char **export_object_lst = (const char **)object_lst; |
+ const char **export_object_lst |
+ = CONST_CAST2 (const char **, char **, object_lst); |
- while (export_object_lst < object) |
- scan_prog_file (*export_object_lst++, PASS_OBJ); |
- } |
- { |
struct id *list = libs.first; |
+ /* Compute the filter to use from the current one, do scan, then adjust |
+ the "current" filter to remove what we just included here. This will |
+ control whether we need a first pass link later on or not, and what |
+ will remain to be scanned there. */ |
+ |
+ scanfilter this_filter = ld1_filter; |
+#if HAVE_AS_REF |
+ if (!shared_obj) |
+ this_filter &= ~SCAN_DWEH; |
+#endif |
+ |
+ while (export_object_lst < object) |
+ scan_prog_file (*export_object_lst++, PASS_OBJ, this_filter); |
+ |
for (; list; list = list->next) |
- scan_prog_file (list->name, PASS_FIRST); |
+ scan_prog_file (list->name, PASS_FIRST, this_filter); |
+ |
+ ld1_filter = ld1_filter & ~this_filter; |
} |
if (exports.first) |
@@ -1340,42 +1775,48 @@ main (int argc, char **argv) |
} |
/* Load the program, searching all libraries and attempting to provide |
- undefined symbols from repository information. */ |
- |
- /* On AIX we do this later. */ |
-#ifndef COLLECT_EXPORT_LIST |
- do_tlink (ld1_argv, object_lst); |
-#endif |
+ undefined symbols from repository information. |
- /* If -r or they will be run via some other method, do not build the |
+ If -r or they will be run via some other method, do not build the |
constructor or destructor list, just return now. */ |
- if (rflag |
-#ifndef COLLECT_EXPORT_LIST |
- || ! do_collecting |
-#endif |
- ) |
- { |
-#ifdef COLLECT_EXPORT_LIST |
- /* Do the link we avoided above if we are exiting. */ |
+ { |
+ bool early_exit |
+ = rflag || (! DO_COLLECT_EXPORT_LIST && ! do_collecting); |
+ |
+ /* Perform the first pass link now, if we're about to exit or if we need |
+ to scan for things we haven't collected yet before pursuing further. |
+ |
+ On AIX, the latter typically includes nothing for shared objects or |
+ frame tables for an executable, out of what the required early scan on |
+ objects and libraries has performed above. In the !shared_obj case, we |
+ expect the relevant tables to be dragged together with their associated |
+ functions from precise cross reference insertions by the compiler. */ |
+ |
+ if (early_exit || ld1_filter != SCAN_NOTHING) |
do_tlink (ld1_argv, object_lst); |
- /* But make sure we delete the export file we may have created. */ |
- if (export_file != 0 && export_file[0]) |
- maybe_unlink (export_file); |
+ if (early_exit) |
+ { |
+#ifdef COLLECT_EXPORT_LIST |
+ /* Make sure we delete the export file we may have created. */ |
+ if (export_file != 0 && export_file[0]) |
+ maybe_unlink (export_file); |
#endif |
- maybe_unlink (c_file); |
- maybe_unlink (o_file); |
- return 0; |
- } |
+ if (lto_mode != LTO_MODE_NONE) |
+ maybe_run_lto_and_relink (ld1_argv, object_lst, object, false); |
- /* Examine the namelist with nm and search it for static constructors |
- and destructors to call. |
- Write the constructor and destructor tables to a .s file and reload. */ |
+ maybe_unlink (c_file); |
+ maybe_unlink (o_file); |
+ return 0; |
+ } |
+ } |
- /* On AIX we already scanned for global constructors/destructors. */ |
-#ifndef COLLECT_EXPORT_LIST |
- scan_prog_file (output_file, PASS_FIRST); |
-#endif |
+ /* Unless we have done it all already, examine the namelist and search for |
+ static constructors and destructors to call. Write the constructor and |
+ destructor tables to a .s file and reload. */ |
+ |
+ if (ld1_filter != SCAN_NOTHING) |
+ scan_prog_file (output_file, PASS_FIRST, ld1_filter); |
#ifdef SCAN_LIBRARIES |
scan_libraries (output_file); |
@@ -1383,11 +1824,23 @@ main (int argc, char **argv) |
if (debug) |
{ |
- notice ("%d constructor(s) found\n", constructors.number); |
- notice ("%d destructor(s) found\n", destructors.number); |
- notice ("%d frame table(s) found\n", frame_tables.number); |
+ notice_translated (ngettext ("%d constructor found\n", |
+ "%d constructors found\n", |
+ constructors.number), |
+ constructors.number); |
+ notice_translated (ngettext ("%d destructor found\n", |
+ "%d destructors found\n", |
+ destructors.number), |
+ destructors.number); |
+ notice_translated (ngettext("%d frame table found\n", |
+ "%d frame tables found\n", |
+ frame_tables.number), |
+ frame_tables.number); |
} |
+ /* If the scan exposed nothing of special interest, there's no need to |
+ generate the glue code and relink so return now. */ |
+ |
if (constructors.number == 0 && destructors.number == 0 |
&& frame_tables.number == 0 |
#if defined (SCAN_LIBRARIES) || defined (COLLECT_EXPORT_LIST) |
@@ -1398,15 +1851,20 @@ main (int argc, char **argv) |
#endif |
) |
{ |
-#ifdef COLLECT_EXPORT_LIST |
- /* Do tlink without additional code generation. */ |
- do_tlink (ld1_argv, object_lst); |
-#endif |
+ /* Do tlink without additional code generation now if we didn't |
+ do it earlier for scanning purposes. */ |
+ if (ld1_filter == SCAN_NOTHING) |
+ do_tlink (ld1_argv, object_lst); |
+ |
+ if (lto_mode) |
+ maybe_run_lto_and_relink (ld1_argv, object_lst, object, false); |
+ |
/* Strip now if it was requested on the command line. */ |
if (strip_flag) |
{ |
char **real_strip_argv = XCNEWVEC (char *, 3); |
- const char ** strip_argv = (const char **) real_strip_argv; |
+ const char ** strip_argv = CONST_CAST2 (const char **, char **, |
+ real_strip_argv); |
strip_argv[0] = strip_file_name; |
strip_argv[1] = output_file; |
@@ -1494,13 +1952,19 @@ main (int argc, char **argv) |
#ifdef COLLECT_EXPORT_LIST |
/* On AIX we must call tlink because of possible templates resolution. */ |
do_tlink (ld2_argv, object_lst); |
+ |
+ if (lto_mode) |
+ maybe_run_lto_and_relink (ld2_argv, object_lst, object, false); |
#else |
/* Otherwise, simply call ld because tlink is already done. */ |
- fork_execute ("ld", ld2_argv); |
+ if (lto_mode) |
+ maybe_run_lto_and_relink (ld2_argv, object_lst, object, true); |
+ else |
+ fork_execute ("ld", ld2_argv); |
/* Let scan_prog_file do any final mods (OSF/rose needs this for |
constructors/destructors in shared libraries. */ |
- scan_prog_file (output_file, PASS_SECOND); |
+ scan_prog_file (output_file, PASS_SECOND, SCAN_ALL); |
#endif |
maybe_unlink (c_file); |
@@ -1564,7 +2028,7 @@ do_wait (const char *prog, struct pex_obj *pex) |
struct pex_obj * |
collect_execute (const char *prog, char **argv, const char *outname, |
- const char *errname) |
+ const char *errname, int flags) |
{ |
struct pex_obj *pex; |
const char *errmsg; |
@@ -1640,7 +2104,7 @@ collect_execute (const char *prog, char **argv, const char *outname, |
if (pex == NULL) |
fatal_perror ("pex_init failed"); |
- errmsg = pex_run (pex, PEX_LAST | PEX_SEARCH, argv[0], argv, outname, |
+ errmsg = pex_run (pex, flags, argv[0], argv, outname, |
errname, &err); |
if (errmsg != NULL) |
{ |
@@ -1664,7 +2128,7 @@ fork_execute (const char *prog, char **argv) |
{ |
struct pex_obj *pex; |
- pex = collect_execute (prog, argv, NULL, NULL); |
+ pex = collect_execute (prog, argv, NULL, NULL, PEX_LAST | PEX_SEARCH); |
do_wait (prog, pex); |
} |
@@ -1679,6 +2143,17 @@ maybe_unlink (const char *file) |
notice ("[Leaving %s]\n", file); |
} |
+/* Call maybe_unlink on the NULL-terminated list, FILE_LIST. */ |
+ |
+static void |
+maybe_unlink_list (char **file_list) |
+{ |
+ char **tmp = file_list; |
+ |
+ while (*tmp) |
+ maybe_unlink (*(tmp++)); |
+} |
+ |
static long sequence_number = 0; |
@@ -2073,34 +2548,71 @@ write_aix_file (FILE *stream, struct id *list) |
#ifdef OBJECT_FORMAT_NONE |
-/* Generic version to scan the name list of the loaded program for |
- the symbols g++ uses for static constructors and destructors. |
+/* Check to make sure the file is an LTO object file. */ |
- The constructor table begins at __CTOR_LIST__ and contains a count |
- of the number of pointers (or -1 if the constructors are built in a |
- separate section by the linker), followed by the pointers to the |
- constructor functions, terminated with a null pointer. The |
- destructor table has the same format, and begins at __DTOR_LIST__. */ |
+static bool |
+maybe_lto_object_file (const char *prog_name) |
+{ |
+ FILE *f; |
+ unsigned char buf[4]; |
+ int i; |
+ |
+ static unsigned char elfmagic[4] = { 0x7f, 'E', 'L', 'F' }; |
+ static unsigned char coffmagic[2] = { 0x4c, 0x01 }; |
+ static unsigned char machomagic[4][4] = { |
+ { 0xcf, 0xfa, 0xed, 0xfe }, |
+ { 0xce, 0xfa, 0xed, 0xfe }, |
+ { 0xfe, 0xed, 0xfa, 0xcf }, |
+ { 0xfe, 0xed, 0xfa, 0xce } |
+ }; |
+ |
+ f = fopen (prog_name, "rb"); |
+ if (f == NULL) |
+ return false; |
+ if (fread (buf, sizeof (buf), 1, f) != 1) |
+ buf[0] = 0; |
+ fclose (f); |
+ |
+ if (memcmp (buf, elfmagic, sizeof (elfmagic)) == 0 |
+ || memcmp (buf, coffmagic, sizeof (coffmagic)) == 0) |
+ return true; |
+ for (i = 0; i < 4; i++) |
+ if (memcmp (buf, machomagic[i], sizeof (machomagic[i])) == 0) |
+ return true; |
+ |
+ return false; |
+} |
+ |
+/* Generic version to scan the name list of the loaded program for |
+ the symbols g++ uses for static constructors and destructors. */ |
static void |
-scan_prog_file (const char *prog_name, enum pass which_pass) |
+scan_prog_file (const char *prog_name, scanpass which_pass, |
+ scanfilter filter) |
{ |
void (*int_handler) (int); |
#ifdef SIGQUIT |
void (*quit_handler) (int); |
#endif |
char *real_nm_argv[4]; |
- const char **nm_argv = (const char **) real_nm_argv; |
+ const char **nm_argv = CONST_CAST2 (const char **, char**, real_nm_argv); |
int argc = 0; |
struct pex_obj *pex; |
const char *errmsg; |
int err; |
char *p, buf[1024]; |
FILE *inf; |
+ int found_lto = 0; |
if (which_pass == PASS_SECOND) |
return; |
+ /* LTO objects must be in a known format. This check prevents |
+ us from accepting an archive containing LTO objects, which |
+ gcc cannnot currently handle. */ |
+ if (which_pass == PASS_LTOINFO && !maybe_lto_object_file (prog_name)) |
+ return; |
+ |
/* If we do not have an `nm', complain. */ |
if (nm_file_name == 0) |
fatal ("cannot find 'nm'"); |
@@ -2131,7 +2643,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
if (pex == NULL) |
fatal_perror ("pex_init failed"); |
- errmsg = pex_run (pex, 0, nm_file_name, real_nm_argv, NULL, NULL, &err); |
+ errmsg = pex_run (pex, 0, nm_file_name, real_nm_argv, NULL, HOST_BIT_BUCKET, |
+ &err); |
if (errmsg != NULL) |
{ |
if (err != 0) |
@@ -2153,7 +2666,12 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
fatal_perror ("can't open nm output"); |
if (debug) |
- fprintf (stderr, "\nnm output with constructors/destructors.\n"); |
+ { |
+ if (which_pass == PASS_LTOINFO) |
+ fprintf (stderr, "\nnm output with LTO info marker symbol.\n"); |
+ else |
+ fprintf (stderr, "\nnm output with constructors/destructors.\n"); |
+ } |
/* Read each line of nm output. */ |
while (fgets (buf, sizeof buf, inf) != (char *) 0) |
@@ -2161,8 +2679,36 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
int ch, ch2; |
char *name, *end; |
+ if (debug) |
+ fprintf (stderr, "\t%s\n", buf); |
+ |
+ if (which_pass == PASS_LTOINFO) |
+ { |
+ if (found_lto) |
+ continue; |
+ |
+ /* Look for the LTO info marker symbol, and add filename to |
+ the LTO objects list if found. */ |
+ for (p = buf; (ch = *p) != '\0' && ch != '\n'; p++) |
+ if (ch == ' ' && p[1] == '_' && p[2] == '_' |
+ && (strncmp (p + (p[3] == '_' ? 2 : 1), "__gnu_lto_v1", 12) == 0) |
+ && ISSPACE (p[p[3] == '_' ? 14 : 13])) |
+ { |
+ add_lto_object (<o_objects, prog_name); |
+ |
+ /* We need to read all the input, so we can't just |
+ return here. But we can avoid useless work. */ |
+ found_lto = 1; |
+ |
+ break; |
+ } |
+ |
+ continue; |
+ } |
+ |
/* If it contains a constructor or destructor name, add the name |
- to the appropriate list. */ |
+ to the appropriate list unless this is a kind of symbol we're |
+ not supposed to even consider. */ |
for (p = buf; (ch = *p) != '\0' && ch != '\n' && ch != '_'; p++) |
if (ch == ' ' && p[1] == 'U' && p[2] == ' ') |
@@ -2183,16 +2729,22 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
switch (is_ctor_dtor (name)) |
{ |
case SYM_CTOR: |
+ if (! (filter & SCAN_CTOR)) |
+ break; |
if (which_pass != PASS_LIB) |
add_to_list (&constructors, name); |
break; |
case SYM_DTOR: |
+ if (! (filter & SCAN_DTOR)) |
+ break; |
if (which_pass != PASS_LIB) |
add_to_list (&destructors, name); |
break; |
case SYM_INIT: |
+ if (! (filter & SCAN_INIT)) |
+ break; |
if (which_pass != PASS_LIB) |
fatal ("init function found in object %s", prog_name); |
#ifndef LD_INIT_SWITCH |
@@ -2201,6 +2753,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
break; |
case SYM_FINI: |
+ if (! (filter & SCAN_FINI)) |
+ break; |
if (which_pass != PASS_LIB) |
fatal ("fini function found in object %s", prog_name); |
#ifndef LD_FINI_SWITCH |
@@ -2209,6 +2763,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
break; |
case SYM_DWEH: |
+ if (! (filter & SCAN_DWEH)) |
+ break; |
if (which_pass != PASS_LIB) |
add_to_list (&frame_tables, name); |
break; |
@@ -2216,9 +2772,6 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
default: /* not a constructor or destructor */ |
continue; |
} |
- |
- if (debug) |
- fprintf (stderr, "\t%s\n", buf); |
} |
if (debug) |
@@ -2248,7 +2801,7 @@ scan_libraries (const char *prog_name) |
void (*quit_handler) (int); |
#endif |
char *real_ldd_argv[4]; |
- const char **ldd_argv = (const char **) real_ldd_argv; |
+ const char **ldd_argv = CONST_CAST2 (const char **, char **, real_ldd_argv); |
int argc = 0; |
struct pex_obj *pex; |
const char *errmsg; |
@@ -2353,7 +2906,7 @@ scan_libraries (const char *prog_name) |
/* Now iterate through the library list adding their symbols to |
the list. */ |
for (list = libraries.first; list; list = list->next) |
- scan_prog_file (list->name, PASS_LIB); |
+ scan_prog_file (list->name, PASS_LIB, SCAN_ALL); |
} |
#endif /* LDD_SUFFIX */ |
@@ -2404,7 +2957,7 @@ scan_libraries (const char *prog_name) |
# define GCC_SYMZERO(X) 0 |
/* 0757 = U803XTOCMAGIC (AIX 4.3) and 0767 = U64_TOCMAGIC (AIX V5) */ |
-#ifdef _AIX51 |
+#if TARGET_AIX_VERSION >= 51 |
# define GCC_CHECK_HDR(X) \ |
((HEADER (X).f_magic == U802TOCMAGIC && ! aix64_flag) \ |
|| (HEADER (X).f_magic == 0767 && aix64_flag)) |
@@ -2443,9 +2996,19 @@ static int ignore_library (const char *); |
static int |
ignore_library (const char *name) |
{ |
- const char *const *p = &aix_std_libs[0]; |
- while (*p++ != NULL) |
- if (! strcmp (name, *p)) return 1; |
+ const char *const *p; |
+ size_t length; |
+ |
+ if (target_system_root[0] != '\0') |
+ { |
+ length = strlen (target_system_root); |
+ if (strncmp (name, target_system_root, length) != 0) |
+ return 0; |
+ name += length; |
+ } |
+ for (p = &aix_std_libs[0]; *p != NULL; ++p) |
+ if (strcmp (name, *p) == 0) |
+ return 1; |
return 0; |
} |
#endif /* COLLECT_EXPORT_LIST */ |
@@ -2455,16 +3018,11 @@ extern char *ldgetname (LDFILE *, GCC_SYMENT *); |
#endif |
/* COFF version to scan the name list of the loaded program for |
- the symbols g++ uses for static constructors and destructors. |
- |
- The constructor table begins at __CTOR_LIST__ and contains a count |
- of the number of pointers (or -1 if the constructors are built in a |
- separate section by the linker), followed by the pointers to the |
- constructor functions, terminated with a null pointer. The |
- destructor table has the same format, and begins at __DTOR_LIST__. */ |
+ the symbols g++ uses for static constructors and destructors. */ |
static void |
-scan_prog_file (const char *prog_name, enum pass which_pass) |
+scan_prog_file (const char *prog_name, scanpass which_pass, |
+ scanfilter filter) |
{ |
LDFILE *ldptr = NULL; |
int sym_index, sym_count; |
@@ -2528,6 +3086,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
switch (is_ctor_dtor (name)) |
{ |
case SYM_CTOR: |
+ if (! (filter & SCAN_CTOR)) |
+ break; |
if (! is_shared) |
add_to_list (&constructors, name); |
#if defined (COLLECT_EXPORT_LIST) && !defined (LD_INIT_SWITCH) |
@@ -2537,6 +3097,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
break; |
case SYM_DTOR: |
+ if (! (filter & SCAN_DTOR)) |
+ break; |
if (! is_shared) |
add_to_list (&destructors, name); |
#if defined (COLLECT_EXPORT_LIST) && !defined (LD_INIT_SWITCH) |
@@ -2547,6 +3109,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
#ifdef COLLECT_EXPORT_LIST |
case SYM_INIT: |
+ if (! (filter & SCAN_INIT)) |
+ break; |
#ifndef LD_INIT_SWITCH |
if (is_shared) |
add_to_list (&constructors, name); |
@@ -2554,6 +3118,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
break; |
case SYM_FINI: |
+ if (! (filter & SCAN_FINI)) |
+ break; |
#ifndef LD_INIT_SWITCH |
if (is_shared) |
add_to_list (&destructors, name); |
@@ -2562,6 +3128,8 @@ scan_prog_file (const char *prog_name, enum pass which_pass) |
#endif |
case SYM_DWEH: |
+ if (! (filter & SCAN_DWEH)) |
+ break; |
if (! is_shared) |
add_to_list (&frame_tables, name); |
#if defined (COLLECT_EXPORT_LIST) && !defined (LD_INIT_SWITCH) |