PATCH: 2 stage BFD linker for LTO plugin

H.J. Lu hjl.tools@gmail.com
Mon Dec 6 02:21:00 GMT 2010


On Sun, Dec 5, 2010 at 4:11 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Dec 5, 2010 at 10:22 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Sat, Dec 4, 2010 at 4:43 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Sat, Dec 4, 2010 at 9:34 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Fri, Dec 3, 2010 at 10:07 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>> On Fri, Dec 3, 2010 at 6:34 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>> On Fri, Dec 3, 2010 at 6:23 PM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>>>>>>> On 04/12/2010 01:24, H.J. Lu wrote:
>>>>>>>
>>>>>>>> I checked in a patch to implement stage 2 linking. Everything
>>>>>>>> seems to work, including "gcc -static ... -lm".
>>>>>>>
>>>>>>>  Any chance you could send a complete diff?
>>>>>>>
>>>>>>
>>>>>> I will submit a complete diff after I fix a few corner cases.
>>>>>> In the meantime, you can clone my git tree and do a "git diff".
>>>>>>
>>>>>
>>>>> Hi,
>>>>>
>>>>> This patch implements 2 stage BFD linker for LTO plugin.
>>>>> It works with current LTO API on all cases I tested.
>>>>>
>>>>> Known issue:  --whole-archive will call plugin on archives with IR
>>>>> in stage 2 linking. But ld never calls plugin to get back object files.
>>>>> I will try to avoid it in a follow up patch.
>>>>>
>>>>
>>>> This turns out not a problem. In stage 2 linking, for --whole-archive
>>>> we call plugin to get symbols in the IR element of an archive and
>>>> it will be ignored for stage 2 linking.  It is OK since we already get
>>>> the trans object files back for stage 2 linking.
>>>>
>>>> BTW, the new linker passed bootstrap-lto with all default languages.
>>>> I am planning to include this patch in the next Linux binutils.
>>>>
>>>
>>> I missed the IR object in an archive:
>>>
>>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42690#c34
>>>
>>> This updated patch fixed it.  OK for trunk?
>>>
>>
>> We shouldn't clear SEC_EXCLUDE if BFD_PLUGIN is set:
>>
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42690#c38
>>
>> This updated patch fixed it.  OK for trunk?
>>
>
> It turns out that my patch also fixes:
>
> http://sourceware.org/bugzilla/show_bug.cgi?id=12277
>

Updated patch, adjusted for plugin ELF symbol visibility bug fix.
OK for trunk?

Thanks.


-- 
H.J.
-------------- next part --------------
bfd/

2010-12-05  H.J. Lu  <hongjiu.lu@intel.com>

	PR ld/12248
	PR ld/12277
	* bfd.c (BFD_PLUGIN): New.
	(BFD_FLAGS_SAVED): Add BFD_PLUGIN.
	(BFD_FLAGS_FOR_BFD_USE_MASK): Likewise.

	* bfd-in2.h: Regenerated.

ld/

2010-12-05  H.J. Lu  <hongjiu.lu@intel.com>

	PR ld/12248
	PR ld/12277
	* ldfile.c (ldfile_try_open_bfd): Set BFD_PLUGIN for plugin. Set
	stage1.

	* ldlang.c (cmdline_list): New.
	(cmdline_next_claimed_output): Likewise.
	(cmdline_list_init): Likewise.
	(cmdline_get_stage2_input_files): Likewise.
	(debug_cmdline_list): Likewise.
	(cmdline_list_append): Likewise.
	(cmdline_set_next_claimed_output): Likewise.
	(cmdline_list_insert_claimed_output): Likewise.
	(new_afile): Set stage1 to FALSE;
	(lang_init): Call cmdline_list_init.
	(lang_gc_sections): Don't clear SEC_EXCLUDE if BFD_PLUGIN is
	set.
	(lang_process): Call plugin_active_plugins_p to check plugin
	support.  Check cmdline_next_claimed_output before opening
	stage 2 input.  Call debug_cmdline_list if trace_file_tries
	is set.  Call cmdline_get_stage2_input_files to get stage 2
	input files.

	* ldlang.h (lang_input_statement_struct): Add stage1.
	(cmdline_enum_type): New.
	(cmdline_header_type): Likewise.
	(cmdline_input_statement_type): Likewise.
	(cmdline_claimed_output_type): Likewise.
	(cmdline_union_type): Likewise.
	(cmdline_list_type): Likewise.
	(cmdline_list_append): Likewise.
	(cmdline_list_insert_claimed_output): Likewise.
	(cmdline_set_next_claimed_output): Likewise.

	* ldmain.c (add_archive_element): Call
	cmdline_set_next_claimed_output with archive BFD.  Set
	BFD_PLUGIN for plugin.

	* lexsup.c (ld_options): Add -flto and -flto-partition= for
	GCC LTO option compatibility.
	(parse_args): Call cmdline_list_append if needed.

	* plugin.c (plugin_opt_plugin_arg): Ignore -pass-through=.
	(add_input_file): Replace lang_add_input_file with
	cmdline_list_insert_claimed_output.
	(add_input_library): Likewise.

