diff mbox

Handle -fsanitize=* in lto-wrapper (PR lto/69254)

Message ID 20160126154439.GY3017@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Jan. 26, 2016, 3:44 p.m. UTC
On Tue, Jan 26, 2016 at 03:06:43PM +0100, Richard Biener wrote:
> I'm somewhat confused about that you drop -fsanitize options from
> the LTO options section writing in lto-opts.c but then add code to
> parse it from there in lto-wrapper.c.  The code there also looks

Sorry, as I said to Bernd, that has been just thinko in the comment change,
the code was doing what I meant to.

> somewhat duplicated - why not just canonicalize any -fsanitize=
> option coming in to the first in merge_and_complain
> and special-case it in append_compiler_options again by say
> 
>   case OPT_fsanitize_:
>      obstack_ptr_grow (argv_obstack, "-fsanitize=shift");

It is certainly much easier to just propagate around a bool flag
whether we need to add -fsanitize=shift after the linker options.
The reason I haven't done that because I didn't want to pass around
yet another argument, furthermore so specific to a particular option.

But looking around, it seems we are already passing around pairs,
the pointer to array of struct cl_decoded_option and pointer to the count of
elements in that array.  By sticking the two into a structure and adding
the sanitize_undefined flag for now, if in the future we need to handle
some other option similarly, we can just handle it in 3-4 spots and not
everywhere among so many different functions.

So, what do you think about the untested patch below (the test with
-c -flto -fsanitize=undefined
-flto -fno-sanitize=undefined
passes)?  The patch is large, because it changed many places from
a pair of pointer and unsigned int (or a pair of pointer to pointer and
pointer to unsigned int) to pointer to this new struct.

2016-01-26  Jakub Jelinek  <jakub@redhat.com>

	PR lto/69254
	* lto-opts.c (lto_write_options): Write also -f{,no-}sanitize=
	options.
	* lto-wrapper.c (struct lto_decoded_options): New type.
	(append_option, merge_and_complain, append_compiler_options,
	append_linker_options, append_offload_options,
	compile_offload_image, compile_images_for_offload_targets,
	find_and_merge_options): Pass around options
	in struct lto_decoded_options instead of struct cl_decoded_option
	pointer and count pair.
	(get_options_from_collect_gcc_options): Likewise.  Parse -fsanitize=
	options and if in the end any ub sanitizers are enabled, set
	decoded_opts->sanitize_undefined to true.
	(run_gcc): Adjust callers of these functions.  If
	fdecoded_options.sanitize_undefined is true, append
	-fsanitize=shift after the linker options.


	Jakub

Comments

Jakub Jelinek Jan. 26, 2016, 8:54 p.m. UTC | #1
On Tue, Jan 26, 2016 at 04:44:39PM +0100, Jakub Jelinek wrote:
> 2016-01-26  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/69254
> 	* lto-opts.c (lto_write_options): Write also -f{,no-}sanitize=
> 	options.
> 	* lto-wrapper.c (struct lto_decoded_options): New type.
> 	(append_option, merge_and_complain, append_compiler_options,
> 	append_linker_options, append_offload_options,
> 	compile_offload_image, compile_images_for_offload_targets,
> 	find_and_merge_options): Pass around options
> 	in struct lto_decoded_options instead of struct cl_decoded_option
> 	pointer and count pair.
> 	(get_options_from_collect_gcc_options): Likewise.  Parse -fsanitize=
> 	options and if in the end any ub sanitizers are enabled, set
> 	decoded_opts->sanitize_undefined to true.
> 	(run_gcc): Adjust callers of these functions.  If
> 	fdecoded_options.sanitize_undefined is true, append
> 	-fsanitize=shift after the linker options.

Now successfully bootstrapped/regtested on x86_64-linux and i686-linux.

	Jakub
diff mbox

Patch

--- gcc/lto-opts.c.jj	2016-01-25 22:33:11.477029666 +0100
+++ gcc/lto-opts.c	2016-01-26 15:41:02.937040062 +0100
@@ -198,10 +198,13 @@  lto_write_options (void)
 
       /* Also drop all options that are handled by the driver as well,
 	 which includes things like -o and -v or -fhelp for example.
-	 We do not need those.  The only exception is -foffload option, if we
-	 write it in offload_lto section.  Also drop all diagnostic options.  */
+	 We do not need those.  The only exceptions are:
+	 1) -foffload option, if we write it in offload_lto section
+	 2) -f{,no-}sanitize=
+	 Also drop all diagnostic options.  */
       if ((cl_options[option->opt_index].flags & (CL_DRIVER|CL_WARNING))
-	  && (!lto_stream_offload_p || option->opt_index != OPT_foffload_))
+	  && (!lto_stream_offload_p || option->opt_index != OPT_foffload_)
+	  && option->opt_index != OPT_fsanitize_)
 	continue;
 
       for (j = 0; j < option->canonical_option_num_elements; ++j)
