]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/collect2.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / collect2.c
index 5a7e874707538ed9bcdf535c1a0cc726d3878e54..cf04a58ba4dd9d36be2df4c20d1aaf3eec6c0424 100644 (file)
@@ -1,6 +1,6 @@
 /* Collect static initialization info into data structures that can be
    traversed by C++ initialization and finalization routines.
-   Copyright (C) 1992-2015 Free Software Foundation, Inc.
+   Copyright (C) 1992-2021 Free Software Foundation, Inc.
    Contributed by Chris Smith (csmith@convex.com).
    Heavily modified by Michael Meissner (meissner@cygnus.com),
    Per Bothner (bothner@cygnus.com), and John Gilmore (gnu@cygnus.com).
@@ -30,6 +30,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "filenames.h"
 #include "file-find.h"
+#include "simple-object.h"
+#include "lto-section-names.h"
 
 /* TARGET_64BIT may be defined to use driver specific functionality. */
 #undef TARGET_64BIT
@@ -182,7 +184,8 @@ static int strip_flag;                      /* true if -s */
 static int export_flag;                 /* true if -bE */
 static int aix64_flag;                 /* true if -b64 */
 static int aixrtl_flag;                        /* true if -brtl */
-static int aixlazy_flag;               /* true if -blazy */
+static int aixlazy_flag;               /* true if -blazy */
+static int visibility_flag;            /* true if -fvisibility */
 #endif
 
 enum lto_mode_d {
@@ -201,6 +204,7 @@ static enum lto_mode_d lto_mode = LTO_MODE_NONE;
 bool helpflag;                 /* true if --help */
 
 static int shared_obj;                 /* true if -shared */
+static int static_obj;                 /* true if -static */
 
 static const char *c_file;             /* <xxx>.c for constructor/destructor list.  */
 static const char *o_file;             /* <xxx>.o for constructor/destructor list.  */
@@ -208,8 +212,6 @@ static const char *o_file;          /* <xxx>.o for constructor/destructor 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.  */
 static const char *nm_file_name;       /* pathname of nm */
 #ifdef LDD_SUFFIX
@@ -255,6 +257,7 @@ bool may_unlink_output_file = false;
 #ifdef COLLECT_EXPORT_LIST
 /* Lists to keep libraries to be scanned for global constructors/destructors.  */
 static struct head libs;                    /* list of libraries */
+static struct head static_libs;             /* list of statically linked libraries */
 static struct path_prefix cmdline_lib_dirs; /* directories specified with -L */
 static struct path_prefix libpath_lib_dirs; /* directories in LIBPATH */
 static struct path_prefix *libpaths[3] = {&cmdline_lib_dirs,
@@ -298,7 +301,6 @@ const char tool_name[] = "collect2";
 
 static symkind is_ctor_dtor (const char *);
 
-static void handler (int);
 static void maybe_unlink_list (char **);
 static void add_to_list (struct head *, const char *);
 static int extract_init_priority (const char *);
@@ -320,9 +322,7 @@ static void write_c_file_glob (FILE *, const char *);
 static void scan_libraries (const char *);
 #endif
 #ifdef COLLECT_EXPORT_LIST
-#if 0
 static int is_in_list (const char *, struct id *);
-#endif
 static void write_aix_file (FILE *, struct id *);
 static char *resolve_lib_name (const char *);
 #endif
@@ -382,6 +382,10 @@ static void scan_prog_file (const char *, scanpass, scanfilter);
 void
 tool_cleanup (bool from_signal)
 {
+  /* maybe_unlink may call notice, which is not signal safe.  */
+  if (from_signal)
+    verbose = false;
+
   if (c_file != 0 && c_file[0])
     maybe_unlink (c_file);
 
@@ -395,20 +399,6 @@ tool_cleanup (bool from_signal)
 
   if (lto_o_files)
     maybe_unlink_list (lto_o_files);
-
-  if (ldout != 0 && ldout[0])
-    {
-      if (!from_signal)
-       dump_ld_file (ldout, stdout);
-      maybe_unlink (ldout);
-    }
-
-  if (lderrout != 0 && lderrout[0])
-    {
-      if (!from_signal)
-       dump_ld_file (lderrout, stderr);
-      maybe_unlink (lderrout);
-    }
 }
 
 static void
@@ -417,14 +407,6 @@ collect_atexit (void)
   tool_cleanup (false);
 }
 
-static void
-handler (int signo)
-{
-  tool_cleanup (true);
-
-  signal (signo, SIG_DFL);
-  raise (signo);
-}
 /* Notify user of a non-error, without translating the format string.  */
 void
 notice_translated (const char *cmsgid, ...)
@@ -474,77 +456,6 @@ extract_string (const char **pp)
   return XOBFINISH (&temporary_obstack, char *);
 }
 \f