ld/testsuite/

2010-12-05  H.J. Lu  <hongjiu.lu@intel.com>

	PR ld/12248
	PR ld/12277
	* ld-plugin/func1p.c: New.
	* ld-plugin/func2.c: Likewise.
	* ld-plugin/func2i.c: Likewise.
	* ld-plugin/func3h.c: Likewise.

	* ld-plugin/plugin.exp: Add object files for symbols claimed
	or created by testplugin.
	* ld-plugin/plugin-7.d: Updated.
	* ld-plugin/plugin-8.d: Likewise.
	* ld-plugin/plugin-9.d: Likewise.

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index e7805b6..10dbe83 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5085,14 +5085,17 @@ struct bfd
   /* Decompress sections in this BFD.  */
 #define BFD_DECOMPRESS 0x10000
 
+  /* This BFD has been processed by the linker plugin.  */
+#define BFD_PLUGIN 0x20000
+
   /* Flags bits to be saved in bfd_preserve_save.  */
 #define BFD_FLAGS_SAVED \
-  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS)
+  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN)
 
   /* Flags bits which are for BFD use only.  */
 #define BFD_FLAGS_FOR_BFD_USE_MASK \
   (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \
-   | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT)
+   | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT | BFD_PLUGIN)
 
   /* Currently my_archive is tested before adding origin to
      anything. I believe that this can become always an add of
diff --git a/bfd/bfd.c b/bfd/bfd.c
index a9ce7cc..7265156 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -157,14 +157,17 @@ CODE_FRAGMENT
 .  {* Decompress sections in this BFD.  *}
 .#define BFD_DECOMPRESS 0x10000
 .
+.  {* This BFD has been processed by the linker plugin.  *}
+.#define BFD_PLUGIN 0x20000
+.
 .  {* Flags bits to be saved in bfd_preserve_save.  *}
 .#define BFD_FLAGS_SAVED \
-.  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS)
+.  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN)
 .
 .  {* Flags bits which are for BFD use only.  *}
 .#define BFD_FLAGS_FOR_BFD_USE_MASK \
 .  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \
-.   | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT)
+.   | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT | BFD_PLUGIN)
 .
 .  {* Currently my_archive is tested before adding origin to
 .     anything. I believe that this can become always an add of
diff --git a/ld/ldfile.c b/ld/ldfile.c
index a9a6954..9601a45 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -312,46 +312,64 @@ success:
      bfd_object that it sets the bfd's arch and mach, which
      will be needed when and if we want to bfd_create a new
      one using this one as a template.  */
-  if (bfd_check_format (entry->the_bfd, bfd_object)
-      && plugin_active_plugins_p ())
+  if (plugin_active_plugins_p ())
     {
-      int fd = open (attempt, O_RDONLY | O_BINARY);
-      if (fd >= 0)
+      if (bfd_check_format (entry->the_bfd, bfd_object))
 	{
-	  struct ld_plugin_input_file file;
-	  int claimed = 0;
-
-	  file.name = attempt;
-	  file.offset = 0;
-	  file.filesize = lseek (fd, 0, SEEK_END);
-	  file.fd = fd;
-	  /* We create a dummy BFD, initially empty, to house
-	     whatever symbols the plugin may want to add.  */
-	  file.handle = plugin_get_ir_dummy_bfd (attempt, entry->the_bfd);
-	  if (plugin_call_claim_file (&file, &claimed))
-	    einfo (_("%P%F: %s: plugin reported error claiming file\n"),
-		   plugin_error_plugin ());
-	  /* fd belongs to us, not the plugin; but we don't need it.  */
-	  close (fd);
-	  if (claimed)
+	  int fd = open (attempt, O_RDONLY | O_BINARY);
+	  if (fd >= 0)
 	    {
-	      /* Discard the real file's BFD and substitute the dummy one.  */
-	      bfd_close (entry->the_bfd);
-	      entry->the_bfd = file.handle;
-	      entry->claimed = TRUE;
-	      bfd_make_readable (entry->the_bfd);
-	    }
-	  else
-	    {
-	      /* If plugin didn't claim the file, we don't need the dummy
-		 bfd.  Can't avoid speculatively creating it, alas.  */
-	      bfd_close_all_done (file.handle);
-	      entry->claimed = FALSE;
+	      struct ld_plugin_input_file file;
+	      int claimed = 0;
+
+	      file.name = attempt;
+	      file.offset = 0;
+	      file.filesize = lseek (fd, 0, SEEK_END);
+	      file.fd = fd;
+	      /* We create a dummy BFD, initially empty, to house
+		 whatever symbols the plugin may want to add.  */
+	      file.handle = plugin_get_ir_dummy_bfd (attempt,
+						     entry->the_bfd);
+	      if (plugin_call_claim_file (&file, &claimed))
+		einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+		       plugin_error_plugin ());
+	      /* fd belongs to us, not the plugin; but we don't need it.  */
+	      close (fd);
+	      if (claimed)
+		{
+		  /* Discard the real file's BFD and substitute the
+		    dummy one.  */
+		  bfd_close (entry->the_bfd);
+		  entry->the_bfd = file.handle;
+		  entry->claimed = TRUE;
+		  bfd_make_readable (entry->the_bfd);
+		  cmdline_set_next_claimed_output (NULL);
+		}
+	      else
+		{
+		  /* If plugin didn't claim the file, we don't need the
+		     dummy bfd.  Can't avoid speculatively creating it,
+		     alas.  */
+		  bfd_close_all_done (file.handle);
+		  entry->claimed = FALSE;
+		}
+
+	      /* Mark this input has been processed by plugin.  */
+	      entry->the_bfd->flags |= BFD_PLUGIN;
 	    }
 	}
