* update-copyright.py: Skip pdt-5.f03 in gfortran.dg subdir.
[platform/upstream/gcc.git] / gcc / lto-wrapper.c
index 11bf9ad..372d2db 100644 (file)
@@ -1,5 +1,5 @@
 /* Wrapper to call lto.  Used by collect2 and the linker plugin.
-   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+   Copyright (C) 2009-2017 Free Software Foundation, Inc.
 
    Factored out of collect2 by Rafael Espindola <espindola@google.com>
 
@@ -68,8 +68,9 @@ static unsigned int nr;
 static char **input_names;
 static char **output_names;
 static char **offload_names;
-static const char *offloadbegin, *offloadend;
+static char *offload_objects_file_name;
 static char *makefile;
+static char *debug_obj;
 
 const char tool_name[] = "lto-wrapper";
 
@@ -84,8 +85,12 @@ tool_cleanup (bool)
     maybe_unlink (ltrans_output_file);
   if (flto_out)
     maybe_unlink (flto_out);
+  if (offload_objects_file_name)
+    maybe_unlink (offload_objects_file_name);
   if (makefile)
     maybe_unlink (makefile);
+  if (debug_obj)
+    maybe_unlink (debug_obj);
   for (i = 0; i < nr; ++i)
     {
       maybe_unlink (input_names[i]);
@@ -190,6 +195,20 @@ append_option (struct cl_decoded_option **decoded_options,
          sizeof (struct cl_decoded_option));
 }
 
+/* Remove option number INDEX from DECODED_OPTIONS, update
+   DECODED_OPTIONS_COUNT.  */
+
+static void
+remove_option (struct cl_decoded_option **decoded_options,
+              int index, unsigned int *decoded_options_count)
+{
+  --*decoded_options_count;
+  memmove (&(*decoded_options)[index + 1],
+          &(*decoded_options)[index],
+          sizeof (struct cl_decoded_option)
+          * (*decoded_options_count - index));
+}
+
 /* Try to merge and complain about options FDECODED_OPTIONS when applied
    ontop of DECODED_OPTIONS.  */
 