-void
-dump_ld_file (const char *name, FILE *to)
-{
-  FILE *stream = fopen (name, "r");
-
-  if (stream == 0)
-    return;
-  while (1)
-    {
-      int c;
-      while (c = getc (stream),
-            c != EOF && (ISIDNUM (c) || c == '$' || c == '.'))
-       obstack_1grow (&temporary_obstack, c);
-      if (obstack_object_size (&temporary_obstack) > 0)
-       {
-         const char *word, *p;
-         char *result;
-         obstack_1grow (&temporary_obstack, '\0');
-         word = XOBFINISH (&temporary_obstack, const char *);
-
-         if (*word == '.')
-           ++word, putc ('.', to);
-         p = word;
-         if (!strncmp (p, USER_LABEL_PREFIX, strlen (USER_LABEL_PREFIX)))
-           p += strlen (USER_LABEL_PREFIX);
-
-#ifdef HAVE_LD_DEMANGLE
-         result = 0;
-#else
-         if (no_demangle)
-           result = 0;
-         else
-           result = cplus_demangle (p, DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE);
-#endif
-
-         if (result)
-           {
-             int diff;
-             fputs (result, to);
-
-             diff = strlen (word) - strlen (result);
-             while (diff > 0 && c == ' ')
-               --diff, putc (' ', to);
-             if (diff < 0 && c == ' ')
-               {
-                 while (diff < 0 && c == ' ')
-                   ++diff, c = getc (stream);
-                 if (!ISSPACE (c))
-                   {
-                     /* Make sure we output at least one space, or
-                        the demangled symbol name will run into
-                        whatever text follows.  */
-                     putc (' ', to);
-                   }
-               }
-
-             free (result);
-           }
-         else
-           fputs (word, to);
-
-         fflush (to);
-         obstack_free (&temporary_obstack, temporary_firstobj);
-       }
-      if (c == EOF)
-       break;
-      putc (c, to);
-    }
-  fclose (stream);
-}
-\f
 /* Return the kind of symbol denoted by name S.  */
 
 static symkind
@@ -699,7 +610,8 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
       size_t num_files;
 
       if (!lto_wrapper)
-       fatal_error (input_location, "COLLECT_LTO_WRAPPER must be set");
+       fatal_error (input_location, "environment variable "
+                    "%<COLLECT_LTO_WRAPPER%> must be set");
 
       num_lto_c_args++;
 
@@ -723,7 +635,7 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
 
       /* Run the LTO back end.  */
       pex = collect_execute (prog, lto_c_argv, NULL, NULL, PEX_SEARCH,
-                            at_file_supplied);
+                            at_file_supplied, "lto_args");
       {
        int c;
        FILE *stream;
@@ -741,7 +653,10 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
              ++num_files;
          }
 
-       lto_o_files = XNEWVEC (char *, num_files + 1);
+       /* signal handler may access uninitialized memory
+          and delete whatever it points to, if lto_o_files
+          is not allocated with calloc.  */
+       lto_o_files = XCNEWVEC (char *, num_files + 1);
        lto_o_files[num_files] = NULL;
        start = XOBFINISH (&temporary_obstack, char *);
        for (i = 0; i < num_files; ++i)
@@ -761,7 +676,7 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
       do_wait (prog, pex);
       pex = NULL;
 
-      /* Compute memory needed for new LD arguments.  At most number of original arguemtns
+      /* Compute memory needed for new LD arguments.  At most number of original arguments
         plus number of partitions.  */
       for (lto_ld_argv_size = 0; lto_ld_argv[lto_ld_argv_size]; lto_ld_argv_size++)
        ;
@@ -803,8 +718,11 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
 
       /* 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", out_lto_ld_argv, HAVE_GNU_LD && at_file_supplied);
-      post_ld_pass (true);
+      fork_execute ("ld", out_lto_ld_argv, HAVE_GNU_LD && at_file_supplied,
+                   "ld_args");
+      /* We assume that temp files were created, and therefore we need to take
+         that into account (maybe run dsymutil).  */
+      post_ld_pass (/*temp_file*/true);
       free (lto_ld_argv);
 
       maybe_unlink_list (lto_o_files);
@@ -813,11 +731,37 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
     {
       /* 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, HAVE_GNU_LD && at_file_supplied);
-      post_ld_pass (false);
+      fork_execute ("ld", lto_ld_argv, HAVE_GNU_LD && at_file_supplied,
+                   "ld_args");
+      /* No LTO objects were found, so no new temp file.  */
+      post_ld_pass (/*temp_file*/false);
+    }
+  else
+    post_ld_pass (false); /* No LTO objects were found, no temp file.  */
+}
+/* Entry point for linker invoation.  Called from main in collect2.c.
+   LD_ARGV is an array of arguments for the linker.  */
+
+static void
+do_link (char **ld_argv, const char *atsuffix)
+{
+  struct pex_obj *pex;
+  const char *prog = "ld";
+  pex = collect_execute (prog, ld_argv, NULL, NULL,
+                        PEX_LAST | PEX_SEARCH,
+                        HAVE_GNU_LD && at_file_supplied, atsuffix);
+  int ret = collect_wait (prog, pex);
+  if (ret)
+    {
+      error ("ld returned %d exit status", ret);
+      exit (ret);
     }
   else
-    post_ld_pass (true);
+    {
+      /* We have just successfully produced an output file, so assume that we
+        may unlink it if need be for now on.  */
+      may_unlink_output_file = true;
+    }
 }
 \f
 /* Main program.  */
@@ -831,6 +775,7 @@ main (int argc, char **argv)
       USE_PLUGIN_LD,
       USE_GOLD_LD,
       USE_BFD_LD,
+      USE_LLD_LD,
       USE_LD_MAX
     } selected_linker = USE_DEFAULT_LD;
   static const char *const ld_suffixes[USE_LD_MAX] =
@@ -838,7 +783,8 @@ main (int argc, char **argv)
       "ld",
       PLUGIN_LD_SUFFIX,
       "ld.gold",
-      "ld.bfd"
+      "ld.bfd",
+      "ld.lld"
     };
   static const char *const real_ld_suffix = "real-ld";
   static const char *const collect_ld_suffix = "collect-ld";
@@ -911,6 +857,9 @@ main (int argc, char **argv)
   int first_file;
   int num_c_args;
   char **old_argv;
+#ifdef COLLECT_EXPORT_LIST
+  bool is_static = false;
+#endif
   int i;
 
   for (i = 0; i < USE_LD_MAX; i++)
@@ -949,11 +898,7 @@ main (int argc, char **argv)
   COLLECT2_HOST_INITIALIZATION;
 #endif
 