+      else 
+	{
+	  /* Mark this input has been processed by plugin.  */
+	  entry->the_bfd->flags |= BFD_PLUGIN;
+	  entry->claimed = FALSE;
+	}
     }
 #endif /* ENABLE_PLUGINS */
 
+  /* Stage 1 is done.  */
+  entry->stage1 = TRUE;
+
   /* It opened OK, the format checked out, and the plugins have had
      their chance to claim it, so this is success.  */
   return TRUE;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index e804a53..1453db3 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -70,6 +70,8 @@ static lang_statement_list_type *stat_save[10];
 static lang_statement_list_type **stat_save_ptr = &stat_save[0];
 static struct unique_sections *unique_section_list;
 static bfd_boolean ldlang_sysrooted_script = FALSE;
+static cmdline_list_type cmdline_list;
+static cmdline_union_type **cmdline_next_claimed_output;
 
 /* Forward declarations.  */
 static void exp_init_os (etree_type *);
@@ -90,6 +92,9 @@ static void lang_record_phdrs (void);
 static void lang_do_version_exports_section (void);
 static void lang_finalize_version_expr_head
   (struct bfd_elf_version_expr_head *);
+static void cmdline_list_init (void);
+static void cmdline_get_stage2_input_files (void);
+static void debug_cmdline_list (void);
 
 /* Exported variables.  */
 const char *output_target;
@@ -1003,6 +1008,7 @@ new_afile (const char *name,
   lang_has_input_file = TRUE;
   p->target = target;
   p->sysrooted = FALSE;
+  p->stage1 = FALSE;
 
   if (file_type == lang_input_file_is_l_enum
       && name[0] == ':' && name[1] != '\0')
@@ -1177,6 +1183,8 @@ lang_init (void)
 
   output_section_statement_table_init ();
 
+  cmdline_list_init ();
+
   lang_list_init (stat_ptr);
 
   lang_list_init (&input_file_chain);
@@ -6249,10 +6257,15 @@ lang_gc_sections (void)
     {
       LANG_FOR_EACH_INPUT_STATEMENT (f)
 	{
-	  asection *sec;
-	  for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
-	    if ((sec->flags & SEC_DEBUGGING) == 0)
-	      sec->flags &= ~SEC_EXCLUDE;
+	  /* Don't clear SEC_EXCLUDE if BFD_PLUGIN is set.  */
+	  if ((f->the_bfd->flags & BFD_PLUGIN) == 0)
+	    {
+	      asection *sec;
+	      for (sec = f->the_bfd->sections; sec != NULL;
+		   sec = sec->next)
+		if ((sec->flags & SEC_DEBUGGING) == 0)
+		  sec->flags &= ~SEC_EXCLUDE;
+	    }
 	}
     }
 
@@ -6418,21 +6431,77 @@ lang_process (void)
   open_input_bfds (statement_list.head, FALSE);
 
 #ifdef ENABLE_PLUGINS
+  /* We call plugin_active_plugins_p () to check any plugin errors
+     from plugin_call_all_symbols_read ().  */
+  if (plugin_active_plugins_p ())
     {
+      bfd *p;
       union lang_statement_union **listend;
-      /* Now all files are read, let the plugin(s) decide if there
-	 are any more to be added to the link before we call the
+      /* Now all files are read, get outputs from input files claimed
+	 by plugin(s) and prepare for stage 2 linking before we call the
 	 emulation's after_open hook.  */
       listend = statement_list.tail;
       ASSERT (!*listend);
       if (plugin_call_all_symbols_read ())
 	einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
 	       plugin_error_plugin ());
-      /* If any new files were added, they will be on the end of the
-	 statement list, and we can open them now by getting open_input_bfds
-	 to carry on from where it ended last time.  */
-      if (*listend)
-	open_input_bfds (*listend, FALSE);
+
+      if (cmdline_next_claimed_output)
+	{
+	  if (trace_file_tries)
+	    debug_cmdline_list ();
+
+	  /* Get stage 2 input files.  */
+	  cmdline_get_stage2_input_files ();
+
+	  /* Must have stage 2 inputs.  */
+	  ASSERT (*listend);
+
+	  /* Exclude all sections in input bfd files processed by the
+	     plugin.  */
+	  for (p = link_info.input_bfds; p != (bfd *) NULL;
+	       p = p->link_next)
+	    {
+	      bfd_boolean exclude = (p->flags & BFD_PLUGIN) != 0;
+	      if (!exclude)
+		{
+		  bfd *abfd = bfd_my_archive (p);
+		  if (abfd)
+		    exclude = (abfd->flags & BFD_PLUGIN) != 0;
+		}
+	      if (exclude)
+		{
+		  asection *sec;
+
+		  if (trace_files || trace_file_tries)
+		    info_msg (_("exclude stage 1 input: %B\n"), p);
+
+		  for (sec = p->sections; sec != NULL; sec = sec->next)
+		    sec->flags |= SEC_EXCLUDE;
+		}
+	      else if (trace_files || trace_file_tries)
+		info_msg (_("keep stage 1 input: %B\n"), p);
+	    }
+
+	  /* Free the old already linked table and create a new one.  */
+	  bfd_section_already_linked_table_free ();
+	  if (!bfd_section_already_linked_table_init ())
+	    einfo (_("%P%F: Failed to create hash table\n"));
+
+	  /* Free the old hash table and create a new one.  */
+	  bfd_link_hash_table_free (link_info.output_bfd,
+				    link_info.hash);
+	  link_info.hash
+	    = bfd_link_hash_table_create (link_info.output_bfd);
+	  if (link_info.hash == NULL)
+	    einfo (_("%P%F: can not create hash table: %E\n"));
+
+	  /* When stage 2 input files are added, they will be on the
+	     end of the statement list, and we can open them now by
+	     getting open_input_bfds to carry on from where it ended
+	     last time.  */
+	  open_input_bfds (*listend, FALSE);
+	}
     }
 #endif /* ENABLE_PLUGINS */
 