@@ -200,6 +219,8 @@ merge_and_complain (struct cl_decoded_option **decoded_options,
                    unsigned int fdecoded_options_count)
 {
   unsigned int i, j;
+  struct cl_decoded_option *pic_option = NULL;
+  struct cl_decoded_option *pie_option = NULL;
 
   /* ???  Merge options from files.  Most cases can be
      handled by either unioning or intersecting
@@ -232,13 +253,11 @@ merge_and_complain (struct cl_decoded_option **decoded_options,
            break;
 
          /* Fallthru.  */
-       case OPT_fPIC:
-       case OPT_fpic:
-       case OPT_fPIE:
-       case OPT_fpie:
+       case OPT_fdiagnostics_show_caret:
+       case OPT_fdiagnostics_show_option:
+       case OPT_fdiagnostics_show_location_:
+       case OPT_fshow_column:
        case OPT_fcommon:
-       case OPT_fexceptions:
-       case OPT_fnon_call_exceptions:
        case OPT_fgnu_tm:
          /* Do what the old LTO code did - collect exactly one option
             setting per OPT code, we pick the first we encounter.
@@ -251,64 +270,43 @@ merge_and_complain (struct cl_decoded_option **decoded_options,
            append_option (decoded_options, decoded_options_count, foption);
          break;
 
-       case OPT_ftrapv:
-       case OPT_fstrict_overflow:
-       case OPT_ffp_contract_:
-         /* For selected options we can merge conservatively.  */
-         for (j = 0; j < *decoded_options_count; ++j)
-           if ((*decoded_options)[j].opt_index == foption->opt_index)
-             break;
-         if (j == *decoded_options_count)
-           append_option (decoded_options, decoded_options_count, foption);
-         /* FP_CONTRACT_OFF < FP_CONTRACT_ON < FP_CONTRACT_FAST,
-            -fno-trapv < -ftrapv,
-            -fno-strict-overflow < -fstrict-overflow  */
-         else if (foption->value < (*decoded_options)[j].value)
-           (*decoded_options)[j] = *foption;
+       /* Figure out what PIC/PIE level wins and merge the results.  */
+       case OPT_fPIC:
+       case OPT_fpic:
+         pic_option = foption;
+         break;
+       case OPT_fPIE:
+       case OPT_fpie:
+         pie_option = foption;
          break;
 
-       case OPT_fmath_errno:
-       case OPT_fsigned_zeros:
-       case OPT_ftrapping_math:
-       case OPT_fwrapv:
        case OPT_fopenmp:
        case OPT_fopenacc:
+       case OPT_fcheck_pointer_bounds:
          /* For selected options we can merge conservatively.  */
          for (j = 0; j < *decoded_options_count; ++j)
            if ((*decoded_options)[j].opt_index == foption->opt_index)
              break;
          if (j == *decoded_options_count)
            append_option (decoded_options, decoded_options_count, foption);
-         /* -fmath-errno > -fno-math-errno,
-            -fsigned-zeros > -fno-signed-zeros,
-            -ftrapping-math -> -fno-trapping-math,
-            -fwrapv > -fno-wrapv.  */
+         /* -fopenmp > -fno-openmp,
+            -fopenacc > -fno-openacc,
+            -fcheck_pointer_bounds > -fcheck_pointer_bounds  */
          else if (foption->value > (*decoded_options)[j].value)
            (*decoded_options)[j] = *foption;
          break;
 
-       case OPT_freg_struct_return:
-       case OPT_fpcc_struct_return:
-       case OPT_fshort_double:
-         for (j = 0; j < *decoded_options_count; ++j)
-           if ((*decoded_options)[j].opt_index == foption->opt_index)
-             break;
-         if (j == *decoded_options_count)
-           fatal_error (input_location,
-                        "Option %s not used consistently in all LTO input"
-                        " files", foption->orig_option_with_args_text);
-         break;
-
-       case OPT_foffload_abi_:
+       case OPT_fopenacc_dim_:
+         /* Append or check identical.  */
          for (j = 0; j < *decoded_options_count; ++j)
            if ((*decoded_options)[j].opt_index == foption->opt_index)
              break;
          if (j == *decoded_options_count)
            append_option (decoded_options, decoded_options_count, foption);
-         else if (foption->value != (*decoded_options)[j].value)
+         else if (strcmp ((*decoded_options)[j].arg, foption->arg))
            fatal_error (input_location,
-                        "Option %s not used consistently in all LTO input"
-                        " files", foption->orig_option_with_args_text);
+                        "Option %s with different values",
+                        foption->orig_option_with_args_text);
          break;
 
        case OPT_O:
@@ -381,12 +379,70 @@ merge_and_complain (struct cl_decoded_option **decoded_options,
              (*decoded_options)[j].value = 1;
            }
          break;
+
+       case OPT_foffload_abi_:
+         for (j = 0; j < *decoded_options_count; ++j)
+           if ((*decoded_options)[j].opt_index == foption->opt_index)
+             break;
+         if (j == *decoded_options_count)
+           append_option (decoded_options, decoded_options_count, foption);
+         else if (foption->value != (*decoded_options)[j].value)
+           fatal_error (input_location,
+                        "Option %s not used consistently in all LTO input"
+                        " files", foption->orig_option_with_args_text);
+         break;
+
 
        case OPT_foffload_:
          append_option (decoded_options, decoded_options_count, foption);
          break;
        }
     }