-#ifdef SIGCHLD
-  /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
-     receive the signal.  A different setting is inheritable */
-  signal (SIGCHLD, SIG_DFL);
-#endif
+  setup_signals ();
 
   /* Unlock the stdio streams.  */
   unlock_std_streams ();
@@ -976,13 +921,21 @@ main (int argc, char **argv)
   object = CONST_CAST2 (const char **, char **, object_lst);
 
 #ifdef DEBUG
-  debug = 1;
+  debug = true;
+#endif
+
+  save_temps = false;
+  verbose = false;
+
+#ifndef DEFAULT_A_OUT_NAME
+  output_file = "a.out";
+#else
+  output_file = DEFAULT_A_OUT_NAME;
 #endif
 
-  /* Parse command line early for instances of -debug.  This allows
-     the debug flag to be set before functions like find_a_file()
-     are called.  We also look for the -flto or -flto-partition=none flag to know
-     what LTO mode we are in.  */
+  /* Parse command line / environment for flags we want early.
+     This allows the debug flag to be set before functions like find_a_file()
+     are called. */
   {
     bool no_partition = false;
 
@@ -990,9 +943,7 @@ main (int argc, char **argv)
       {
        if (! strcmp (argv[i], "-debug"))
          debug = true;
-        else if (! strcmp (argv[i], "-flto-partition=none"))
-         no_partition = true;
-       else if (!strncmp (argv[i], "-fno-lto", 8))
+       else if (startswith (argv[i], "-fno-lto"))
          lto_mode = LTO_MODE_NONE;
         else if (! strcmp (argv[i], "-plugin"))
          {
@@ -1004,54 +955,78 @@ main (int argc, char **argv)
          selected_linker = USE_BFD_LD;
        else if (strcmp (argv[i], "-fuse-ld=gold") == 0)
          selected_linker = USE_GOLD_LD;
+       else if (strcmp (argv[i], "-fuse-ld=lld") == 0)
+         selected_linker = USE_LLD_LD;
+       else if (startswith (argv[i], "-o"))
+         {
+           /* Parse the output filename if it's given so that we can make
+              meaningful temp filenames.  */
+           if (argv[i][2] != '\0')
+             output_file = &argv[i][2];
+           else if (argv[i+1] != NULL)
+             output_file = argv[++i];
+         }
 
 #ifdef COLLECT_EXPORT_LIST
        /* These flags are position independent, although their order
           is important - subsequent flags override earlier ones. */
        else if (strcmp (argv[i], "-b64") == 0)
-           aix64_flag = 1;
+         aix64_flag = 1;
        /* -bexport:filename always needs the :filename */
-       else if (strncmp (argv[i], "-bE:", 4) == 0
-             || strncmp (argv[i], "-bexport:", 9) == 0)
-           export_flag = 1;
+       else if (startswith (argv[i], "-bE:")
+                || startswith (argv[i], "-bexport:"))
+         export_flag = 1;
        else if (strcmp (argv[i], "-brtl") == 0
              || strcmp (argv[i], "-bsvr4") == 0
              || strcmp (argv[i], "-G") == 0)
-           aixrtl_flag = 1;
+         aixrtl_flag = 1;
        else if (strcmp (argv[i], "-bnortl") == 0)
-           aixrtl_flag = 0;
+         aixrtl_flag = 0;
        else if (strcmp (argv[i], "-blazy") == 0)
-           aixlazy_flag = 1;
+         aixlazy_flag = 1;
 #endif
       }
-    verbose = debug;
-    find_file_set_debug (debug);
-    if (use_plugin)
-      lto_mode = LTO_MODE_NONE;
-    if (no_partition && lto_mode == LTO_MODE_WHOPR)
-      lto_mode = LTO_MODE_LTO;
-  }
 
-#ifndef DEFAULT_A_OUT_NAME
-  output_file = "a.out";
-#else
-  output_file = DEFAULT_A_OUT_NAME;
-#endif
-
-  obstack_begin (&temporary_obstack, 0);
-  temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0);
+    obstack_begin (&temporary_obstack, 0);
+    temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0);
 
 #ifndef HAVE_LD_DEMANGLE
   current_demangling_style = auto_demangling;
 #endif
-  p = getenv ("COLLECT_GCC_OPTIONS");
-  while (p && *p)
-    {
-      const char *q = extract_string (&p);
-      if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
-       num_c_args++;
+
+    /* Now pick up any flags we want early from COLLECT_GCC_OPTIONS
+       The LTO options are passed here as are other options that might
+       be unsuitable for ld (e.g. -save-temps).  */
+    p = getenv ("COLLECT_GCC_OPTIONS");
+    while (p && *p)
+      {
+       const char *q = extract_string (&p);
+       if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
+         num_c_args++;
+       if (startswith (q, "-flto-partition=none"))
+         no_partition = true;
+       else if (startswith (q, "-fno-lto"))
+         lto_mode = LTO_MODE_NONE;
+       else if (startswith (q, "-save-temps"))
+         /* FIXME: Honour =obj.  */
+         save_temps = true;
+       else if (strcmp (q, "-dumpdir") == 0)
+         dumppfx = xstrdup (extract_string (&p));
+       else if (strcmp (q, "-o") == 0
+                || strcmp (q, "-B") == 0
+                || strcmp (q, "-isystem") == 0)
+         (void) extract_string (&p);
     }
-  obstack_free (&temporary_obstack, temporary_firstobj);
+    obstack_free (&temporary_obstack, temporary_firstobj);
+
+    verbose = verbose || debug;
+    save_temps = save_temps || debug;
+    find_file_set_debug (debug);
+    if (use_plugin)
+      lto_mode = LTO_MODE_NONE;
+    if (no_partition && lto_mode == LTO_MODE_WHOPR)
+      lto_mode = LTO_MODE_LTO;
+  }
 
   /* -fno-profile-arcs -fno-test-coverage -fno-branch-probabilities
      -fno-exceptions -w -fno-whole-program */