@@ -7852,3 +7921,166 @@ lang_append_dynamic_list_cpp_new (void)
 
   lang_append_dynamic_list (dynamic);
 }
+
+static void
+cmdline_list_init (void)
+{
+  cmdline_list.tail = &cmdline_list.head;
+}
+
+/* Append a cmmand line option to cmdline_list.  */
+
+void
+cmdline_list_append (cmdline_enum_type type, void *data)
+{
+  cmdline_union_type *new_opt;
+
+  new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+  new_opt->header.type = type;
+  new_opt->header.next = NULL;
+  *cmdline_list.tail = new_opt;
+  cmdline_list.tail = &new_opt->header.next;
+  if (type == cmdline_is_lang_input_statement_enum)
+    new_opt->input_statement.input = (lang_input_statement_type *) data;
+}
+
+/* Set cmdline_next_claimed_output.  */
+
+void
+cmdline_set_next_claimed_output (bfd *archive)
+{
+  if (!cmdline_next_claimed_output)
+    {
+      cmdline_union_type *c;
+      cmdline_next_claimed_output = &cmdline_list.head;
+      for (c = cmdline_list.head; c != NULL; c = c->header.next)
+	if (c->header.type == cmdline_is_lang_input_statement_enum)
+	  {
+	    /* We must put object files generated by plugin before
+	       the archive containing the IR object.  */
+	    if (archive
+		&& archive == c->input_statement.input->the_bfd)
+	      {
+		cmdline_next_claimed_output = &c;
+		break;
+	      }
+	    else if (!c->input_statement.input->stage1)
+	      {
+		cmdline_next_claimed_output = &c->header.next;
+		break;
+	      }
+	  }
+    }
+}
+
+/* Insert claimed output after cmdline_next_claimed_output.  */
+
+void
+cmdline_list_insert_claimed_output (const char *output)
+{
+  cmdline_union_type *new_opt, *next;
+
+  if (trace_files || trace_file_tries)
+    info_msg (_("insert plugin claimed output: %s\n"), output);
+
+  new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+  new_opt->header.type = cmdline_is_claimed_output_enum;
+  new_opt->claimed_output.output = output;
+
+  next = *cmdline_next_claimed_output;
+  *cmdline_next_claimed_output = new_opt;
+  new_opt->header.next = next;
+  if (cmdline_list.tail == cmdline_next_claimed_output)
+    {
+      cmdline_next_claimed_output = &new_opt->header.next;
+      cmdline_list.tail = cmdline_next_claimed_output;
+    }
+  else
+    cmdline_next_claimed_output = &new_opt->header.next;
+}
+
+/* Get stage 2 input files.  */
+
+static void
+cmdline_get_stage2_input_files (void)
+{
+  cmdline_union_type *c;
+  int ingroup = 0;
+
+  for (c = cmdline_list.head; c != NULL; c = c->header.next)
+    switch (c->header.type)
+      {
+      default:
+	abort ();
+      case cmdline_is_lang_input_statement_enum:
+	if (!c->input_statement.input->claimed)
+	  {
+	    lang_input_statement_type *input;
+	    input = lang_add_input_file (c->input_statement.input->filename,
+					 lang_input_file_is_file_enum,
+					 NULL);
+	    input->add_DT_NEEDED_for_dynamic
+	      = c->input_statement.input->add_DT_NEEDED_for_dynamic;
+	    input->add_DT_NEEDED_for_regular
+	      = c->input_statement.input->add_DT_NEEDED_for_regular;
+	    input->whole_archive
+	      = c->input_statement.input->whole_archive;
+	  }
+	break;
+      case cmdline_is_claimed_output_enum:
+	lang_add_input_file (c->claimed_output.output,
+			     lang_input_file_is_file_enum, NULL);
+	break;
+      case cmdline_is_enter_group_enum:
+	lang_enter_group ();
+	ingroup++;
+	break;
+      case cmdline_is_leave_group_enum:
+	lang_leave_group ();
+	ingroup--;
+	break;
+      }
+
+  while (ingroup)
+    {
+      lang_leave_group ();
+      ingroup--;
+    }
+}
+
+static void
+debug_cmdline_list (void)
+{
+  cmdline_union_type *c;
+
+  info_msg (_("Stage 2 command line:\n "));
+
+  for (c = cmdline_list.head; c != NULL; c = c->header.next)
+    switch (c->header.type)
+      {
+      default:
+	abort ();
+      case cmdline_is_lang_input_statement_enum:
+	if (!c->input_statement.input->claimed)
+	  {
+	    if (c->input_statement.input->the_bfd
+		&& (bfd_get_format (c->input_statement.input->the_bfd)
+		    == bfd_archive))
+	      info_msg (" %s", (c->input_statement.input->whole_archive
+			      ? "--whole-archive" : "--no-whole-archive"));
+	    info_msg (" %s", c->input_statement.input->filename);
+	  }
+	break;
+      case cmdline_is_claimed_output_enum:
+	info_msg (" %s", c->claimed_output.output);
+	break;
+      case cmdline_is_enter_group_enum:
+	info_msg (" --start-group");
+	break;
+      case cmdline_is_leave_group_enum:
+	info_msg (" --end-group");
+	break;
+      }
+
+  info_msg ("\n");
+}
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 15e5587..f41fe44 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -279,6 +279,9 @@ typedef struct lang_input_statement_struct
   /* Whether to include the entire contents of an archive.  */
   unsigned int whole_archive : 1;
 