--- gcc/lto-wrapper.c.jj	2016-01-26 15:24:10.457845617 +0100
+++ gcc/lto-wrapper.c	2016-01-26 16:38:16.784303735 +0100
@@ -118,6 +118,16 @@  maybe_unlink (const char *file)
 /* Template of LTRANS dumpbase suffix.  */
 #define DUMPBASE_SUFFIX ".ltrans18446744073709551615"
 
+/* Structure containing decoded options, number of them and auxiliary
+   state from the options handling.  */
+
+struct lto_decoded_options
+{
+  struct cl_decoded_option *opt;
+  unsigned int count;
+  bool sanitize_undefined;
+};
+
 /* Create decoded options from the COLLECT_GCC and COLLECT_GCC_OPTIONS
    environment according to LANG_MASK.  */
 
@@ -125,13 +135,14 @@  static void
 get_options_from_collect_gcc_options (const char *collect_gcc,
 				      const char *collect_gcc_options,
 				      unsigned int lang_mask,
-				      struct cl_decoded_option **decoded_options,
-				      unsigned int *decoded_options_count)
+				      struct lto_decoded_options *decoded_opts)
 {
   struct obstack argv_obstack;
   char *argv_storage;
   const char **argv;
   int j, k, argc;
+  unsigned int i;
+  int sanitize = 0;
 
   argv_storage = xstrdup (collect_gcc_options);
   obstack_init (&argv_obstack);
@@ -166,9 +177,30 @@  get_options_from_collect_gcc_options (co
   argc = obstack_object_size (&argv_obstack) / sizeof (void *) - 1;
   argv = XOBFINISH (&argv_obstack, const char **);
 
+  memset (decoded_opts, 0, sizeof (*decoded_opts));
   decode_cmdline_options_to_array (argc, (const char **)argv,
 				   lang_mask,
-				   decoded_options, decoded_options_count);
+				   &decoded_opts->opt, &decoded_opts->count);
+
+  for (i = 0; i < decoded_opts->count; ++i)
+    {
+      struct cl_decoded_option *foption = &decoded_opts->opt[i];
+      switch (foption->opt_index)
+	{
+	case OPT_fsanitize_:
+	  sanitize = parse_sanitizer_options (foption->arg, input_location,
+					      OPT_fsanitize_, sanitize,
+					      foption->value, false);
+	  break;
+	default:
+	  break;
+	}
+    }
+  /* If DECODED_OPTS->OPT requested any ubsan sanitization, collect
+     that info for later handling.  */
+  if (sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
+    decoded_opts->sanitize_undefined = true;
+
   obstack_free (&argv_obstack, NULL);
 }
 
@@ -176,17 +208,16 @@  get_options_from_collect_gcc_options (co
    DECODED_OPTIONS_COUNT.  */
 
 static void
-append_option (struct cl_decoded_option **decoded_options,
-	       unsigned int *decoded_options_count,
+append_option (struct lto_decoded_options *decoded_options,
 	       struct cl_decoded_option *option)
 {
-  ++*decoded_options_count;
-  *decoded_options
+  ++decoded_options->count;
+  decoded_options->opt
     = (struct cl_decoded_option *)
-	xrealloc (*decoded_options,
-		  (*decoded_options_count
+	xrealloc (decoded_options->opt,
+		  (decoded_options->count
 		   * sizeof (struct cl_decoded_option)));
-  memcpy (&(*decoded_options)[*decoded_options_count - 1], option,
+  memcpy (&decoded_options->opt[decoded_options->count - 1], option,
 	  sizeof (struct cl_decoded_option));
 }
 
@@ -194,13 +225,13 @@  append_option (struct cl_decoded_option
    ontop of DECODED_OPTIONS.  */
 
 static void
-merge_and_complain (struct cl_decoded_option **decoded_options,
-		    unsigned int *decoded_options_count,
-		    struct cl_decoded_option *fdecoded_options,
-		    unsigned int fdecoded_options_count)
+merge_and_complain (struct lto_decoded_options *decoded_options,
+		    struct lto_decoded_options *fdecoded_options)
 {
   unsigned int i, j;
 
+  decoded_options->sanitize_undefined |= fdecoded_options->sanitize_undefined;
+
   /* ???  Merge options from files.  Most cases can be
      handled by either unioning or intersecting
      (for example -fwrapv is a case for unioning,
@@ -216,9 +247,9 @@  merge_and_complain (struct cl_decoded_op
 
   /* The following does what the old LTO option code did,
      union all target and a selected set of common options.  */
-  for (i = 0; i < fdecoded_options_count; ++i)
+  for (i = 0; i < fdecoded_options->count; ++i)
     {
-      struct cl_decoded_option *foption = &fdecoded_options[i];
+      struct cl_decoded_option *foption = &fdecoded_options->opt[i];
       switch (foption->opt_index)
 	{
 	case OPT_SPECIAL_unknown:
@@ -248,27 +279,27 @@  merge_and_complain (struct cl_decoded_op
 	     setting per OPT code, we pick the first we encounter.
 	     ???  This doesn't make too much sense, but when it doesn't
 	     then we should complain.  */
-	  for (j = 0; j < *decoded_options_count; ++j)
-	    if ((*decoded_options)[j].opt_index == foption->opt_index)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[j].opt_index == foption->opt_index)
 	      break;
-	  if (j == *decoded_options_count)
-	    append_option (decoded_options, decoded_options_count, foption);
+	  if (j == decoded_options->count)
+	    append_option (decoded_options, 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)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[j].opt_index == foption->opt_index)
 	      break;
-	  if (j == *decoded_options_count)
-	    append_option (decoded_options, decoded_options_count, foption);
+	  if (j == decoded_options->count)
+	    append_option (decoded_options, 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;
+	  else if (foption->value < decoded_options->opt[j].value)
+	    decoded_options->opt[j] = *foption;
 	  break;
 
 	case OPT_fmath_errno:
@@ -280,38 +311,38 @@  merge_and_complain (struct cl_decoded_op
 	case OPT_fcilkplus:
 	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)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[j].opt_index == foption->opt_index)
 	      break;
-	  if (j == *decoded_options_count)
-	    append_option (decoded_options, decoded_options_count, foption);
+	  if (j == decoded_options->count)
+	    append_option (decoded_options, foption);
 	  /* -fmath-errno > -fno-math-errno,
 	     -fsigned-zeros > -fno-signed-zeros,
 	     -ftrapping-math -> -fno-trapping-math,
 	     -fwrapv > -fno-wrapv.  */
-	  else if (foption->value > (*decoded_options)[j].value)
-	    (*decoded_options)[j] = *foption;
+	  else if (foption->value > decoded_options->opt[j].value)
+	    decoded_options->opt[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)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[j].opt_index == foption->opt_index)
 	      break;
-	  if (j == *decoded_options_count)
+	  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_:
-	  for (j = 0; j < *decoded_options_count; ++j)
-	    if ((*decoded_options)[j].opt_index == foption->opt_index)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[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)
+	  if (j == decoded_options->count)
+	    append_option (decoded_options, foption);
+	  else if (foption->value != decoded_options->opt[j].value)
 	    fatal_error (input_location,
 			 "Option %s not used consistently in all LTO input"
 			 " files", foption->orig_option_with_args_text);
@@ -321,15 +352,15 @@  merge_and_complain (struct cl_decoded_op
 	case OPT_Ofast:
 	case OPT_Og:
 	case OPT_Os:
-	  for (j = 0; j < *decoded_options_count; ++j)
-	    if ((*decoded_options)[j].opt_index == OPT_O
-		|| (*decoded_options)[j].opt_index == OPT_Ofast
-		|| (*decoded_options)[j].opt_index == OPT_Og
-		|| (*decoded_options)[j].opt_index == OPT_Os)
+	  for (j = 0; j < decoded_options->count; ++j)
+	    if (decoded_options->opt[j].opt_index == OPT_O
+		|| decoded_options->opt[j].opt_index == OPT_Ofast
+		|| decoded_options->opt[j].opt_index == OPT_Og
+		|| decoded_options->opt[j].opt_index == OPT_Os)
 	      break;
-	  if (j == *decoded_options_count)
-	    append_option (decoded_options, decoded_options_count, foption);
-	  else if ((*decoded_options)[j].opt_index == foption->opt_index
+	  if (j == decoded_options->count)
+	    append_option (decoded_options, foption);
+	  else if (decoded_options->opt[j].opt_index == foption->opt_index
 		   && foption->opt_index != OPT_O)
 	    /* Exact same options get merged.  */
 	    ;
@@ -359,13 +390,13 @@  merge_and_complain (struct cl_decoded_op
 		default:
 		  gcc_unreachable ();
 		}
-	      switch ((*decoded_options)[j].opt_index)
+	      switch (decoded_options->opt[j].opt_index)
 		{
 		case OPT_O:
-		  if ((*decoded_options)[j].arg[0] == '\0')
+		  if (decoded_options->opt[j].arg[0] == '\0')
 		    level = MAX (level, 1);
 		  else
-		    level = MAX (level, atoi ((*decoded_options)[j].arg));
+		    level = MAX (level, atoi (decoded_options->opt[j].arg));
 		  break;
 		case OPT_Ofast:
 		  level = MAX (level, 3);
@@ -379,17 +410,17 @@  merge_and_complain (struct cl_decoded_op
 		default:
 		  gcc_unreachable ();
 		}
-	      (*decoded_options)[j].opt_index = OPT_O;
+	      decoded_options->opt[j].opt_index = OPT_O;
 	      char *tem;
 	      tem = xasprintf ("-O%d", level);
-	      (*decoded_options)[j].arg = &tem[2];
-	      (*decoded_options)[j].canonical_option[0] = tem;
-	      (*decoded_options)[j].value = 1;
+	      decoded_options->opt[j].arg = &tem[2];
+	      decoded_options->opt[j].canonical_option[0] = tem;
+	      decoded_options->opt[j].value = 1;
 	    }
 	  break;
 
 	case OPT_foffload_:
-	  append_option (decoded_options, decoded_options_count, foption);
+	  append_option (decoded_options, foption);
 	  break;
 	}
     }
@@ -459,13 +490,13 @@  parse_env_var (const char *str, char ***
 /* Append options OPTS from lto or offload_lto sections to ARGV_OBSTACK.  */
 
 static void
-append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts,
-			 unsigned int count)
+append_compiler_options (obstack *argv_obstack,
+			 struct lto_decoded_options *opts)
 {
   /* Append compiler driver arguments as far as they were merged.  */
-  for (unsigned int j = 1; j < count; ++j)
+  for (unsigned int j = 1; j < opts->count; ++j)
     {
-      struct cl_decoded_option *option = &opts[j];
+      struct cl_decoded_option *option = &opts->opt[j];
 
       /* File options have been properly filtered by lto-opts.c.  */
       switch (option->opt_index)
@@ -531,14 +562,13 @@  append_compiler_options (obstack *argv_o
 /* Append linker options OPTS to ARGV_OBSTACK.  */
 
 static void
-append_linker_options (obstack *argv_obstack, struct cl_decoded_option *opts,
-		       unsigned int count)
+append_linker_options (obstack *argv_obstack, struct lto_decoded_options *opts)
 {
   /* Append linker driver arguments.  Compiler options from the linker
      driver arguments will override / merge with those from the compiler.  */
-  for (unsigned int j = 1; j < count; ++j)
+  for (unsigned int j = 1; j < opts->count; ++j)
     {
-      struct cl_decoded_option *option = &opts[j];
+      struct cl_decoded_option *option = &opts->opt[j];
 
       /* Do not pass on frontend specific flags not suitable for lto.  */
       if (!(cl_options[option->opt_index].flags
@@ -584,15 +614,14 @@  append_linker_options (obstack *argv_obs
 
 static void
 append_offload_options (obstack *argv_obstack, const char *target,
-			struct cl_decoded_option *options,
-			unsigned int options_count)
+			struct lto_decoded_options *options)
 {
-  for (unsigned i = 0; i < options_count; i++)
+  for (unsigned i = 0; i < options->count; i++)
     {
       const char *cur, *next, *opts;
       char **argv;
       unsigned argc;
-      struct cl_decoded_option *option = &options[i];
+      struct cl_decoded_option *option = &options->opt[i];
 
       if (option->opt_index != OPT_foffload_)
 	continue;
@@ -664,10 +693,8 @@  access_check (const char *name, int mode
 static char *
 compile_offload_image (const char *target, const char *compiler_path,
 		       unsigned in_argc, char *in_argv[],
-		       struct cl_decoded_option *compiler_opts,
-		       unsigned int compiler_opt_count,
-		       struct cl_decoded_option *linker_opts,
-		       unsigned int linker_opt_count)
+		       struct lto_decoded_options *compiler_opts,
+		       struct lto_decoded_options *linker_opts)
 {
   char *filename = NULL;
   char **argv;
@@ -708,15 +735,12 @@  compile_offload_image (const char *targe
 	obstack_ptr_grow (&argv_obstack, in_argv[i]);
 
       /* Append options from offload_lto sections.  */
-      append_compiler_options (&argv_obstack, compiler_opts,
-			       compiler_opt_count);
+      append_compiler_options (&argv_obstack, compiler_opts);
 
       /* Append options specified by -foffload last.  In case of conflicting
 	 options we expect offload compiler to choose the latest.  */
-      append_offload_options (&argv_obstack, target, compiler_opts,
-			      compiler_opt_count);
-      append_offload_options (&argv_obstack, target, linker_opts,
-			      linker_opt_count);
+      append_offload_options (&argv_obstack, target, compiler_opts);
+      append_offload_options (&argv_obstack, target, linker_opts);
 
       obstack_ptr_grow (&argv_obstack, NULL);
       argv = XOBFINISH (&argv_obstack, char **);
@@ -736,10 +760,8 @@  compile_offload_image (const char *targe
 
 static void
 compile_images_for_offload_targets (unsigned in_argc, char *in_argv[],
-				    struct cl_decoded_option *compiler_opts,
-				    unsigned int compiler_opt_count,
-				    struct cl_decoded_option *linker_opts,
-				    unsigned int linker_opt_count)
+				    struct lto_decoded_options *compiler_opts,
+				    struct lto_decoded_options *linker_opts)
 {
   char **names = NULL;
   const char *target_names = getenv (OFFLOAD_TARGET_NAMES_ENV);
@@ -764,8 +786,7 @@  compile_images_for_offload_targets (unsi
 
       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);
+				 compiler_opts, linker_opts);
       if (!offload_names[next_name_entry])
 	fatal_error (input_location,
 		     "problem with building target image for %s\n", names[i]);
@@ -837,23 +858,20 @@  find_offloadbeginend (void)
 }
 
 /* A subroutine of run_gcc.  Examine the open file FD for lto sections with
-   name prefix PREFIX, at FILE_OFFSET, and store any options we find in OPTS
-   and OPT_COUNT.  Return true if we found a matchingn section, false
-   otherwise.  COLLECT_GCC holds the value of the environment variable with
-   the same name.  */
+   name prefix PREFIX, at FILE_OFFSET, and store any options we find in OPTS.
+   Return true if we found a matchingn section, false otherwise.  COLLECT_GCC
+   holds the value of the environment variable with the same name.  */
 
 static bool
 find_and_merge_options (int fd, off_t file_offset, const char *prefix,
-			struct cl_decoded_option **opts,
-			unsigned int *opt_count, const char *collect_gcc)
+			struct lto_decoded_options *opts,
+			const char *collect_gcc)
 {
   off_t offset, length;
   char *data;
   char *fopts;
   const char *errmsg;
   int err;
-  struct cl_decoded_option *fdecoded_options = *opts;
-  unsigned int fdecoded_options_count = *opt_count;
 
   simple_object_read *sobj;
   sobj = simple_object_start_read (fd, file_offset, "__GNU_LTO",
@@ -877,21 +895,14 @@  find_and_merge_options (int fd, off_t fi
   fopts = data;
   do
     {
-      struct cl_decoded_option *f2decoded_options;
-      unsigned int f2decoded_options_count;
+      struct lto_decoded_options fdecoded_options;
       get_options_from_collect_gcc_options (collect_gcc,
 					    fopts, CL_LANG_ALL,
-					    &f2decoded_options,
-					    &f2decoded_options_count);
-      if (!fdecoded_options)
-       {
-	 fdecoded_options = f2decoded_options;
-	 fdecoded_options_count = f2decoded_options_count;
-       }
+					    &fdecoded_options);
+      if (opts->opt == NULL)
+	*opts = fdecoded_options;
       else
-	merge_and_complain (&fdecoded_options,
-			    &fdecoded_options_count,
-			    f2decoded_options, f2decoded_options_count);
+	merge_and_complain (opts, &fdecoded_options);
 
       fopts += strlen (fopts) + 1;
     }
@@ -899,8 +910,6 @@  find_and_merge_options (int fd, off_t fi
 
   free (data);
   simple_object_release_read (sobj);
-  *opts = fdecoded_options;
-  *opt_count = fdecoded_options_count;
   return true;
 }
 
@@ -918,12 +927,9 @@  run_gcc (unsigned argc, char *argv[])
   int parallel = 0;
   int jobserver = 0;
   bool no_partition = false;
-  struct cl_decoded_option *fdecoded_options = NULL;
-  struct cl_decoded_option *offload_fdecoded_options = NULL;
-  unsigned int fdecoded_options_count = 0;
-  unsigned int offload_fdecoded_options_count = 0;
-  struct cl_decoded_option *decoded_options;
-  unsigned int decoded_options_count;
+  struct lto_decoded_options fdecoded_options;
+  struct lto_decoded_options offload_fdecoded_options;
+  struct lto_decoded_options decoded_options;
   struct obstack argv_obstack;
   int new_head_argc;
   bool have_lto = false;
@@ -931,6 +937,8 @@  run_gcc (unsigned argc, char *argv[])
   unsigned lto_argc = 0, offload_argc = 0;
   char **lto_argv, **offload_argv;
 
+  memset (&fdecoded_options, 0, sizeof fdecoded_options);
+  memset (&offload_fdecoded_options, 0, sizeof offload_fdecoded_options);
   /* Get the driver and options.  */
   collect_gcc = getenv ("COLLECT_GCC");
   if (!collect_gcc)
@@ -942,8 +950,7 @@  run_gcc (unsigned argc, char *argv[])
 		 "environment variable COLLECT_GCC_OPTIONS must be set");
   get_options_from_collect_gcc_options (collect_gcc, collect_gcc_options,
 					CL_LANG_ALL,
-					&decoded_options,
-					&decoded_options_count);
+					&decoded_options);
 
   /* Allocate arrays for input object files with LTO or offload IL,
      and for possible preceding arguments.  */
@@ -978,16 +985,14 @@  run_gcc (unsigned argc, char *argv[])
 	}
 
       if (find_and_merge_options (fd, file_offset, LTO_SECTION_NAME_PREFIX,
-				  &fdecoded_options, &fdecoded_options_count,
-				  collect_gcc))
+				  &fdecoded_options, collect_gcc))
 	{
 	  have_lto = true;
 	  lto_argv[lto_argc++] = argv[i];
 	}
 
       if (find_and_merge_options (fd, file_offset, OFFLOAD_SECTION_NAME_PREFIX,
-				  &offload_fdecoded_options,
-				  &offload_fdecoded_options_count, collect_gcc))
+				  &offload_fdecoded_options, collect_gcc))
 	{
 	  have_offload = true;
 	  offload_argv[offload_argc++] = argv[i];
@@ -1002,14 +1007,22 @@  run_gcc (unsigned argc, char *argv[])
   obstack_ptr_grow (&argv_obstack, "-xlto");
   obstack_ptr_grow (&argv_obstack, "-c");
 
-  append_compiler_options (&argv_obstack, fdecoded_options,
-			   fdecoded_options_count);
-  append_linker_options (&argv_obstack, decoded_options, decoded_options_count);
+  append_compiler_options (&argv_obstack, &fdecoded_options);
+  append_linker_options (&argv_obstack, &decoded_options);
+  /* If any of the TUs have enabled any undefined behavior sanitizer
+     options, make sure one of them is enabled in lto1, no matter what
+     the linker options say.  It doesn't matter much which undefined
+     behavior sanitizer it is, as long as it is handled only in the FEs
+     or before IPA, and afterwards the instrumentation is present only
+     in the form of builtin functions.  Adding this option makes sure
+     the builtins are initialized in lto1.  */
+  if (fdecoded_options.sanitize_undefined)
+    obstack_ptr_grow (&argv_obstack, "-fsanitize=shift");
 
   /* Scan linker driver arguments for things that are of relevance to us.  */
-  for (j = 1; j < decoded_options_count; ++j)
+  for (j = 1; j < decoded_options.count; ++j)
     {
-      struct cl_decoded_option *option = &decoded_options[j];
+      struct cl_decoded_option *option = &decoded_options.opt[j];
       switch (option->opt_index)
 	{
 	case OPT_o:
@@ -1092,10 +1105,8 @@  run_gcc (unsigned argc, char *argv[])
   if (have_offload)
     {
       compile_images_for_offload_targets (offload_argc, offload_argv,
-					  offload_fdecoded_options,
-					  offload_fdecoded_options_count,
-					  decoded_options,
-					  decoded_options_count);
+					  &offload_fdecoded_options,
+					  &decoded_options);
       if (offload_names)
 	{
 	  find_offloadbeginend ();