@@ -1063,27 +1038,6 @@ main (int argc, char **argv)
   if (argc < 2)
     fatal_error (input_location, "no arguments");
 
-#ifdef SIGQUIT
-  if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
-    signal (SIGQUIT, handler);
-#endif
-  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
-    signal (SIGINT, handler);
-#ifdef SIGALRM
-  if (signal (SIGALRM, SIG_IGN) != SIG_IGN)
-    signal (SIGALRM, handler);
-#endif
-#ifdef SIGHUP
-  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
-    signal (SIGHUP, handler);
-#endif
-  if (signal (SIGSEGV, SIG_IGN) != SIG_IGN)
-    signal (SIGSEGV, handler);
-#ifdef SIGBUS
-  if (signal (SIGBUS, SIG_IGN) != SIG_IGN)
-    signal (SIGBUS, handler);
-#endif
-
   /* Extract COMPILER_PATH and PATH into our prefix list.  */
   prefix_from_env ("COMPILER_PATH", &cpath);
   prefix_from_env ("PATH", &path);
@@ -1093,7 +1047,8 @@ main (int argc, char **argv)
   /* Maybe we know the right file to use (if not cross).  */
   ld_file_name = 0;
 #ifdef DEFAULT_LINKER
-  if (selected_linker == USE_BFD_LD || selected_linker == USE_GOLD_LD)
+  if (selected_linker == USE_BFD_LD || selected_linker == USE_GOLD_LD ||
+      selected_linker == USE_LLD_LD)
     {
       char *linker_name;
 # ifdef HOST_EXECUTABLE_SUFFIX
@@ -1200,16 +1155,23 @@ main (int argc, char **argv)
   *ld1++ = *ld2++ = ld_file_name;
 
   /* Make temp file names.  */
-  c_file = make_temp_file (".c");
-  o_file = make_temp_file (".o");
+  if (save_temps)
+    {
+      c_file = concat (output_file, ".cdtor.c", NULL);
+      o_file = concat (output_file, ".cdtor.o", NULL);
 #ifdef COLLECT_EXPORT_LIST
-  export_file = make_temp_file (".x");
+      export_file = concat (output_file, ".x", NULL);
 #endif
-  if (!debug)
+    }
+  else
     {
-      ldout = make_temp_file (".ld");
-      lderrout = make_temp_file (".le");
+      c_file = make_temp_file (".cdtor.c");
+      o_file = make_temp_file (".cdtor.o");
+#ifdef COLLECT_EXPORT_LIST
+      export_file = make_temp_file (".x");
+#endif
     }
+  /* Build the command line to compile the ctor/dtor list.  */
   *c_ptr++ = c_file_name;
   *c_ptr++ = "-x";
   *c_ptr++ = "c";
@@ -1241,6 +1203,8 @@ main (int argc, char **argv)
        *c_ptr++ = xstrdup (q);
       if (strcmp (q, "-shared") == 0)
        shared_obj = 1;
+      if (strcmp (q, "-static") == 0)
+       static_obj = 1;
       if (*q == '-' && q[1] == 'B')
        {
          *c_ptr++ = xstrdup (q);
@@ -1250,6 +1214,15 @@ main (int argc, char **argv)
              *c_ptr++ = xstrdup (q);
            }
        }
+      else if (strcmp (q, "-o") == 0
+              || strcmp (q, "-dumpdir") == 0
+              || strcmp (q, "-isystem") == 0)
+       (void) extract_string (&p);
+#ifdef COLLECT_EXPORT_LIST
+      /* Detect any invocation with -fvisibility.  */
+      if (startswith (q, "-fvisibility"))
+       visibility_flag = 1;
+#endif
     }
   obstack_free (&temporary_obstack, temporary_firstobj);
   *c_ptr++ = "-fno-profile-arcs";
@@ -1269,6 +1242,9 @@ main (int argc, char **argv)
   /* Parse arguments.  Remember output file spec, pass the rest to ld.  */
   /* After the first file, put in the c++ rt0.  */
 
+#ifdef COLLECT_EXPORT_LIST
+  is_static = static_obj;
+#endif
   first_file = 1;
   while ((arg = *++argv) != (char *) 0)
     {
@@ -1293,7 +1269,7 @@ main (int argc, char **argv)
              break;
 
             case 'f':
-             if (strncmp (arg, "-flto", 5) == 0)
+             if (startswith (arg, "-flto"))
                {
 #ifdef ENABLE_LTO
                  /* Do not pass LTO flag to the linker. */
@@ -1305,13 +1281,13 @@ main (int argc, char **argv)
 #endif
                }
              else if (!use_collect_ld
-                      && strncmp (arg, "-fuse-ld=", 9) == 0)
+                      && startswith (arg, "-fuse-ld="))
                {
-                 /* Do not pass -fuse-ld={bfd|gold} to the linker. */
+                 /* Do not pass -fuse-ld={bfd|gold|lld} to the linker. */
                  ld1--;
                  ld2--;
                }