+  /* Set if the file has been processed in stage 1 .  */
+  unsigned int stage1 : 1;
+
   /* Set when bfd opening is successful.  */
   unsigned int loaded : 1;
 
@@ -650,4 +653,47 @@ extern bfd_boolean
 ldlang_override_segment_assignment
   (struct bfd_link_info *, bfd *, asection *, asection *, bfd_boolean);
 
+typedef enum
+{
+  cmdline_is_lang_input_statement_enum,
+  cmdline_is_claimed_output_enum,
+  cmdline_is_enter_group_enum,
+  cmdline_is_leave_group_enum,
+} cmdline_enum_type;
+
+typedef struct cmdlin_header_struct
+{
+  union cmdline_union *next;
+  cmdline_enum_type type;
+} cmdline_header_type;
+
+typedef struct cmdline_input_statement_struct
+{
+  cmdline_header_type header;
+  lang_input_statement_type *input;
+} cmdline_input_statement_type;
+
+typedef struct cmdline_claimed_output_struct
+{
+  cmdline_header_type header;
+  const char *output;
+} cmdline_claimed_output_type;
+
+typedef union cmdline_union
+{
+  cmdline_header_type header;
+  cmdline_input_statement_type input_statement;
+  cmdline_claimed_output_type claimed_output;
+} cmdline_union_type;
+
+typedef struct cmdline_list
+{
+  cmdline_union_type *head;
+  cmdline_union_type **tail;
+} cmdline_list_type;
+
+extern void cmdline_list_append (cmdline_enum_type, void *);
+extern void cmdline_list_insert_claimed_output (const char *);
+extern void cmdline_set_next_claimed_output (bfd *);
+
 #endif