+
+  /* Merge PIC options:
+      -fPIC + -fpic = -fpic
+      -fPIC + -fno-pic = -fno-pic
+      -fpic/-fPIC + nothin = nothing.  
+     It is a common mistake to mix few -fPIC compiled objects into otherwise
+     non-PIC code.  We do not want to build everything with PIC then.
+
+     It would be good to warn on mismatches, but it is bit hard to do as
+     we do not know what nothing translates to.  */
+    
+  for (unsigned int j = 0; j < *decoded_options_count;)
+    if ((*decoded_options)[j].opt_index == OPT_fPIC
+        || (*decoded_options)[j].opt_index == OPT_fpic)
+      {
+       if (!pic_option
+           || (pic_option->value > 0) != ((*decoded_options)[j].value > 0))
+         remove_option (decoded_options, j, decoded_options_count);
+       else if (pic_option->opt_index == OPT_fPIC
+                && (*decoded_options)[j].opt_index == OPT_fpic)
+         {
+           (*decoded_options)[j] = *pic_option;
+           j++;
+         }
+       else
+         j++;
+      }
+   else if ((*decoded_options)[j].opt_index == OPT_fPIE
+            || (*decoded_options)[j].opt_index == OPT_fpie)
+      {
+       if (!pie_option
+           || pie_option->value != (*decoded_options)[j].value)
+         remove_option (decoded_options, j, decoded_options_count);
+       else if (pie_option->opt_index == OPT_fPIE
+                && (*decoded_options)[j].opt_index == OPT_fpie)
+         {
+           (*decoded_options)[j] = *pie_option;
+           j++;
+         }
+       else
+         j++;
+      }
+   else
+     j++;
 }
 
 /* Auxiliary function that frees elements of PTR and PTR itself.
@@ -478,31 +534,25 @@ append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts,
         on any CL_TARGET flag and a few selected others.  */
       switch (option->opt_index)
        {
+       case OPT_fdiagnostics_show_caret:
+       case OPT_fdiagnostics_show_option:
+       case OPT_fdiagnostics_show_location_:
+       case OPT_fshow_column:
        case OPT_fPIC:
        case OPT_fpic:
        case OPT_fPIE:
        case OPT_fpie:
        case OPT_fcommon:
-       case OPT_fexceptions:
-       case OPT_fnon_call_exceptions:
        case OPT_fgnu_tm:
-       case OPT_freg_struct_return:
-       case OPT_fpcc_struct_return:
-       case OPT_fshort_double:
-       case OPT_ffp_contract_:
-       case OPT_fmath_errno:
-       case OPT_fsigned_zeros:
-       case OPT_ftrapping_math:
-       case OPT_fwrapv:
        case OPT_fopenmp:
        case OPT_fopenacc:
-       case OPT_ftrapv:
-       case OPT_fstrict_overflow:
+       case OPT_fopenacc_dim_:
        case OPT_foffload_abi_:
        case OPT_O:
        case OPT_Ofast:
        case OPT_Og:
        case OPT_Os:
+       case OPT_fcheck_pointer_bounds:
          break;
 
        default:
@@ -516,6 +566,36 @@ append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts,
     }
 }
 
+/* Append diag options in OPTS with length COUNT to ARGV_OBSTACK.  */
+
+static void
+append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts,
+                    unsigned int count)
+{
+  /* Append compiler driver arguments as far as they were merged.  */
+  for (unsigned int j = 1; j < count; ++j)
+    {
+      struct cl_decoded_option *option = &opts[j];
+
+      switch (option->opt_index)
+       {
+       case OPT_fdiagnostics_color_:
+       case OPT_fdiagnostics_show_caret:
+       case OPT_fdiagnostics_show_option:
+       case OPT_fdiagnostics_show_location_:
+       case OPT_fshow_column:
+         break;
+       default:
+         continue;
+       }
+
+      /* Pass the option on.  */
+      for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i)
+       obstack_ptr_grow (argv_obstack, option->canonical_option[i]);
+    }
+}
+
+
 /* Append linker options OPTS to ARGV_OBSTACK.  */
 
 static void