-             else if (strncmp (arg, "-fno-lto", 8) == 0)
+             else if (startswith (arg, "-fno-lto"))
                {
                  /* Do not pass -fno-lto to the linker. */
                  ld1--;
@@ -1341,7 +1317,7 @@ main (int argc, char **argv)
 
                  stream = fopen (list_filename, "r");
                  if (stream == NULL)
-                   fatal_error (input_location, "can't open %s: %m",
+                   fatal_error (input_location, "cannot open %s: %m",
                                 list_filename);
 
                  while (fgets (buf, sizeof buf, stream) != NULL)
@@ -1374,6 +1350,18 @@ main (int argc, char **argv)
 #endif
               break;
 
+#ifdef COLLECT_EXPORT_LIST
+           case 'b':
+             if (!strcmp (arg, "-bstatic"))
+               {
+                 is_static = true;
+               }
+             else if (!strcmp (arg, "-bdynamic") || !strcmp (arg, "-bshared"))
+               {
+                 is_static = false;
+               }
+             break;
+#endif
            case 'l':
              if (first_file)
                {
@@ -1390,6 +1378,8 @@ main (int argc, char **argv)
 
                /* Saving a full library name.  */
                add_to_list (&libs, s);
+               if (is_static)
+                   add_to_list (&static_libs, s);
              }
 #endif
              break;
@@ -1402,10 +1392,10 @@ main (int argc, char **argv)
 #endif
 
            case 'o':
-             if (arg[2] == '\0')
-               output_file = *ld1++ = *ld2++ = *++argv;
-             else
+             if (arg[2] != '\0')
                output_file = &arg[2];
+             else if (argv[1])
+               output_file = *ld1++ = *ld2++ = *++argv;
              break;
 
            case 'r':
@@ -1438,7 +1428,7 @@ main (int argc, char **argv)
                  ld2--;
 #endif
                }
-             else if (strncmp (arg, "--demangle", 10) == 0)
+             else if (startswith (arg, "--demangle"))
                {
 #ifndef HAVE_LD_DEMANGLE
                  no_demangle = 0;
@@ -1447,7 +1437,7 @@ main (int argc, char **argv)
                      enum demangling_styles style
                        = cplus_demangle_name_to_style (arg+11);
                      if (style == unknown_demangling)
-                       error ("unknown demangling style '%s'", arg+11);
+                       error ("unknown demangling style %qs", arg+11);
                      else
                        current_demangling_style = style;
                    }
@@ -1455,7 +1445,7 @@ main (int argc, char **argv)
                  ld2--;
 #endif
                }
-             else if (strncmp (arg, "--sysroot=", 10) == 0)
+             else if (startswith (arg, "--sysroot="))
                target_system_root = arg + 10;
              else if (strcmp (arg, "--version") == 0)
                verbose = true;
@@ -1490,6 +1480,8 @@ main (int argc, char **argv)
            {
              /* Saving a full library name.  */
              add_to_list (&libs, arg);
+             if (is_static)
+               add_to_list (&static_libs, arg);
            }
 #endif
        }
@@ -1501,6 +1493,8 @@ main (int argc, char **argv)
     {
       fprintf (stderr, "List of libraries:\n");
       dump_list (stderr, "\t", libs.first);
+      fprintf (stderr, "List of statically linked libraries:\n");
+      dump_list (stderr, "\t", static_libs.first);
     }
 
   /* The AIX linker will discard static constructors in object files if
@@ -1525,9 +1519,11 @@ main (int argc, char **argv)
       this_filter &= ~SCAN_DWEH;
 #endif
 
+    /* Scan object files.  */
     while (export_object_lst < object)
       scan_prog_file (*export_object_lst++, PASS_OBJ, this_filter);
 
+    /* Scan libraries.  */
     for (; list; list = list->next)
       scan_prog_file (list->name, PASS_FIRST, this_filter);
 
@@ -1565,7 +1561,7 @@ main (int argc, char **argv)
       printf ("  --help          Display this information\n");
       printf ("  -v, --version   Display this program's version number\n");
       printf ("\n");
-      printf ("Overview: http://gcc.gnu.org/onlinedocs/gccint/Collect2.html\n");
+      printf ("Overview: https://gcc.gnu.org/onlinedocs/gccint/Collect2.html\n");
       printf ("Report bugs: %s\n", bug_report_url);
       printf ("\n");
     }
@@ -1628,7 +1624,7 @@ main (int argc, char **argv)
        functions from precise cross reference insertions by the compiler.  */
 
     if (early_exit || ld1_filter != SCAN_NOTHING)
-      do_tlink (ld1_argv, object_lst);
+      do_link (ld1_argv, "ld1_args");
 
     if (early_exit)
       {
@@ -1640,10 +1636,8 @@ main (int argc, char **argv)
        if (lto_mode != LTO_MODE_NONE)
          maybe_run_lto_and_relink (ld1_argv, object_lst, object, false);
        else
-         post_ld_pass (false);
+         post_ld_pass (/*temp_file*/false);
 
-       maybe_unlink (c_file);
-       maybe_unlink (o_file);
        return 0;
       }
   }
@@ -1688,10 +1682,10 @@ main (int argc, char **argv)
 #endif
       )
     {
-      /* Do tlink without additional code generation now if we didn't
+      /* Do link 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);
+       do_link (ld1_argv, "ld1_args");
 
       if (lto_mode)
         maybe_run_lto_and_relink (ld1_argv, object_lst, object, false);
@@ -1706,16 +1700,13 @@ main (int argc, char **argv)
          strip_argv[0] = strip_file_name;
          strip_argv[1] = output_file;
          strip_argv[2] = (char *) 0;
-         fork_execute ("strip", real_strip_argv, false);
+         fork_execute ("strip", real_strip_argv, false, NULL);
        }
 
 #ifdef COLLECT_EXPORT_LIST
       maybe_unlink (export_file);
 #endif
-      post_ld_pass (false);
-
-      maybe_unlink (c_file);
-      maybe_unlink (o_file);
+      post_ld_pass (/*temp_file*/false);
       return 0;
     }
 