diff --git a/ld/ldmain.c b/ld/ldmain.c
index c5b7d88..8f30a69 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -798,6 +798,7 @@ add_archive_element (struct bfd_link_info *info,
 {
   lang_input_statement_type *input;
   lang_input_statement_type orig_input;
+  bfd *archive;
 
   input = (lang_input_statement_type *)
       xcalloc (1, sizeof (lang_input_statement_type));
@@ -809,20 +810,31 @@ add_archive_element (struct bfd_link_info *info,
      (if enabled) may possibly alter it to point to a replacement
      BFD, but we still want to output the original BFD filename.  */
   orig_input = *input;
+  archive = bfd_my_archive (abfd);
 #ifdef ENABLE_PLUGINS
-  if (bfd_my_archive (abfd) != NULL && plugin_active_plugins_p ())
+  if (plugin_active_plugins_p ())
     {
-      /* We must offer this archive member to the plugins to claim.  */
-      int fd = open (bfd_my_archive (abfd)->filename, O_RDONLY | O_BINARY);
-      if (fd >= 0)
+      if (archive == NULL)
+	info_msg (_("plugin ignored non-archive member: %B\n"), abfd);
+      else
 	{
+	  /* We must offer this archive member to the plugins to claim.  */
+	  int fd = open (archive->filename, O_RDONLY | O_BINARY);
 	  struct ld_plugin_input_file file;
 	  int claimed = 0;
+
+	  if (fd < 0)
+	    {
+	      bfd_set_error (bfd_error_system_call);
+	      einfo (_("%P%F: plugin cannot open archive %B: %E\n"),
+		     archive);
+	    }
+
 	  /* Offset and filesize must refer to the individual archive
 	     member, not the whole file, and must exclude the header.
 	     Fortunately for us, that is how the data is stored in the
 	     origin field of the bfd and in the arelt_data.  */
-	  file.name = bfd_my_archive (abfd)->filename;
+	  file.name = archive->filename;
 	  file.offset = abfd->origin;
 	  file.filesize = arelt_size (abfd);
 	  file.fd = fd;
@@ -836,6 +848,9 @@ add_archive_element (struct bfd_link_info *info,
 	  close (fd);
 	  if (claimed)
 	    {
+	      /* Need to put object files generated by plugin before
+		 the archive.  */
+	      cmdline_set_next_claimed_output (archive);
 	      /* Substitute the dummy BFD.  */
 	      input->the_bfd = file.handle;
 	      input->claimed = TRUE;
@@ -848,6 +863,9 @@ add_archive_element (struct bfd_link_info *info,
 	      bfd_close_all_done (file.handle);
 	      input->claimed = FALSE;
 	    }
+
+	  /* Mark this input has been processed by plugin.  */
+	  input->the_bfd->flags |= BFD_PLUGIN;
 	}
     }
 #endif /* ENABLE_PLUGINS */
@@ -898,16 +916,16 @@ add_archive_element (struct bfd_link_info *info,
 	  header_printed = TRUE;
 	}
 
-      if (bfd_my_archive (abfd) == NULL)
+      if (archive == NULL)
 	{
 	  minfo ("%s", bfd_get_filename (abfd));
 	  len = strlen (bfd_get_filename (abfd));
 	}
       else
 	{
-	  minfo ("%s(%s)", bfd_get_filename (bfd_my_archive (abfd)),
+	  minfo ("%s(%s)", bfd_get_filename (archive),
 		 bfd_get_filename (abfd));
-	  len = (strlen (bfd_get_filename (bfd_my_archive (abfd)))
+	  len = (strlen (bfd_get_filename (archive))
 		 + strlen (bfd_get_filename (abfd))
 		 + 2);
 	}
diff --git a/ld/lexsup.c b/ld/lexsup.c
index e4356bc..94fa2cd 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -283,6 +283,12 @@ static const struct ld_option ld_options[] =
     '\0', N_("PLUGIN"), N_("Load named plugin"), ONE_DASH },
   { {"plugin-opt", required_argument, NULL, OPTION_PLUGIN_OPT},
     '\0', N_("ARG"), N_("Send arg to last-loaded plugin"), ONE_DASH },
+  { {"flto", optional_argument, NULL, OPTION_IGNORE},
+    '\0', NULL, N_("Ignored for GCC LTO option compatibility"),
+    ONE_DASH },
+  { {"flto-partition=", required_argument, NULL, OPTION_IGNORE},
+    '\0', NULL, N_("Ignored for GCC LTO option compatibility"),
+    ONE_DASH },
 #endif /* ENABLE_PLUGINS */
   { {"Qy", no_argument, NULL, OPTION_IGNORE},
     '\0', NULL, N_("Ignored for SVR4 compatibility"), ONE_DASH },
@@ -719,6 +725,7 @@ parse_args (unsigned argc, char **argv)
     {
       int longind;
       int optc;
+      lang_input_statement_type *input;
 
       /* Using last_optind lets us avoid calling ldemul_parse_args
 	 multiple times on a single option, which would lead to
@@ -757,7 +764,10 @@ parse_args (unsigned argc, char **argv)
 	  einfo (_("%P%F: use the --help option for usage information\n"));
 
 	case 1:			/* File name.  */
-	  lang_add_input_file (optarg, lang_input_file_is_file_enum, NULL);
+	  input = lang_add_input_file (optarg,
+				       lang_input_file_is_file_enum, NULL);
+	  cmdline_list_append (cmdline_is_lang_input_statement_enum,
+			       input);
 	  break;
 
 	case OPTION_IGNORE:
@@ -914,7 +924,10 @@ parse_args (unsigned argc, char **argv)
 	  ldfile_add_library_path (optarg, TRUE);
 	  break;
 	case 'l':
-	  lang_add_input_file (optarg, lang_input_file_is_l_enum, NULL);
+	  input = lang_add_input_file (optarg,
+				       lang_input_file_is_l_enum, NULL);
+	  cmdline_list_append (cmdline_is_lang_input_statement_enum,
+			       input);
 	  break;
 	case 'M':
 	  config.map_filename = "-";
@@ -1099,9 +1112,11 @@ parse_args (unsigned argc, char **argv)
 	    if (stat (optarg, &s) >= 0
 		&& ! S_ISDIR (s.st_mode))
 	      {
-		lang_add_input_file (optarg,
-				     lang_input_file_is_symbols_only_enum,
-				     NULL);
+		input = lang_add_input_file (optarg,
+					     lang_input_file_is_symbols_only_enum,
+					     NULL);
+		cmdline_list_append (cmdline_is_lang_input_statement_enum,
+				     input);
 		break;
 	      }
 	  }
@@ -1483,6 +1498,7 @@ parse_args (unsigned argc, char **argv)
 	  break;
 	case '(':
 	  lang_enter_group ();
+	  cmdline_list_append (cmdline_is_enter_group_enum, NULL);
 	  ingroup++;
 	  break;
 	case ')':
@@ -1490,6 +1506,7 @@ parse_args (unsigned argc, char **argv)
 	    einfo (_("%P%F: group ended before it began (--help for usage)\n"));
 
 	  lang_leave_group ();
+	  cmdline_list_append (cmdline_is_leave_group_enum, NULL);
 	  ingroup--;
 	  break;
 
diff --git a/ld/plugin.c b/ld/plugin.c
index 8c919b6..d78c058 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -211,6 +211,17 @@ plugin_opt_plugin_arg (const char *arg)
   if (!last_plugin)
     return set_plugin_error (_("<no plugin>"));
 
+  /* Ignore -pass-through= from GCC driver.  */
+  if (*arg == '-')
+    {
+      const char *p;
+      for (p = arg + 1; p; p++)
+	if (*p != '-')
+	  break;
+      if (strncmp (p, "pass-through=", 13) == 0)
+	return 0;
+    }
+
   newarg = xmalloc (sizeof *newarg);
   newarg->arg = arg;
   newarg->next = NULL;
@@ -541,9 +552,7 @@ static enum ld_plugin_status
 add_input_file (const char *pathname)
 {
   ASSERT (called_plugin);
-  if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_file_enum,
-			    NULL))
-    return LDPS_ERR;
+  cmdline_list_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
@@ -552,9 +561,7 @@ static enum ld_plugin_status
 add_input_library (const char *pathname)
 {
   ASSERT (called_plugin);
-  if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_l_enum,
-			    NULL))
-    return LDPS_ERR;
+  cmdline_list_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
diff --git a/ld/testsuite/ld-plugin/func1p.c b/ld/testsuite/ld-plugin/func1p.c
new file mode 100644
index 0000000..917dcbb
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func1p.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("protected")))
+func1 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func2.c b/ld/testsuite/ld-plugin/func2.c
new file mode 100644
index 0000000..d233fcf
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func2.c
@@ -0,0 +1,7 @@
+
+extern int retval;
+
+int func2 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func2i.c b/ld/testsuite/ld-plugin/func2i.c
new file mode 100644
index 0000000..00d7cdd
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func2i.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("internal")))
+func2 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func3h.c b/ld/testsuite/ld-plugin/func3h.c
new file mode 100644
index 0000000..525de63
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func3h.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("hidden")))
+func3 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/plugin-7.d b/ld/testsuite/ld-plugin/plugin-7.d
index 75f25e0..ee7a6eb 100644
--- a/ld/testsuite/ld-plugin/plugin-7.d
+++ b/ld/testsuite/ld-plugin/plugin-7.d
@@ -19,7 +19,8 @@ tv\[16\]: LDPT_OPTION 'registerallsymbolsread'
 tv\[17\]: LDPT_OPTION 'registercleanup'
 tv\[18\]: LDPT_OPTION 'claim:tmpdir/func.o'
 tv\[19\]: LDPT_OPTION 'sym:_?func::0:0:0'