@@ -541,12 +621,13 @@ append_linker_options (obstack *argv_obstack, struct cl_decoded_option *opts,
          /* We've handled these LTO options, do not pass them on.  */
          continue;
 
-       case OPT_freg_struct_return:
-       case OPT_fpcc_struct_return:
-       case OPT_fshort_double:
-         /* Ignore these, they are determined by the input files.
-            ???  We fail to diagnose a possible mismatch here.  */
-         continue;
+       case OPT_fopenmp:
+       case OPT_fopenacc:
+         /* Ignore -fno-XXX form of these options, as otherwise
+            corresponding builtins will not be enabled.  */
+         if (option->value == 0)
+           continue;
+         break;
 
        default:
          break;
@@ -584,6 +665,8 @@ append_offload_options (obstack *argv_obstack, const char *target,
       else
        {
          opts = strchr (option->arg, '=');
+         /* If there are offload targets specified, but no actual options,
+            there is nothing to do here.  */
          if (!opts)
            continue;
 
@@ -596,10 +679,12 @@ append_offload_options (obstack *argv_obstack, const char *target,
                next = opts;
              next = (next > opts) ? opts : next;
 
+             /* Are we looking for this offload target?  */
              if (strlen (target) == (size_t) (next - cur)
                  && strncmp (target, cur, next - cur) == 0)
                break;
 
+             /* Skip the comma or equal sign.  */
              cur = next + 1;
            }
 
@@ -671,6 +756,10 @@ compile_offload_image (const char *target, const char *compiler_path,
       struct obstack argv_obstack;
       obstack_init (&argv_obstack);
       obstack_ptr_grow (&argv_obstack, compiler);
+      if (save_temps)
+       obstack_ptr_grow (&argv_obstack, "-save-temps");
+      if (verbose)
+       obstack_ptr_grow (&argv_obstack, "-v");
       obstack_ptr_grow (&argv_obstack, "-o");
       obstack_ptr_grow (&argv_obstack, filename);
 
@@ -681,6 +770,7 @@ compile_offload_image (const char *target, const char *compiler_path,
       /* Append options from offload_lto sections.  */
       append_compiler_options (&argv_obstack, compiler_opts,
                               compiler_opt_count);
+      append_diag_options (&argv_obstack, linker_opts, linker_opt_count);
 
       /* Append options specified by -foffload last.  In case of conflicting
         options we expect offload compiler to choose the latest.  */
@@ -718,6 +808,7 @@ compile_images_for_offload_targets (unsigned in_argc, char *in_argv[],
     return;
   unsigned num_targets = parse_env_var (target_names, &names, NULL);
 
+  int next_name_entry = 0;
   const char *compiler_path = getenv ("COMPILER_PATH");
   if (!compiler_path)
     goto out;
@@ -727,13 +818,19 @@ compile_images_for_offload_targets (unsigned in_argc, char *in_argv[],
   offload_names = XCNEWVEC (char *, num_targets + 1);
   for (unsigned i = 0; i < num_targets; i++)
     {
-      offload_names[i]
+      /* HSA does not use LTO-like streaming and a different compiler, skip
+        it. */
+      if (strcmp (names[i], "hsa") == 0)
+       continue;
+
+      offload_names[next_name_entry]
        = compile_offload_image (names[i], compiler_path, in_argc, in_argv,
                                 compiler_opts, compiler_opt_count,
                                 linker_opts, linker_opt_count);
-      if (!offload_names[i])
+      if (!offload_names[next_name_entry])
        fatal_error (input_location,
                     "problem with building target image for %s\n", names[i]);
+      next_name_entry++;
     }
 
  out:
@@ -760,42 +857,36 @@ copy_file (const char *dest, const char *src)
            fatal_error (input_location, "writing output file");
        }
     }
+  fclose (d);
+  fclose (s);
 }
 
-/* Find the crtoffloadbegin.o and crtoffloadend.o files in LIBRARY_PATH, make
-   copies and store the names of the copies in offloadbegin and offloadend.  */
+/* Find the crtoffloadtable.o file in LIBRARY_PATH, make copy and pass name of
+   the copy to the linker.  */
 
 static void
-find_offloadbeginend (void)
+find_crtoffloadtable (void)
 {
   char **paths = NULL;
   const char *library_path = getenv ("LIBRARY_PATH");
   if (!library_path)
     return;
-  unsigned n_paths = parse_env_var (library_path, &paths, "/crtoffloadbegin.o");
+  unsigned n_paths = parse_env_var (library_path, &paths, "/crtoffloadtable.o");
 
   unsigned i;
   for (i = 0; i < n_paths; i++)
     if (access_check (paths[i], R_OK) == 0)
       {
-       size_t len = strlen (paths[i]);
-       char *tmp = xstrdup (paths[i]);
-       strcpy (paths[i] + len - strlen ("begin.o"), "end.o");
-       if (access_check (paths[i], R_OK) != 0)
-         fatal_error (input_location,
-                      "installation error, can't find crtoffloadend.o");
-       /* The linker will delete the filenames we give it, so make
-          copies.  */
-       offloadbegin = make_temp_file (".o");
-       offloadend = make_temp_file (".o");
-       copy_file (offloadbegin, tmp);
-       copy_file (offloadend, paths[i]);
-       free (tmp);
+       /* The linker will delete the filename we give it, so make a copy.  */
+       char *crtoffloadtable = make_temp_file (".crtoffloadtable.o");
+       copy_file (crtoffloadtable, paths[i]);
+       printf ("%s\n", crtoffloadtable);
+       XDELETEVEC (crtoffloadtable);
        break;
       }
   if (i == n_paths)
     fatal_error (input_location,
-                "installation error, can't find crtoffloadbegin.o");
+                "installation error, can't find crtoffloadtable.o");
 
   free_array_of_ptrs ((void **) paths, n_paths);
 }
@@ -868,6 +959,67 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix,
   return true;
 }
 
+/* Copy early debug info sections from INFILE to a new file whose name
+   is returned.  Return NULL on error.  */
+
+const char *
+debug_objcopy (const char *infile)
+{
+  const char *outfile;
+  const char *errmsg;
+  int err;
+
+  const char *p;
+  off_t inoff = 0;
+  long loffset;
+  int consumed;
+  if ((p = strrchr (infile, '@'))
+      && p != infile
+      && sscanf (p, "@%li%n", &loffset, &consumed) >= 1
+      && strlen (p) == (unsigned int) consumed)
+    {
+      char *fname = xstrdup (infile);
+      fname[p - infile] = '\0';
+      infile = fname;
+      inoff = (off_t) loffset;
+    }
+  int infd = open (infile, O_RDONLY);
+  if (infd == -1)
+    return NULL;
+  simple_object_read *inobj = simple_object_start_read (infd, inoff,
+                                                       "__GNU_LTO",
+                                                       &errmsg, &err);
+  if (!inobj)
+    return NULL;
+
+  off_t off, len;
+  if (simple_object_find_section (inobj, ".gnu.debuglto_.debug_info",
+                                 &off, &len, &errmsg, &err) != 1)
+    {
+      if (errmsg)
+       fatal_error (0, "%s: %s\n", errmsg, xstrerror (err));
+
+      simple_object_release_read (inobj);
+      close (infd);
+      return NULL;
+    }
+
+  outfile = make_temp_file ("debugobjtem");
+  errmsg = simple_object_copy_lto_debug_sections (inobj, outfile, &err);
+  if (errmsg)
+    {
+      unlink_if_ordinary (outfile);
+      fatal_error (0, "%s: %s\n", errmsg, xstrerror (err));
+    }
+
+  simple_object_release_read (inobj);
+  close (infd);
+
+  return outfile;
+}
+
+
+
 /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */
 
 static void
@@ -892,8 +1044,10 @@ run_gcc (unsigned argc, char *argv[])
   int new_head_argc;
   bool have_lto = false;
   bool have_offload = false;
-  unsigned lto_argc = 0, offload_argc = 0;
-  char **lto_argv, **offload_argv;
+  unsigned lto_argc = 0, ltoobj_argc = 0;
+  char **lto_argv, **ltoobj_argv;
+  bool skip_debug = false;
+  unsigned n_debugobj;
 
   /* Get the driver and options.  */
   collect_gcc = getenv ("COLLECT_GCC");
@@ -909,10 +1063,10 @@ run_gcc (unsigned argc, char *argv[])
                                        &decoded_options,
                                        &decoded_options_count);
 
-  /* Allocate arrays for input object files with LTO or offload IL,
+  /* Allocate array for input object files with LTO IL,
      and for possible preceding arguments.  */
   lto_argv = XNEWVEC (char *, argc);
-  offload_argv = XNEWVEC (char *, argc);
+  ltoobj_argv = XNEWVEC (char *, argc);
 
   /* Look at saved options in the IL files.  */
   for (i = 1; i < argc; ++i)
@@ -924,6 +1078,15 @@ run_gcc (unsigned argc, char *argv[])
       int consumed;
       char *filename = argv[i];
 
+      if (strncmp (argv[i], "-foffload-objects=",
+                  sizeof ("-foffload-objects=") - 1) == 0)
+       {
+         have_offload = true;
+         offload_objects_file_name
+           = argv[i] + sizeof ("-foffload-objects=") - 1;
+         continue;
+       }
+
       if ((p = strrchr (argv[i], '@'))
          && p != argv[i] 
          && sscanf (p, "@%li%n", &loffset, &consumed) >= 1
@@ -946,17 +1109,8 @@ run_gcc (unsigned argc, char *argv[])
                                  collect_gcc))
        {
          have_lto = true;
-         lto_argv[lto_argc++] = argv[i];
+         ltoobj_argv[ltoobj_argc++] = argv[i];
        }