@@ -1792,21 +1783,21 @@ main (int argc, char **argv)
   /* Assemble the constructor and destructor tables.
      Link the tables in with the rest of the program.  */
 
-  fork_execute ("gcc",  c_argv, at_file_supplied);
+  fork_execute ("gcc",  c_argv, at_file_supplied, "gcc_args");
 #ifdef COLLECT_EXPORT_LIST
-  /* On AIX we must call tlink because of possible templates resolution.  */
-  do_tlink (ld2_argv, object_lst);
+  /* On AIX we must call link because of possible templates resolution.  */
+  do_link (ld2_argv, "ld2_args");
 
   if (lto_mode)
     maybe_run_lto_and_relink (ld2_argv, object_lst, object, false);
 #else
-  /* Otherwise, simply call ld because tlink is already done.  */
+  /* Otherwise, simply call ld because link is already done.  */
   if (lto_mode)
     maybe_run_lto_and_relink (ld2_argv, object_lst, object, true);
   else
     {
-      fork_execute ("ld", ld2_argv, HAVE_GNU_LD && at_file_supplied);
-      post_ld_pass (false);
+      fork_execute ("ld", ld2_argv, HAVE_GNU_LD && at_file_supplied, "ld_args");
+      post_ld_pass (/*temp_file*/false);
     }
 
   /* Let scan_prog_file do any final mods (OSF/rose needs this for
@@ -1814,13 +1805,6 @@ main (int argc, char **argv)
   scan_prog_file (output_file, PASS_SECOND, SCAN_ALL);
 #endif
 
-  maybe_unlink (c_file);
-  maybe_unlink (o_file);
-
-#ifdef COLLECT_EXPORT_LIST
-  maybe_unlink (export_file);
-#endif
-
   return 0;
 }
 
@@ -1831,9 +1815,10 @@ main (int argc, char **argv)
 void
 maybe_unlink (const char *file)
 {
-  if (debug)
+  if (save_temps && file_exists (file))
     {
-      notice ("[Leaving %s]\n", file);
+      if (verbose)
+       notice ("[Leaving %s]\n", file);
       return;
     }
 
@@ -1975,7 +1960,6 @@ write_list (FILE *stream, const char *prefix, struct id *list)
 
 #ifdef COLLECT_EXPORT_LIST
 /* This function is really used only on AIX, but may be useful.  */
-#if 0
 static int
 is_in_list (const char *prefix, struct id *list)
 {
@@ -1986,7 +1970,6 @@ is_in_list (const char *prefix, struct id *list)
     }
     return 0;
 }
-#endif
 #endif /* COLLECT_EXPORT_LIST */
 
 /* Added for debugging purpose.  */
@@ -2108,12 +2091,23 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED)
       fprintf (stream, "  struct object *next;\n");
       fprintf (stream, "};\n");
 
+      fprintf (stream, "extern void __register_frame_info_table_bases (void *, struct object *, void *tbase, void *dbase);\n");
       fprintf (stream, "extern void __register_frame_info_table (void *, struct object *);\n");
       fprintf (stream, "extern void *__deregister_frame_info (void *);\n");
+#ifdef TARGET_AIX_VERSION
+      fprintf (stream, "extern void *__gcc_unwind_dbase;\n");
+#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.
+        This might not be the start of the segment, signed offsets assumed.
+       */
+      fprintf (stream, "\t__register_frame_info_table_bases (frame_table, &ob, (void *)0, &__gcc_unwind_dbase);\n");
+#else
       fprintf (stream, "\t__register_frame_info_table (frame_table, &ob);\n");
+#endif
       fprintf (stream, "\t}\n");
 
       fprintf (stream, "static void dereg_frame () {\n");
@@ -2121,6 +2115,11 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED)
       fprintf (stream, "\t}\n");
     }
 
+#ifdef COLLECT_EXPORT_LIST
+  /* Set visibility of initializers to default.  */
+  if (visibility_flag)
+    fprintf (stream, "#pragma GCC visibility push(default)\n");
+#endif
   fprintf (stream, "void %s() {\n", initname);
   if (constructors.number > 0 || frames)
     {
@@ -2153,11 +2152,24 @@ write_c_file_stat (FILE *stream, const char *name ATTRIBUTE_UNUSED)
               destructors.number + frames);
     }
   fprintf (stream, "}\n");
+#ifdef COLLECT_EXPORT_LIST
+  if (visibility_flag)
+    fprintf (stream, "#pragma GCC visibility pop\n");
+#endif
 
   if (shared_obj)
     {
+#ifdef COLLECT_EXPORT_LIST
+      /* Set visibility of initializers to default.  */
+      if (visibility_flag)
+       fprintf (stream, "#pragma GCC visibility push(default)\n");
+#endif
       COLLECT_SHARED_INIT_FUNC (stream, initname);
       COLLECT_SHARED_FINI_FUNC (stream, fininame);
+#ifdef COLLECT_EXPORT_LIST
+      if (visibility_flag)
+       fprintf (stream, "#pragma GCC visibility pop\n");
+#endif
     }
 }
 
@@ -2254,38 +2266,48 @@ write_aix_file (FILE *stream, struct id *list)
 
 /* Check to make sure the file is an LTO object file.  */
 
+static int
+has_lto_section (void *data, const char *name ATTRIBUTE_UNUSED,
+                off_t offset ATTRIBUTE_UNUSED,
+                off_t length ATTRIBUTE_UNUSED)
+{
+  int *found = (int *) data;
+
+  if (!startswith (name, LTO_SECTION_NAME_PREFIX)
+      && !startswith (name, OFFLOAD_SECTION_NAME_PREFIX))
+    return 1;
+
+  *found = 1;
+
+  /* Stop iteration.  */
+  return 0;
+}
+
 static bool