-tv\[20\]: LDPT_NULL value        0x0 \(0\)
+tv\[20\]: LDPT_OPTION 'add:tmpdir/func.o'
+tv\[21\]: LDPT_NULL value        0x0 \(0\)
 #...
 hook called: claim_file tmpdir/main.o \[@0/.* not claimed
 hook called: claim_file tmpdir/func.o \[@0/.* CLAIMED
diff --git a/ld/testsuite/ld-plugin/plugin-8.d b/ld/testsuite/ld-plugin/plugin-8.d
index e72b039..a4db899 100644
--- a/ld/testsuite/ld-plugin/plugin-8.d
+++ b/ld/testsuite/ld-plugin/plugin-8.d
@@ -21,7 +21,9 @@ tv\[18\]: LDPT_OPTION 'claim:tmpdir/func.o'
 tv\[19\]: LDPT_OPTION 'sym:_?func::0:0:0'
 tv\[20\]: LDPT_OPTION 'sym:_?func2::0:0:0'
 tv\[21\]: LDPT_OPTION 'dumpresolutions'
-tv\[22\]: LDPT_NULL value        0x0 \(0\)
+tv\[22\]: LDPT_OPTION 'add:tmpdir/func.o'
+tv\[23\]: LDPT_OPTION 'add:tmpdir/func2.o'
+tv\[24\]: LDPT_NULL value        0x0 \(0\)
 #...
 hook called: claim_file tmpdir/main.o \[@0/.* not claimed
 hook called: claim_file tmpdir/func.o \[@0/.* CLAIMED
diff --git a/ld/testsuite/ld-plugin/plugin-9.d b/ld/testsuite/ld-plugin/plugin-9.d
index b74f4a6..a4db899 100644
--- a/ld/testsuite/ld-plugin/plugin-9.d
+++ b/ld/testsuite/ld-plugin/plugin-9.d
@@ -22,7 +22,8 @@ tv\[19\]: LDPT_OPTION 'sym:_?func::0:0:0'
 tv\[20\]: LDPT_OPTION 'sym:_?func2::0:0:0'
 tv\[21\]: LDPT_OPTION 'dumpresolutions'
 tv\[22\]: LDPT_OPTION 'add:tmpdir/func.o'
-tv\[23\]: LDPT_NULL value        0x0 \(0\)
+tv\[23\]: LDPT_OPTION 'add:tmpdir/func2.o'
+tv\[24\]: LDPT_NULL value        0x0 \(0\)
 #...
 hook called: claim_file tmpdir/main.o \[@0/.* not claimed
 hook called: claim_file tmpdir/func.o \[@0/.* CLAIMED
diff --git a/ld/testsuite/ld-plugin/plugin.exp b/ld/testsuite/ld-plugin/plugin.exp
index 8952f1d..fd0f939 100644
--- a/ld/testsuite/ld-plugin/plugin.exp
+++ b/ld/testsuite/ld-plugin/plugin.exp
@@ -70,6 +70,7 @@ set plugin_nm_output ""
 if { $can_compile && \
 	(![ld_compile "$CC $CFLAGS" $srcdir/$subdir/main.c tmpdir/main.o] \
 	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/func.c tmpdir/func.o] \
+	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/func2.c tmpdir/func2.o] \
 	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/text.c tmpdir/text.o]) } {
     # Defer fail until we have list of tests set.
     set failed_compile 1
@@ -108,12 +109,15 @@ set plugin_tests [list \
     [list "plugin claimfile replace symbol" "-plugin $plugin_path $regclm \
 			$regas $regcln -plugin-opt claim:tmpdir/func.o \
 			-plugin-opt sym:${_}func::0:0:0 \
+			-plugin-opt add:tmpdir/func.o \
     $testobjfiles $libs" "" "" {{ld plugin-7.d}} "main.x" ] \
     [list "plugin claimfile resolve symbol" "-plugin $plugin_path $regclm \
 			$regas $regcln -plugin-opt claim:tmpdir/func.o \
 			-plugin-opt sym:${_}func::0:0:0 \
 			-plugin-opt sym:${_}func2::0:0:0 \
 			-plugin-opt dumpresolutions \
+			-plugin-opt add:tmpdir/func.o \
+			-plugin-opt add:tmpdir/func2.o \
     $testobjfiles $libs" "" "" {{ld plugin-8.d}} "main.x" ] \
     [list "plugin claimfile replace file" "-plugin $plugin_path $regclm \
 			$regas $regcln -plugin-opt claim:tmpdir/func.o \
@@ -121,6 +125,7 @@ set plugin_tests [list \
 			-plugin-opt sym:${_}func2::0:0:0 \
 			-plugin-opt dumpresolutions \
 			-plugin-opt add:tmpdir/func.o \
+			-plugin-opt add:tmpdir/func2.o \
     $testobjfiles $libs" "" "" {{ld plugin-9.d}} "main.x" ] \
 ]
 
@@ -152,6 +157,10 @@ set plugin_extra_elf_tests [list \
 			-plugin-opt sym:${_}func2::0:2:0 \
 			-plugin-opt sym:${_}func3::0:3:0 \
 			-plugin-opt dumpresolutions \
+			-plugin-opt add:tmpdir/func.o \
+			-plugin-opt add:tmpdir/func1p.o \
+			-plugin-opt add:tmpdir/func2i.o \
+			-plugin-opt add:tmpdir/func3h.o \
     $testobjfiles $libs" "" "" {{ld plugin-ignore.d} \
 				{readelf -s plugin-vis-1.d}} "main.x" ] \
 ]
@@ -170,7 +179,10 @@ if { !$can_compile || $failed_compile } {
 
 run_ld_link_tests $plugin_tests
 
-if { [is_elf_format] } {
+if { [is_elf_format] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func1p.c tmpdir/func1p.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func2i.c tmpdir/func2i.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func3h.c tmpdir/func3h.o] } {
     run_ld_link_tests $plugin_extra_elf_tests
 }
 


More information about the Binutils mailing list