-
-      if (find_and_merge_options (fd, file_offset, OFFLOAD_SECTION_NAME_PREFIX,
-                                 &offload_fdecoded_options,
-                                 &offload_fdecoded_options_count, collect_gcc))
-       {
-         have_offload = true;
-         offload_argv[offload_argc++] = argv[i];
-       }
-
       close (fd);
     }
 
@@ -1016,6 +1170,17 @@ run_gcc (unsigned argc, char *argv[])
        }
     }
 
+  /* Output lto-wrapper invocation command.  */
+  if (verbose)
+    {
+      for (i = 0; i < argc; ++i)
+       {
+         fputs (argv[i], stderr);
+         fputc (' ', stderr);
+       }
+      fputc ('\n', stderr);
+    }
+
   if (no_partition)
     {
       lto_mode = LTO_MODE_LTO;
@@ -1055,44 +1220,102 @@ run_gcc (unsigned argc, char *argv[])
 
   if (have_offload)
     {
-      compile_images_for_offload_targets (offload_argc, offload_argv,
+      unsigned i, num_offload_files;
+      char **offload_argv;
+      FILE *f;
+
+      f = fopen (offload_objects_file_name, "r");
+      if (f == NULL)
+       fatal_error (input_location, "cannot open %s: %m",
+                    offload_objects_file_name);
+      if (fscanf (f, "%u ", &num_offload_files) != 1)
+       fatal_error (input_location, "cannot read %s: %m",
+                    offload_objects_file_name);
+      offload_argv = XCNEWVEC (char *, num_offload_files);
+
+      /* Read names of object files with offload.  */
+      for (i = 0; i < num_offload_files; i++)
+       {
+         const unsigned piece = 32;
+         char *buf, *filename = XNEWVEC (char, piece);
+         size_t len;
+
+         buf = filename;
+cont1:
+         if (!fgets (buf, piece, f))
+           break;
+         len = strlen (filename);
+         if (filename[len - 1] != '\n')
+           {
+             filename = XRESIZEVEC (char, filename, len + piece);
+             buf = filename + len;
+             goto cont1;
+           }
+         filename[len - 1] = '\0';
+         offload_argv[i] = filename;
+       }
+      fclose (f);
+      if (offload_argv[num_offload_files - 1] == NULL)
+       fatal_error (input_location, "invalid format of %s",
+                    offload_objects_file_name);
+      maybe_unlink (offload_objects_file_name);
+      offload_objects_file_name = NULL;
+
+      /* Look at saved offload options in files.  */
+      for (i = 0; i < num_offload_files; i++)
+       {
+         char *p;
+         long loffset;
+         int fd, consumed;
+         off_t file_offset = 0;
+         char *filename = offload_argv[i];
+
+         if ((p = strrchr (offload_argv[i], '@'))
+             && p != offload_argv[i]
+             && sscanf (p, "@%li%n", &loffset, &consumed) >= 1
+             && strlen (p) == (unsigned int) consumed)
+           {
+             filename = XNEWVEC (char, p - offload_argv[i] + 1);
+             memcpy (filename, offload_argv[i], p - offload_argv[i]);
+             filename[p - offload_argv[i]] = '\0';
+             file_offset = (off_t) loffset;
+           }
+         fd = open (filename, O_RDONLY | O_BINARY);
+         if (fd == -1)
+           fatal_error (input_location, "cannot open %s: %m", filename);
+         if (!find_and_merge_options (fd, file_offset,
+                                      OFFLOAD_SECTION_NAME_PREFIX,
+                                      &offload_fdecoded_options,
+                                      &offload_fdecoded_options_count,
+                                      collect_gcc))
+           fatal_error (input_location, "cannot read %s: %m", filename);
+         close (fd);
+         if (filename != offload_argv[i])
+           XDELETEVEC (filename);
+       }
+
+      compile_images_for_offload_targets (num_offload_files, offload_argv,
                                          offload_fdecoded_options,
                                          offload_fdecoded_options_count,
                                          decoded_options,
                                          decoded_options_count);
+
+      free_array_of_ptrs ((void **) offload_argv, num_offload_files);
+
       if (offload_names)
        {
-         find_offloadbeginend ();
+         find_crtoffloadtable ();
          for (i = 0; offload_names[i]; i++)
            printf ("%s\n", offload_names[i]);
          free_array_of_ptrs ((void **) offload_names, i);
        }
     }
 
-  if (offloadbegin)
-    printf ("%s\n", offloadbegin);
-
   /* If object files contain offload sections, but do not contain LTO sections,
      then there is no need to perform a link-time recompilation, i.e.
      lto-wrapper is used only for a compilation of offload images.  */
   if (have_offload && !have_lto)
-    {
-      for (i = 1; i < argc; ++i)
-       if (strncmp (argv[i], "-fresolution=", sizeof ("-fresolution=") - 1))
-         {
-           char *out_file;
-           /* Can be ".o" or ".so".  */
-           char *ext = strrchr (argv[i], '.');
-           if (ext == NULL)
-             out_file = make_temp_file ("");
-           else
-             out_file = make_temp_file (ext);
-           /* The linker will delete the files we give it, so make copies.  */
-           copy_file (out_file, argv[i]);
-           printf ("%s\n", out_file);
-         }
-      goto finish;
-    }
+    goto finish;
 
   if (lto_mode == LTO_MODE_LTO)
     {
@@ -1147,18 +1370,105 @@ run_gcc (unsigned argc, char *argv[])
         obstack_ptr_grow (&argv_obstack, "-fwpa");
     }
 
-  /* Append the input objects and possible preceding arguments.  */
+  /* Append input arguments.  */
   for (i = 0; i < lto_argc; ++i)
     obstack_ptr_grow (&argv_obstack, lto_argv[i]);
+  /* Append the input objects.  */
+  for (i = 0; i < ltoobj_argc; ++i)
+    obstack_ptr_grow (&argv_obstack, ltoobj_argv[i]);
   obstack_ptr_grow (&argv_obstack, NULL);
 
   new_argv = XOBFINISH (&argv_obstack, const char **);
   argv_ptr = &new_argv[new_head_argc];
   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
 
+  /* Handle early generated debug information.  At compile-time
+     we output early DWARF debug info into .gnu.debuglto_ prefixed
+     sections.  LTRANS object DWARF debug info refers to that.
+     So we need to transfer the .gnu.debuglto_ sections to the final
+     link.  Ideally the linker plugin interface would allow us to
+     not claim those sections and instruct the linker to keep
+     them, renaming them in the process.  For now we extract and
+     rename those sections via a simple-object interface to produce
+     regular objects containing only the early debug info.  We
+     then partially link those to a single early debug info object
+     and pass that as additional output back to the linker plugin.  */
+
+  /* Prepare the partial link to gather the compile-time generated
+     debug-info into a single input for the final link.  */
+  debug_obj = make_temp_file ("debugobj");
+  obstack_ptr_grow (&argv_obstack, collect_gcc);
+  for (i = 1; i < decoded_options_count; ++i)
+    {
+      /* Retain linker choice and -B.  */
+      if (decoded_options[i].opt_index == OPT_B
+         || decoded_options[i].opt_index == OPT_fuse_ld_bfd
+         || decoded_options[i].opt_index == OPT_fuse_ld_gold)
+       append_linker_options (&argv_obstack, &decoded_options[i-1], 2);
+      /* Retain all target options, this preserves -m32 for example.  */
+      if (cl_options[decoded_options[i].opt_index].flags & CL_TARGET)
+       append_linker_options (&argv_obstack, &decoded_options[i-1], 2);
+      /* Recognize -g0.  */
+      if (decoded_options[i].opt_index == OPT_g
+         && strcmp (decoded_options[i].arg, "0") == 0)
+       skip_debug = true;
+    }
+  obstack_ptr_grow (&argv_obstack, "-r");
+  obstack_ptr_grow (&argv_obstack, "-nostdlib");
+  obstack_ptr_grow (&argv_obstack, "-o");
+  obstack_ptr_grow (&argv_obstack, debug_obj);
+
+  /* Copy the early generated debug info from the objects to temporary
+     files and append those to the partial link commandline.  */
+  n_debugobj = 0;
+  if (! skip_debug)
+    for (i = 0; i < ltoobj_argc; ++i)
+      {
+       const char *tem;
+       if ((tem = debug_objcopy (ltoobj_argv[i])))
+         {
+           obstack_ptr_grow (&argv_obstack, tem);
+           n_debugobj++;
+         }
+      }
+
+  /* Link them all into a single object.  Ideally this would reduce
+     disk space usage mainly due to .debug_str merging but unfortunately
+     GNU ld doesn't perform this with -r.  */
+  if (n_debugobj)
+    {
+      obstack_ptr_grow (&argv_obstack, NULL);
+      const char **debug_link_argv = XOBFINISH (&argv_obstack, const char **);
+      fork_execute (debug_link_argv[0],
+                   CONST_CAST (char **, debug_link_argv), false);
+
+      /* And dispose the temporaries.  */
+      for (i = 0; debug_link_argv[i]; ++i)
+       ;
+      for (--i; i > 0; --i)
+       {
+         if (strcmp (debug_link_argv[i], debug_obj) == 0)
+           break;
+         maybe_unlink (debug_link_argv[i]);
+       }
+    }
+  else
+    {
+      unlink_if_ordinary (debug_obj);
+      free (debug_obj);
+      debug_obj = NULL;
+      skip_debug = true;
+    }
+
   if (lto_mode == LTO_MODE_LTO)
     {
       printf ("%s\n", flto_out);
+      if (!skip_debug)
+       {
+         printf ("%s\n", debug_obj);
+         free (debug_obj);
+         debug_obj = NULL;
+       }
       free (flto_out);
       flto_out = NULL;
     }
@@ -1307,6 +1617,12 @@ cont:
          for (i = 0; i < nr; ++i)
            maybe_unlink (input_names[i]);
        }
+      if (!skip_debug)
+       {
+         printf ("%s\n", debug_obj);
+         free (debug_obj);
+         debug_obj = NULL;
+       }
       for (i = 0; i < nr; ++i)
        {
          fputs (output_names[i], stdout);
@@ -1321,11 +1637,7 @@ cont:
     }
 
  finish:
-  if (offloadend)
-    printf ("%s\n", offloadend);
-
   XDELETE (lto_argv);
-  XDELETE (offload_argv);
   obstack_free (&argv_obstack, NULL);
 }
 
@@ -1337,7 +1649,7 @@ main (int argc, char *argv[])
 {
   const char *p;
 
-  gcc_obstack_init (&opts_obstack);
+  init_opts_obstack ();
 
   p = argv[0] + strlen (argv[0]);
   while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))