-maybe_lto_object_file (const char *prog_name)
+is_lto_object_file (const char *prog_name)
 {
-  FILE *f;
-  unsigned char buf[4];
-  int i;
+  const char *errmsg;
+  int err;
+  int found = 0;
+  off_t inoff = 0;
+  int infd = open (prog_name, O_RDONLY | O_BINARY);
 
-  static unsigned char elfmagic[4] = { 0x7f, 'E', 'L', 'F' };
-  static unsigned char coffmagic[2] = { 0x4c, 0x01 };
-  static unsigned char coffmagic_x64[2] = { 0x64, 0x86 };
-  static unsigned char machomagic[4][4] = {
-    { 0xcf, 0xfa, 0xed, 0xfe },
-    { 0xce, 0xfa, 0xed, 0xfe },
-    { 0xfe, 0xed, 0xfa, 0xcf },
-    { 0xfe, 0xed, 0xfa, 0xce }
-  };
+  if (infd == -1)
+    return false;
 
-  f = fopen (prog_name, "rb");
-  if (f == NULL)
+  simple_object_read *inobj = simple_object_start_read (infd, inoff,
+                                                       LTO_SEGMENT_NAME,
+                                                       &errmsg, &err);
+  if (!inobj)
     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
-      || memcmp (buf, coffmagic_x64, sizeof (coffmagic_x64)) == 0)
+  errmsg = simple_object_find_sections (inobj, has_lto_section,
+                                       (void *) &found, &err);
+  if (! errmsg && found)
     return true;
-  for (i = 0; i < 4; i++)
-    if (memcmp (buf, machomagic[i], sizeof (machomagic[i])) == 0)
-      return true;
 
+  if (errmsg)
+    fatal_error (0, "%s: %s", errmsg, xstrerror (err));
   return false;
 }
 
@@ -2308,7 +2330,6 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
   int err;
   char *p, buf[1024];
   FILE *inf;
-  int found_lto = 0;
 
   if (which_pass == PASS_SECOND)
     return;
@@ -2316,12 +2337,17 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
   /* LTO objects must be in a known format.  This check prevents
      us from accepting an archive containing LTO objects, which
      gcc cannot currently handle.  */
-  if (which_pass == PASS_LTOINFO && !maybe_lto_object_file (prog_name))
-    return;
+  if (which_pass == PASS_LTOINFO)
+    {
+      if(is_lto_object_file (prog_name)) {
+       add_lto_object (&lto_objects, prog_name);
+      }
+      return;
+    }
 
   /* If we do not have an `nm', complain.  */
   if (nm_file_name == 0)
-    fatal_error (input_location, "cannot find 'nm'");
+    fatal_error (input_location, "cannot find %<nm%>");
 
   nm_argv[argc++] = nm_file_name;
   if (NM_FLAGS[0] != '\0')
@@ -2347,7 +2373,7 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
 
   pex = pex_init (PEX_USE_PIPES, "collect2", NULL);
   if (pex == NULL)
-    fatal_error (input_location, "pex_init failed: %m");
+    fatal_error (input_location, "%<pex_init%> failed: %m");
 
   errmsg = pex_run (pex, 0, nm_file_name, real_nm_argv, NULL, HOST_BIT_BUCKET,
                    &err);
@@ -2369,15 +2395,10 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
 
   inf = pex_read_output (pex, 0);
   if (inf == NULL)
-    fatal_error (input_location, "can't open nm output: %m");
+    fatal_error (input_location, "cannot open nm output: %m");
 
   if (debug)
-    {
-      if (which_pass == PASS_LTOINFO)
-        fprintf (stderr, "\nnm output with LTO info marker symbol.\n");
-      else
-        fprintf (stderr, "\nnm output with constructors/destructors.\n");
-    }
+    fprintf (stderr, "\nnm output with constructors/destructors.\n");
 
   /* Read each line of nm output.  */
   while (fgets (buf, sizeof buf, inf) != (char *) 0)
@@ -2388,30 +2409,6 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
       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 (&lto_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 unless this is a kind of symbol we're
         not supposed to even consider.  */
@@ -2521,7 +2518,7 @@ scan_libraries (const char *prog_name)
   /* If we do not have an `ldd', complain.  */
   if (ldd_file_name == 0)
     {
-      error ("cannot find 'ldd'");
+      error ("cannot find %<ldd%>");
       return;
     }
 
@@ -2567,7 +2564,7 @@ scan_libraries (const char *prog_name)
 
   inf = pex_read_output (pex, 0);
   if (inf == NULL)
-    fatal_error (input_location, "can't open ldd output: %m");
+    fatal_error (input_location, "cannot open ldd output: %m");
 
   if (debug)
     notice ("\nldd output with constructors/destructors.\n");
@@ -2584,7 +2581,7 @@ scan_libraries (const char *prog_name)
        continue;
 
       name = p;
-      if (strncmp (name, "not found", sizeof ("not found") - 1) == 0)
+      if (startswith (name, "not found"))
        fatal_error (input_location, "dynamic dependency %s not found", buf);
 
       /* Find the end of the symbol name.  */
@@ -2597,8 +2594,8 @@ scan_libraries (const char *prog_name)
       if (access (name, R_OK) == 0)
        add_to_list (&libraries, name);
       else
-       fatal_error (input_location, "unable to open dynamic dependency '%s'",
-                    buf);
+       fatal_error (input_location, "unable to open dynamic dependency "
+                    "%qs", buf);
 
       if (debug)
        fprintf (stderr, "\t%s\n", buf);
@@ -2630,17 +2627,6 @@ scan_libraries (const char *prog_name)
 
 #ifdef OBJECT_FORMAT_COFF
 
-#if defined (EXTENDED_COFF)
-
-#   define GCC_SYMBOLS(X)      (SYMHEADER (X).isymMax + SYMHEADER (X).iextMax)
-#   define GCC_SYMENT          SYMR
-#   define GCC_OK_SYMBOL(X)    ((X).st == stProc || (X).st == stGlobal)
-#   define GCC_SYMINC(X)       (1)
-#   define GCC_SYMZERO(X)      (SYMHEADER (X).isymMax)
-#   define GCC_CHECK_HDR(X)    (PSYMTAB (X) != 0)
-
-#else
-
 #   define GCC_SYMBOLS(X)      (HEADER (ldptr).f_nsyms)
 #   define GCC_SYMENT          SYMENT
 #   if defined (C_WEAKEXT)
@@ -2679,8 +2665,6 @@ scan_libraries (const char *prog_name)
       && !(HEADER (X).f_flags & F_LOADONLY))
 #endif
 
-#endif
-
 #ifdef COLLECT_EXPORT_LIST
 /* Array of standard AIX libraries which should not
    be scanned for ctors/dtors.  */
@@ -2761,7 +2745,10 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
       if ((ldptr = ldopen (CONST_CAST (char *, prog_name), ldptr)) != NULL)
        {
          if (! MY_ISCOFF (HEADER (ldptr).f_magic))
-           fatal_error (input_location, "%s: not a COFF file", prog_name);
+           {
+             warning (0, "%s: not a COFF file", prog_name);
+             continue;
+           }
 
          if (GCC_CHECK_HDR (ldptr))
            {
@@ -2804,7 +2791,12 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
                        case SYM_AIXI:
                          if (! (filter & SCAN_CTOR))
                            break;
-                         if (is_shared && !aixlazy_flag)
+                         if (is_shared && !aixlazy_flag
+#ifdef COLLECT_EXPORT_LIST
+                             && ! static_obj
+                             && ! is_in_list (prog_name, static_libs.first)
+#endif
+                             )
                            add_to_list (&constructors, name);
                          break;
 
@@ -2878,22 +2870,25 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
                             provides an explicit export list.  */
                          if (shared_obj && !is_shared
                              && which_pass == PASS_OBJ && !export_flag)
-                           add_to_list (&exports, name);
+                           {
+                             /* Do not auto-export __dso_handle or
+                                __gcc_unwind_dbase.  They are required
+                                to be local to each module.  */
+                             if (strcmp(name, "__dso_handle") != 0
+                                 && strcmp(name, "__gcc_unwind_dbase") != 0)
+                               {
+                                 add_to_list (&exports, name);
+                               }
+                           }
 #endif
                          continue;
                        }
 
                      if (debug)
-#if !defined(EXTENDED_COFF)
                        fprintf (stderr, "\tsec=%d class=%d type=%s%o %s\n",
                                 symbol.n_scnum, symbol.n_sclass,
                                 (symbol.n_type ? "0" : ""), symbol.n_type,
                                 name);
-#else
-                       fprintf (stderr,
-                                "\tiss = %5d, value = %5ld, index = %5d, name = %s\n",
-                                symbol.iss, (long) symbol.value, symbol.index, name);
-#endif
                    }
                }
            }
@@ -3011,18 +3006,52 @@ process_args (int *argcp, char **argv) {
 
 static void
 do_dsymutil (const char *output_file) {
-  const char *dsymutil = DSYMUTIL + 1;
+  const char *dsymutil = 0;
   struct pex_obj *pex;
-  char **real_argv = XCNEWVEC (char *, 3);
+  char **real_argv = XCNEWVEC (char *, verbose ? 4 : 3);
   const char ** argv = CONST_CAST2 (const char **, char **,
                                    real_argv);
+/* For cross-builds search the PATH using target-qualified name if we
+   have not already found a suitable dsymutil.  In practice, all modern
+   versions of dsymutil handle all supported archs, however the approach
+   here is consistent with the way other installations work (and one can
+   always symlink a multitarget dsymutil with a target-specific name).  */
+  const char *dsname = "dsymutil";
+#ifdef CROSS_DIRECTORY_STRUCTURE
+  const char *qname = concat (target_machine, "-", dsname, NULL);
+#else
+  const char *qname = dsname;
+#endif
+#ifdef DEFAULT_DSYMUTIL
+  /* Configured default takes priority.  */
+  if (dsymutil == 0 && access (DEFAULT_DSYMUTIL, X_OK) == 0)
+    dsymutil = DEFAULT_DSYMUTIL;
+  if (dsymutil == 0)
+#endif
+#ifdef DSYMUTIL
+  /* Followed by one supplied in the target header, somewhat like the
+     REAL_XX_NAME used elsewhere.  */
+    dsymutil = find_a_file (&cpath, DSYMUTIL, X_OK);
+  if (dsymutil == 0)
+    dsymutil = find_a_file (&path, DSYMUTIL, X_OK);
+  if (dsymutil == 0)
+#endif
+    dsymutil = find_a_file (&cpath, dsname, X_OK);
+  if (dsymutil == 0)
+    dsymutil = find_a_file (&path, qname, X_OK);
 
   argv[0] = dsymutil;
   argv[1] = output_file;
-  argv[2] = (char *) 0;
+  if (verbose)
+    {
+      argv[2] = "-v";
+      argv[3] = (char *) 0;
+    }
+  else
+    argv[2] = (char *) 0;
 
   pex = collect_execute (dsymutil, real_argv, NULL, NULL,
-                        PEX_LAST | PEX_SEARCH, false);
+                        PEX_LAST | PEX_SEARCH, false, NULL);
   do_wait (dsymutil, pex);
 }