public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* PATCH: 2 stage BFD linker for LTO plugin
@ 2010-12-04  6:07 H.J. Lu
  2010-12-04 17:34 ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-04  6:07 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

[-- Attachment #1: Type: text/plain, Size: 3029 bytes --]

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.

OK for trunk?

Thanks.

-- 
H.J.
---
bfd/

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

	PR driver/42690
	* 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-03  H.J. Lu  <hongjiu.lu@intel.com>

	PR driver/42690
	* ldfile.c (ldfile_try_open_bfd): Turn on BFD_PLUGIN and set
	claimed to false on non-object files and unclaimed object files.
	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_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.

	* lexsup.c (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-03  H.J. Lu  <hongjiu.lu@intel.com>

	PR driver/42690
	* ld-plugin/func1i.c: New.
	* ld-plugin/func2.c: Likewise.
	* ld-plugin/func2h.c: Likewise.
	* ld-plugin/func3p.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.

[-- Attachment #2: binutils-2stage-1.patch --]
[-- Type: text/plain, Size: 25934 bytes --]

bfd/

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

	PR driver/42690
	* 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-03  H.J. Lu  <hongjiu.lu@intel.com>

	* ldfile.c (ldfile_try_open_bfd): Turn on BFD_PLUGIN and set
	claimed to false on non-object files and unclaimed object files.
	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_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.

	* lexsup.c (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-03  H.J. Lu  <hongjiu.lu@intel.com>

	PR driver/42690
	* ld-plugin/func1i.c: New.
	* ld-plugin/func2.c: Likewise.
	* ld-plugin/func2h.c: Likewise.
	* ld-plugin/func3p.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 3d9feb5..3737db9 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -312,46 +312,63 @@ 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 ();
+		}
+	      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);
+		  /* Mark this input has been processed by plugin.  */
+		  entry->the_bfd->flags |= BFD_PLUGIN;
+		  entry->claimed = FALSE;
+		}
 	    }
 	}
+      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 f74c1b5..458d856 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);
@@ -6419,21 +6427,67 @@ 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 *abfd = bfd_my_archive (p);
+	      if (!abfd)
+		abfd = p;
+	      if ((abfd->flags & BFD_PLUGIN) != 0)
+		{
+		  asection *sec;
+		  for (sec = p->sections; sec != NULL; sec = sec->next)
+		    sec->flags |= SEC_EXCLUDE;
+		}
+	    }
+
+	  /* 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 */
 
@@ -7853,3 +7907,153 @@ 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 (void)
+{
+  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
+	    && !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;
+
+  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;
+
+  printf (_("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))
+	      printf (" %s", (c->input_statement.input->whole_archive
+			      ? "--whole-archive" : "--no-whole-archive"));
+	    printf (" %s", c->input_statement.input->filename);
+	  }
+	break;
+      case cmdline_is_claimed_output_enum:
+	printf (" %s", c->claimed_output.output);
+	break;
+      case cmdline_is_enter_group_enum:
+	printf (" --start-group");
+	break;
+      case cmdline_is_leave_group_enum:
+	printf (" --end-group");
+	break;
+      }
+
+  printf ("\n");
+}
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 0b7b43b..d0aea1d 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 (void);
+
 #endif
diff --git a/ld/lexsup.c b/ld/lexsup.c
index b6274f8..f3d8684 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -720,6 +720,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
@@ -758,7 +759,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:
@@ -915,7 +919,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 = "-";
@@ -1100,9 +1107,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;
 	      }
 	  }
@@ -1484,6 +1493,7 @@ parse_args (unsigned argc, char **argv)
 	  break;
 	case '(':
 	  lang_enter_group ();
+	  cmdline_list_append (cmdline_is_enter_group_enum, NULL);
 	  ingroup++;
 	  break;
 	case ')':
@@ -1491,6 +1501,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 6afc14b..c321996 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;
@@ -520,9 +531,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;
 }
 
@@ -531,9 +540,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/func1i.c b/ld/testsuite/ld-plugin/func1i.c
new file mode 100644
index 0000000..eb334a8
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func1i.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("internal")))
+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/func2h.c b/ld/testsuite/ld-plugin/func2h.c
new file mode 100644
index 0000000..9398108
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func2h.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("hidden")))
+func2 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func3p.c b/ld/testsuite/ld-plugin/func3p.c
new file mode 100644
index 0000000..fdba15b
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func3p.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("protected")))
+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..665c2b4 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/func1i.o \
+			-plugin-opt add:tmpdir/func2h.o \
+			-plugin-opt add:tmpdir/func3p.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/func1i.c tmpdir/func1i.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func2h.c tmpdir/func2h.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func3p.c tmpdir/func3p.o] } {
     run_ld_link_tests $plugin_extra_elf_tests
 }
 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-04  6:07 PATCH: 2 stage BFD linker for LTO plugin H.J. Lu
@ 2010-12-04 17:34 ` H.J. Lu
  2010-12-05  0:44   ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-04 17:34 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

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.


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-04 17:34 ` H.J. Lu
@ 2010-12-05  0:44   ` H.J. Lu
  2010-12-05 18:23     ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-05  0:44 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

[-- Attachment #1: Type: text/plain, Size: 1607 bytes --]

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?

Thanks.

-- 
H.J.
---

[-- Attachment #2: binutils-2stage-2.patch --]
[-- Type: text/plain, Size: 28767 bytes --]

bfd/

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

	PR ld/12248
	* 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-03  H.J. Lu  <hongjiu.lu@intel.com>

	PR ld/12248
	* ldfile.c (ldfile_try_open_bfd): Turn on BFD_PLUGIN and set
	claimed to false on non-object files and unclaimed object files.
	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_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 if input isn't claimed by plugin.

	* lexsup.c (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-03  H.J. Lu  <hongjiu.lu@intel.com>

	PR ld/12248
	* ld-plugin/func1i.c: New.
	* ld-plugin/func2.c: Likewise.
	* ld-plugin/func2h.c: Likewise.
	* ld-plugin/func3p.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 3d9feb5..a708dc2 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -312,46 +312,63 @@ 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);
+		  /* Mark this input has been processed by plugin.  */
+		  entry->the_bfd->flags |= BFD_PLUGIN;
+		  entry->claimed = FALSE;
+		}
 	    }
 	}
+      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 2927ec8..1ff8e97 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);
@@ -6418,21 +6426,71 @@ 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;
+		  for (sec = p->sections; sec != NULL; sec = sec->next)
+		    sec->flags |= SEC_EXCLUDE;
+		}
+	    }
+
+	  /* 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 +7910,162 @@ 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 == 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;
+
+  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;
+
+  printf (_("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))
+	      printf (" %s", (c->input_statement.input->whole_archive
+			      ? "--whole-archive" : "--no-whole-archive"));
+	    printf (" %s", c->input_statement.input->filename);
+	  }
+	break;
+      case cmdline_is_claimed_output_enum:
+	printf (" %s", c->claimed_output.output);
+	break;
+      case cmdline_is_enter_group_enum:
+	printf (" --start-group");
+	break;
+      case cmdline_is_leave_group_enum:
+	printf (" --end-group");
+	break;
+      }
+
+  printf ("\n");
+}
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 0b7b43b..8ba3e5a 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..7810a01 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -798,6 +798,9 @@ add_archive_element (struct bfd_link_info *info,
 {
   lang_input_statement_type *input;
   lang_input_statement_type orig_input;
+#ifdef ENABLE_PLUGINS
+  bfd *archive;
+#endif
 
   input = (lang_input_statement_type *)
       xcalloc (1, sizeof (lang_input_statement_type));
@@ -810,10 +813,11 @@ add_archive_element (struct bfd_link_info *info,
      BFD, but we still want to output the original BFD filename.  */
   orig_input = *input;
 #ifdef ENABLE_PLUGINS
-  if (bfd_my_archive (abfd) != NULL && plugin_active_plugins_p ())
+  archive = bfd_my_archive (abfd);
+  if (archive != NULL && 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);
+      int fd = open (archive->filename, O_RDONLY | O_BINARY);
       if (fd >= 0)
 	{
 	  struct ld_plugin_input_file file;
@@ -822,7 +826,7 @@ add_archive_element (struct bfd_link_info *info,
 	     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 +840,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;
@@ -846,6 +853,8 @@ add_archive_element (struct bfd_link_info *info,
 	    {
 	      /* Abandon the dummy BFD.  */
 	      bfd_close_all_done (file.handle);
+	      /* Mark this input has been processed by plugin.  */
+	      input->the_bfd->flags |= BFD_PLUGIN;
 	      input->claimed = FALSE;
 	    }
 	}
diff --git a/ld/lexsup.c b/ld/lexsup.c
index e4356bc..722bbf6 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -719,6 +719,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 +758,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 +918,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 +1106,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 +1492,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 +1500,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 6afc14b..d9f7162 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;
@@ -520,9 +531,9 @@ 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;
+  if (trace_files || trace_file_tries)
+    info_msg ("Plugin add input file: %s\n", pathname);
+  cmdline_list_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
@@ -531,9 +542,9 @@ 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;
+  if (trace_files || trace_file_tries)
+    info_msg ("Plugin add input library: %s\n", pathname);
+  cmdline_list_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
diff --git a/ld/testsuite/ld-plugin/func1i.c b/ld/testsuite/ld-plugin/func1i.c
new file mode 100644
index 0000000..eb334a8
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func1i.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("internal")))
+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/func2h.c b/ld/testsuite/ld-plugin/func2h.c
new file mode 100644
index 0000000..9398108
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func2h.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("hidden")))
+func2 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func3p.c b/ld/testsuite/ld-plugin/func3p.c
new file mode 100644
index 0000000..fdba15b
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func3p.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("protected")))
+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..665c2b4 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/func1i.o \
+			-plugin-opt add:tmpdir/func2h.o \
+			-plugin-opt add:tmpdir/func3p.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/func1i.c tmpdir/func1i.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func2h.c tmpdir/func2h.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func3p.c tmpdir/func3p.o] } {
     run_ld_link_tests $plugin_extra_elf_tests
 }
 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-05  0:44   ` H.J. Lu
@ 2010-12-05 18:23     ` H.J. Lu
  2010-12-06  0:11       ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-05 18:23 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

[-- Attachment #1: Type: text/plain, Size: 1881 bytes --]

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?

Thanks.


-- 
H.J.

[-- Attachment #2: binutils-2stage-4.patch --]
[-- Type: text/plain, Size: 31182 bytes --]

bfd/

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

	PR ld/12248
	* 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
	* 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
	* ld-plugin/func1i.c: New.
	* ld-plugin/func2.c: Likewise.
	* ld-plugin/func2h.c: Likewise.
	* ld-plugin/func3p.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 3d9feb5..0ec49bf 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 2927ec8..a955503 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 0b7b43b..8ba3e5a 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 6afc14b..c321996 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;
@@ -520,9 +531,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;
 }
 
@@ -531,9 +540,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/func1i.c b/ld/testsuite/ld-plugin/func1i.c
new file mode 100644
index 0000000..eb334a8
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func1i.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("internal")))
+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/func2h.c b/ld/testsuite/ld-plugin/func2h.c
new file mode 100644
index 0000000..9398108
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func2h.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("hidden")))
+func2 (void)
+{
+  return retval;
+}
diff --git a/ld/testsuite/ld-plugin/func3p.c b/ld/testsuite/ld-plugin/func3p.c
new file mode 100644
index 0000000..fdba15b
--- /dev/null
+++ b/ld/testsuite/ld-plugin/func3p.c
@@ -0,0 +1,8 @@
+extern int retval;
+
+int
+__attribute__ ((visibility ("protected")))
+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..665c2b4 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/func1i.o \
+			-plugin-opt add:tmpdir/func2h.o \
+			-plugin-opt add:tmpdir/func3p.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/func1i.c tmpdir/func1i.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func2h.c tmpdir/func2h.o] \
+     && [ld_compile "$CC $CFLAGS" $srcdir/$subdir/func3p.c tmpdir/func3p.o] } {
     run_ld_link_tests $plugin_extra_elf_tests
 }
 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-05 18:23     ` H.J. Lu
@ 2010-12-06  0:11       ` H.J. Lu
  2010-12-06  2:21         ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06  0:11 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

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


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06  0:11       ` H.J. Lu
@ 2010-12-06  2:21         ` H.J. Lu
  2010-12-06 16:59           ` Dave Korn
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06  2:21 UTC (permalink / raw)
  To: Dave Korn; +Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

[-- Attachment #1: Type: text/plain, Size: 2319 bytes --]

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.

[-- Attachment #2: binutils-2stage-5.patch --]
[-- Type: text/plain, Size: 31221 bytes --]

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
 }
 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06  2:21         ` H.J. Lu
@ 2010-12-06 16:59           ` Dave Korn
  2010-12-06 17:20             ` H.J. Lu
                               ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Dave Korn @ 2010-12-06 16:59 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On 06/12/2010 02:20, H.J. Lu wrote:

>>>>> 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?

  Well, I reckon this patch is great (but don't have the approval rights).
It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
underway without anything abnormal showing up.

> +	  /* 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"));

  If I had known that there was really this little stored state to be unwound
and regenerated, I would have wanted to do it this way in the first place.

> +typedef struct cmdlin_header_struct

  Typo there.

  Tristan, sorry, you must be sick of hearing from me by now, but I notice the
branch was still labile a couple of hours ago... it would be really good if we
could get HJ's patch approved and backported before you spin the release.

    cheers,
      DaveK

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 16:59           ` Dave Korn
@ 2010-12-06 17:20             ` H.J. Lu
  2010-12-07 20:12               ` H.J. Lu
  2010-12-06 17:44             ` H.J. Lu
  2010-12-07  8:33             ` Tristan Gingold
  2 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06 17:20 UTC (permalink / raw)
  To: Dave Korn
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
> On 06/12/2010 02:20, H.J. Lu wrote:
>
>>>>>> 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?
>
>  Well, I reckon this patch is great (but don't have the approval rights).
> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
> underway without anything abnormal showing up.
>
>> +       /* 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"));
>
>  If I had known that there was really this little stored state to be unwound
> and regenerated, I would have wanted to do it this way in the first place.
>
>> +typedef struct cmdlin_header_struct
>
>  Typo there.

Fixed.

>  Tristan, sorry, you must be sick of hearing from me by now, but I notice the
> branch was still labile a couple of hours ago... it would be really good if we
> could get HJ's patch approved and backported before you spin the release.

I also fixed a bunch other plugin bugs on trrunk.  If we want a BFD linker with
working plugin support in binutils 2.21, we should just copy all pluging changes
on my hjl/lto branch at

http://git.kernel.org/?p=devel/binutils/hjl/x86.git;a=summary

into 2.21.

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 16:59           ` Dave Korn
  2010-12-06 17:20             ` H.J. Lu
@ 2010-12-06 17:44             ` H.J. Lu
  2010-12-06 17:55               ` Dave Korn
  2010-12-07  8:33             ` Tristan Gingold
  2 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06 17:44 UTC (permalink / raw)
  To: Dave Korn
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>
>  Well, I reckon this patch is great (but don't have the approval rights).
> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
> underway without anything abnormal showing up.
>

Can we close

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42690

as invalid and point the linker bug to

http://www.sourceware.org/bugzilla/show_bug.cgi?id=12248

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 17:44             ` H.J. Lu
@ 2010-12-06 17:55               ` Dave Korn
  2010-12-06 17:57                 ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: Dave Korn @ 2010-12-06 17:55 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On 06/12/2010 17:44, H.J. Lu wrote:
> On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>>  Well, I reckon this patch is great (but don't have the approval rights).
>> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
>> underway without anything abnormal showing up.
>>
> 
> Can we close
> 
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42690
> 
> as invalid

  It's not invalid; GOLD linking with -static (explicit or implicit) would
still have problems without the pass through.

> and point the linker bug to
> 
> http://www.sourceware.org/bugzilla/show_bug.cgi?id=12248

  I added a reference to it to the gcc PR and closed it.

    cheers,
      DaveK

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 17:55               ` Dave Korn
@ 2010-12-06 17:57                 ` H.J. Lu
  2010-12-06 23:29                   ` Alan Modra
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06 17:57 UTC (permalink / raw)
  To: Dave Korn
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On Mon, Dec 6, 2010 at 10:19 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
> On 06/12/2010 17:44, H.J. Lu wrote:
>> On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>>>  Well, I reckon this patch is great (but don't have the approval rights).
>>> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
>>> underway without anything abnormal showing up.
>>>
>>
>> Can we close
>>
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42690
>>
>> as invalid
>
>  It's not invalid; GOLD linking with -static (explicit or implicit) would
> still have problems without the pass through.
>

Personally, I think 2 stage linking is one way to fix this issue.


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 17:57                 ` H.J. Lu
@ 2010-12-06 23:29                   ` Alan Modra
  2010-12-06 23:32                     ` H.J. Lu
                                       ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Alan Modra @ 2010-12-06 23:29 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Dave Korn, Cary Coutant, Ian Lance Taylor, GCC Development,
	Binutils, Tristan Gingold

On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
> Personally, I think 2 stage linking is one way to fix this issue.

Ian has stated that he thinks this is a really bad idea.  I haven't
approved the patch because I value Ian's opinion, and can see why he
thinks it is the wrong way to go.  On the other hand, BFD is full of
bad ideas..  I'm not strongly opposed to your patch myself.

HJ, you showed that link times for gcc did not regress too much with
your 2 stage lto link patch.  It would be more interesting to see the
results for a large C++ project, mozilla for example.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:29                   ` Alan Modra
@ 2010-12-06 23:32                     ` H.J. Lu
  2010-12-06 23:55                       ` Ian Lance Taylor
  2010-12-06 23:54                     ` Ian Lance Taylor
  2010-12-13 21:58                     ` Ian Lance Taylor
  2 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-06 23:32 UTC (permalink / raw)
  To: H.J. Lu, Dave Korn, Cary Coutant, Ian Lance Taylor,
	GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 3:29 PM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>> Personally, I think 2 stage linking is one way to fix this issue.
>
> Ian has stated that he thinks this is a really bad idea.  I haven't
> approved the patch because I value Ian's opinion, and can see why he
> thinks it is the wrong way to go.  On the other hand, BFD is full of
> bad ideas..  I'm not strongly opposed to your patch myself.
>
> HJ, you showed that link times for gcc did not regress too much with
> your 2 stage lto link patch.  It would be more interesting to see the
> results for a large C++ project, mozilla for example.
>

I don't have such programs at hand. Will timings from gccgo, which is
written in C++, help?


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:29                   ` Alan Modra
  2010-12-06 23:32                     ` H.J. Lu
@ 2010-12-06 23:54                     ` Ian Lance Taylor
  2010-12-07  0:00                       ` H.J. Lu
  2010-12-13 21:58                     ` Ian Lance Taylor
  2 siblings, 1 reply; 37+ messages in thread
From: Ian Lance Taylor @ 2010-12-06 23:54 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

Alan Modra <amodra@gmail.com> writes:

> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>> Personally, I think 2 stage linking is one way to fix this issue.
>
> Ian has stated that he thinks this is a really bad idea.  I haven't
> approved the patch because I value Ian's opinion, and can see why he
> thinks it is the wrong way to go.  On the other hand, BFD is full of
> bad ideas..  I'm not strongly opposed to your patch myself.

Why don't we spend a short amount of time fixing these relatively minor
issues in lto-plugin without going all the way to two-stage linking?
Both Cary and I made several suggestions for how to do this.

Ian

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:32                     ` H.J. Lu
@ 2010-12-06 23:55                       ` Ian Lance Taylor
  2010-12-07  0:01                         ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: Ian Lance Taylor @ 2010-12-06 23:55 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

"H.J. Lu" <hjl.tools@gmail.com> writes:

> I don't have such programs at hand. Will timings from gccgo, which is
> written in C++, help?

gccgo by itself is not really a large C++ program.  It's only about
50,000 lines of C++.

Building gcc with --enable-build-with-cxx would get you a large C++
program, but of course it would not be very typical of C++ programs.

Ian

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:54                     ` Ian Lance Taylor
@ 2010-12-07  0:00                       ` H.J. Lu
  2010-12-07  0:09                         ` Ian Lance Taylor
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-07  0:00 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 3:54 PM, Ian Lance Taylor <iant@google.com> wrote:
> Alan Modra <amodra@gmail.com> writes:
>
>> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>>> Personally, I think 2 stage linking is one way to fix this issue.
>>
>> Ian has stated that he thinks this is a really bad idea.  I haven't
>> approved the patch because I value Ian's opinion, and can see why he
>> thinks it is the wrong way to go.  On the other hand, BFD is full of
>> bad ideas..  I'm not strongly opposed to your patch myself.
>
> Why don't we spend a short amount of time fixing these relatively minor
> issues in lto-plugin without going all the way to two-stage linking?

The issues may be "relatively minor".  But proper fix without
two-stage linking may be quite tricky.

> Both Cary and I made several suggestions for how to do this.

Fixing all known/unknown issues may not be easy without
two-stage linking.  With two-stage linking, everything just works.


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:55                       ` Ian Lance Taylor
@ 2010-12-07  0:01                         ` H.J. Lu
  0 siblings, 0 replies; 37+ messages in thread
From: H.J. Lu @ 2010-12-07  0:01 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 3:55 PM, Ian Lance Taylor <iant@google.com> wrote:
> "H.J. Lu" <hjl.tools@gmail.com> writes:
>
>> I don't have such programs at hand. Will timings from gccgo, which is
>> written in C++, help?
>
> gccgo by itself is not really a large C++ program.  It's only about
> 50,000 lines of C++.
>
> Building gcc with --enable-build-with-cxx would get you a large C++
> program, but of course it would not be very typical of C++ programs.
>

I can give it a try if it will help make a decision.


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  0:00                       ` H.J. Lu
@ 2010-12-07  0:09                         ` Ian Lance Taylor
  2010-12-07  1:02                           ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: Ian Lance Taylor @ 2010-12-07  0:09 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

"H.J. Lu" <hjl.tools@gmail.com> writes:

> On Mon, Dec 6, 2010 at 3:54 PM, Ian Lance Taylor <iant@google.com> wrote:
>> Alan Modra <amodra@gmail.com> writes:
>>
>>> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>>>> Personally, I think 2 stage linking is one way to fix this issue.
>>>
>>> Ian has stated that he thinks this is a really bad idea.  I haven't
>>> approved the patch because I value Ian's opinion, and can see why he
>>> thinks it is the wrong way to go.  On the other hand, BFD is full of
>>> bad ideas..  I'm not strongly opposed to your patch myself.
>>
>> Why don't we spend a short amount of time fixing these relatively minor
>> issues in lto-plugin without going all the way to two-stage linking?
>
> The issues may be "relatively minor".  But proper fix without
> two-stage linking may be quite tricky.

I see no particular reason why that should be the case.  The issues are
conceptually simple.

Ian

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  0:09                         ` Ian Lance Taylor
@ 2010-12-07  1:02                           ` H.J. Lu
  2010-12-07  1:10                             ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-07  1:02 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 4:08 PM, Ian Lance Taylor <iant@google.com> wrote:
> "H.J. Lu" <hjl.tools@gmail.com> writes:
>
>> On Mon, Dec 6, 2010 at 3:54 PM, Ian Lance Taylor <iant@google.com> wrote:
>>> Alan Modra <amodra@gmail.com> writes:
>>>
>>>> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>>>>> Personally, I think 2 stage linking is one way to fix this issue.
>>>>
>>>> Ian has stated that he thinks this is a really bad idea.  I haven't
>>>> approved the patch because I value Ian's opinion, and can see why he
>>>> thinks it is the wrong way to go.  On the other hand, BFD is full of
>>>> bad ideas..  I'm not strongly opposed to your patch myself.
>>>
>>> Why don't we spend a short amount of time fixing these relatively minor
>>> issues in lto-plugin without going all the way to two-stage linking?
>>
>> The issues may be "relatively minor".  But proper fix without
>> two-stage linking may be quite tricky.
>
> I see no particular reason why that should be the case.  The issues are
> conceptually simple.

I'd like to a gold implementation which works on all known cases.

-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  1:02                           ` H.J. Lu
@ 2010-12-07  1:10                             ` H.J. Lu
  2010-12-07  1:19                               ` Cary Coutant
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-07  1:10 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 5:02 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Dec 6, 2010 at 4:08 PM, Ian Lance Taylor <iant@google.com> wrote:
>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>
>>> On Mon, Dec 6, 2010 at 3:54 PM, Ian Lance Taylor <iant@google.com> wrote:
>>>> Alan Modra <amodra@gmail.com> writes:
>>>>
>>>>> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>>>>>> Personally, I think 2 stage linking is one way to fix this issue.
>>>>>
>>>>> Ian has stated that he thinks this is a really bad idea.  I haven't
>>>>> approved the patch because I value Ian's opinion, and can see why he
>>>>> thinks it is the wrong way to go.  On the other hand, BFD is full of
>>>>> bad ideas..  I'm not strongly opposed to your patch myself.
>>>>
>>>> Why don't we spend a short amount of time fixing these relatively minor
>>>> issues in lto-plugin without going all the way to two-stage linking?
>>>
>>> The issues may be "relatively minor".  But proper fix without
>>> two-stage linking may be quite tricky.
>>
>> I see no particular reason why that should be the case.  The issues are
>> conceptually simple.
>
> I'd like to a gold implementation which works on all known cases.
>

BTW, gold LTO plugin miscompiled 416.gamess in SPEC CPU 2006:

http://www.sourceware.org/bugzilla/show_bug.cgi?id=12244

BFD linker plugin is OK.

-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  1:10                             ` H.J. Lu
@ 2010-12-07  1:19                               ` Cary Coutant
  2010-12-07  1:32                                 ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: Cary Coutant @ 2010-12-07  1:19 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Ian Lance Taylor, Dave Korn, GCC Development, Binutils, Tristan Gingold

>>> I see no particular reason why that should be the case.  The issues are
>>> conceptually simple.
>>
>> I'd like to a gold implementation which works on all known cases.

You'd like to what?

> BTW, gold LTO plugin miscompiled 416.gamess in SPEC CPU 2006:
>
> http://www.sourceware.org/bugzilla/show_bug.cgi?id=12244
>
> BFD linker plugin is OK.

I can't tell for sure from either bug report, but is this the -lm
problem discussed earlier in this thread? The gcc driver needs to add
-lm as a pass-through library when -lm is on the command line.

-cary

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  1:19                               ` Cary Coutant
@ 2010-12-07  1:32                                 ` H.J. Lu
  0 siblings, 0 replies; 37+ messages in thread
From: H.J. Lu @ 2010-12-07  1:32 UTC (permalink / raw)
  To: Cary Coutant
  Cc: Ian Lance Taylor, Dave Korn, GCC Development, Binutils, Tristan Gingold

On Mon, Dec 6, 2010 at 5:19 PM, Cary Coutant <ccoutant@google.com> wrote:
>>>> I see no particular reason why that should be the case.  The issues are
>>>> conceptually simple.
>>>
>>> I'd like to a gold implementation which works on all known cases.
>
> You'd like to what?

I'd like to see gold implementation which works on all known cases.

>> BTW, gold LTO plugin miscompiled 416.gamess in SPEC CPU 2006:
>>
>> http://www.sourceware.org/bugzilla/show_bug.cgi?id=12244
>>
>> BFD linker plugin is OK.
>
> I can't tell for sure from either bug report, but is this the -lm
> problem discussed earlier in this thread? The gcc driver needs to add
> -lm as a pass-through library when -lm is on the command line.
>

I am using libm.so in this case.  I am not sure if add -lm as a
pass-through library
will help.

-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 16:59           ` Dave Korn
  2010-12-06 17:20             ` H.J. Lu
  2010-12-06 17:44             ` H.J. Lu
@ 2010-12-07  8:33             ` Tristan Gingold
  2010-12-07 10:35               ` Dave Korn
  2 siblings, 1 reply; 37+ messages in thread
From: Tristan Gingold @ 2010-12-07  8:33 UTC (permalink / raw)
  To: Dave Korn
  Cc: H.J. Lu, Cary Coutant, Ian Lance Taylor, GCC Development, Binutils


On Dec 6, 2010, at 6:23 PM, Dave Korn wrote:
>  Tristan, sorry, you must be sick of hearing from me by now,

No, not really :-)

> but I notice the
> branch was still labile a couple of hours ago... it would be really good if we
> could get HJ's patch approved and backported before you spin the release.

The issue is that this patch isn't yet approved for the trunk and looks slightly controversial.
And I'd like to make the release soon (ie this week).

Unless this patch is quickly approved (and back-ported), I plan to do the release this week.  If it is accepted after,
this is not a real issue as it can be part of binutils 2.21.1 (which should be released before March - or within gcc 4.6)

Tristan.



^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07  8:33             ` Tristan Gingold
@ 2010-12-07 10:35               ` Dave Korn
  0 siblings, 0 replies; 37+ messages in thread
From: Dave Korn @ 2010-12-07 10:35 UTC (permalink / raw)
  To: Tristan Gingold
  Cc: H.J. Lu, Cary Coutant, Ian Lance Taylor, GCC Development, Binutils

On 07/12/2010 08:33, Tristan Gingold wrote:
> On Dec 6, 2010, at 6:23 PM, Dave Korn wrote:
>> Tristan, sorry, you must be sick of hearing from me by now,
> 
> No, not really :-)
> 
>> but I notice the branch was still labile a couple of hours ago... it
>> would be really good if we could get HJ's patch approved and backported
>> before you spin the release.
> 
> The issue is that this patch isn't yet approved for the trunk and looks
> slightly controversial. And I'd like to make the release soon (ie this
> week).
> 
> Unless this patch is quickly approved (and back-ported), I plan to do the
> release this week.  If it is accepted after, this is not a real issue as it
> can be part of binutils 2.21.1 (which should be released before March - or
> within gcc 4.6)

  Yeah, we clearly aren't going to arrive at any sudden consensus, so don't
delay it.  It can wait for .1, it's not needed until GCC releases.

    cheers,
      DaveK

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 17:20             ` H.J. Lu
@ 2010-12-07 20:12               ` H.J. Lu
  2010-12-08  5:55                 ` H.J. Lu
  0 siblings, 1 reply; 37+ messages in thread
From: H.J. Lu @ 2010-12-07 20:12 UTC (permalink / raw)
  To: Dave Korn
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

[-- Attachment #1: Type: text/plain, Size: 4565 bytes --]

On Mon, Dec 6, 2010 at 9:20 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>> On 06/12/2010 02:20, H.J. Lu wrote:
>>
>>>>>>> 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?
>>
>>  Well, I reckon this patch is great (but don't have the approval rights).
>> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
>> underway without anything abnormal showing up.
>>
>>> +       /* 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"));
>>
>>  If I had known that there was really this little stored state to be unwound
>> and regenerated, I would have wanted to do it this way in the first place.
>>
>>> +typedef struct cmdlin_header_struct
>>
>>  Typo there.
>
> Fixed.
>

cmdline_set_next_claimed_output took the address of an stack
variable:

http://www.sourceware.org/bugzilla/show_bug.cgi?id=12293

Fixed in this updated patch.

-- 
H.J.
---
bfd/

2010-12-07  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-07  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 (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-07  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.

[-- Attachment #2: binutils-2stage-6.patch --]
[-- Type: text/plain, Size: 30506 bytes --]

bfd/

2010-12-07  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-07  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 (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-07  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..9aa9228 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 (cmdline_union_type *);
 
 /* 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 (cmdline_list.head);
+
+	  /* 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,164 @@ 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, *next;
+      cmdline_next_claimed_output = &cmdline_list.head;
+      for (c = cmdline_list.head; c != NULL; c = next)
+	{
+	  next = c->header.next;
+	  /* We must put object files generated by plugin before
+	     the archive containing the IR object.  */
+	  if ((next
+	       && archive
+	       && (next->header.type
+		   == cmdline_is_lang_input_statement_enum)
+	       && archive == next->input_statement.input->the_bfd)
+	      || (c->header.type == cmdline_is_lang_input_statement_enum
+		  && !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 (cmdline_union_type *c)
+{
+  info_msg (_("Stage 2 command line:\n "));
+
+  for (; 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..21f39c5 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 cmdline_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 5530dca..8b773fe 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -791,6 +791,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));
@@ -802,20 +803,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;
@@ -829,6 +841,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;
@@ -841,6 +856,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 */
@@ -891,16 +909,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 acb63fb..94fa2cd 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -725,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
@@ -763,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:
@@ -920,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 = "-";
@@ -1105,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;
 	      }
 	  }
@@ -1489,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 ')':
@@ -1496,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 e4943c2..cfe37d2 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -214,6 +214,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;
@@ -544,9 +555,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;
 }
 
@@ -555,9 +564,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
 }
 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-07 20:12               ` H.J. Lu
@ 2010-12-08  5:55                 ` H.J. Lu
  0 siblings, 0 replies; 37+ messages in thread
From: H.J. Lu @ 2010-12-08  5:55 UTC (permalink / raw)
  To: Dave Korn
  Cc: Cary Coutant, Ian Lance Taylor, GCC Development, Binutils,
	Tristan Gingold

On Tue, Dec 7, 2010 at 12:12 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Dec 6, 2010 at 9:20 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Mon, Dec 6, 2010 at 9:23 AM, Dave Korn <dave.korn.cygwin@gmail.com> wrote:
>>> On 06/12/2010 02:20, H.J. Lu wrote:
>>>
>>>>>>>> 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?
>>>
>>>  Well, I reckon this patch is great (but don't have the approval rights).
>>> It's passed an lto-bootstrap of gcc on i686-pc-cygwin and the tests are well
>>> underway without anything abnormal showing up.
>>>
>>>> +       /* 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"));
>>>
>>>  If I had known that there was really this little stored state to be unwound
>>> and regenerated, I would have wanted to do it this way in the first place.
>>>
>>>> +typedef struct cmdlin_header_struct
>>>
>>>  Typo there.
>>
>> Fixed.
>>
>
> cmdline_set_next_claimed_output took the address of an stack
> variable:
>
> http://www.sourceware.org/bugzilla/show_bug.cgi?id=12293
>
> Fixed in this updated patch.
>

I fixed:

http://sourceware.org/bugzilla/show_bug.cgi?id=12295

on hjl/lto branch with commit

c7374491324a96819584e0692aaf9c8aab6f2241

http://git.kernel.org/?p=devel/binutils/hjl/x86.git;a=summary


H.J.
-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-06 23:29                   ` Alan Modra
  2010-12-06 23:32                     ` H.J. Lu
  2010-12-06 23:54                     ` Ian Lance Taylor
@ 2010-12-13 21:58                     ` Ian Lance Taylor
  2010-12-13 22:18                       ` Cary Coutant
  2011-01-19  0:28                       ` Ian Lance Taylor
  2 siblings, 2 replies; 37+ messages in thread
From: Ian Lance Taylor @ 2010-12-13 21:58 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Dave Korn, Cary Coutant, GCC Development, Binutils, Tristan Gingold

[-- Attachment #1: Type: text/plain, Size: 2864 bytes --]

Alan Modra <amodra@gmail.com> writes:

> On Mon, Dec 06, 2010 at 09:57:14AM -0800, H.J. Lu wrote:
>> Personally, I think 2 stage linking is one way to fix this issue.
>
> Ian has stated that he thinks this is a really bad idea.  I haven't
> approved the patch because I value Ian's opinion, and can see why he
> thinks it is the wrong way to go.  On the other hand, BFD is full of
> bad ideas..  I'm not strongly opposed to your patch myself.

Here is an alternative proposal, with a patch for gold.

We add a new plugin vector: LDPT_REGISTER_RESCAN_ARCHIVE_HOOK.  Like
LDPT_REGISTER_CLAIM_FILE_HOOK, this gives the plugin the address of a
function which can register a plugin function: rescan_archive.

typedef
enum ld_plugin_status
(*ld_plugin_rescan_archive_handler) (
  const struct ld_plugin_input_file *file, int *rescan);

If the plugin registers this hook, the linker will call the hook for
each archive that it sees.  If the hook sets the *rescan variable to a
non-zero value, then the linker will rescan the archive after calling
the all_symbols_read hook.  The archive will be rescanned using the same
position dependent options as when it was originally scanned.  In
particular, if the archive occurred within --start-group/--end-group,
the entire group will be rescanned.

The point of this patch is that the known problems with the LTO plugin
are when the plugin introduces a previously unknown symbol which must be
satisfied from some archive.  The new symbol is introduced when the
all_symbols_hook read hook calls add_input_file to add a new object file
to the link, and the new object file refers to the new symbol.  Since
the symbol was not previously seen, if the definition should come from
an archive, the archive will not have been searched.  Hence the test
case in GCC PR 42690 comment #32.

Fortunately, we know that gcc is not going to introduce arbitrary new
symbol references.  The current system hacks around the problem by using
a special -pass-through option which directs the plugin to add specific
archives to the link, namely -lc and -lgcc.  That works for most cases,
but not when gcc introduces a symbol which comes from -lm.  Still,
despite the -lm issue, we do not have to concern ourselves with
arbitrary archives, because gcc is not going to act arbitrarily.

Also we know that the problem can only occur with archives, not with
shared objects.

The rescan_archive proposal fixes the problems and obviates the need to
use -pass-through.  It avoids the unnecessary cost of a complete relink.

I've appended a patch for gold which implements this proposal.  I've
also appended a patch for lto-plugin.  This patched lto-plugin does not
use -pass-through when rescan_archive is available.  It rescans libc.a,
libgcc.a, and libm.a.  It handles the PR 42690 comment #32 test case
correctly.

Any thoughts on this approach?

Ian



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gold patch --]
[-- Type: text/x-diff, Size: 12827 bytes --]

Index: include/plugin-api.h
===================================================================
RCS file: /cvs/src/src/include/plugin-api.h,v
retrieving revision 1.12
diff -p -u -r1.12 plugin-api.h
--- include/plugin-api.h	14 Oct 2010 01:31:28 -0000	1.12
+++ include/plugin-api.h	13 Dec 2010 20:53:16 -0000
@@ -163,6 +163,13 @@ typedef
 enum ld_plugin_status
 (*ld_plugin_all_symbols_read_handler) (void);
 
+/* The plugin library's "rescan archive" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_rescan_archive_handler) (
+  const struct ld_plugin_input_file *file, int *rescan);
+
 /* The plugin library's cleanup handler.  */
 
 typedef
@@ -182,6 +189,13 @@ enum ld_plugin_status
 (*ld_plugin_register_all_symbols_read) (
   ld_plugin_all_symbols_read_handler handler);
 
+/* The linker's interface for registering the "rescan archive" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_register_rescan_archive) (
+  ld_plugin_rescan_archive_handler handler);
+
 /* The linker's interface for registering the cleanup handler.  */
 
 typedef
@@ -269,7 +283,8 @@ enum ld_plugin_tag
   LDPT_ADD_INPUT_LIBRARY,
   LDPT_OUTPUT_NAME,
   LDPT_SET_EXTRA_LIBRARY_PATH,
-  LDPT_GNU_LD_VERSION
+  LDPT_GNU_LD_VERSION,
+  LDPT_REGISTER_RESCAN_ARCHIVE_HOOK
 };
 
 /* The plugin transfer vector.  */
@@ -283,6 +298,7 @@ struct ld_plugin_tv
     const char *tv_string;
     ld_plugin_register_claim_file tv_register_claim_file;
     ld_plugin_register_all_symbols_read tv_register_all_symbols_read;
+    ld_plugin_register_rescan_archive tv_register_rescan_archive;
     ld_plugin_register_cleanup tv_register_cleanup;
     ld_plugin_add_symbols tv_add_symbols;
     ld_plugin_get_symbols tv_get_symbols;
Index: plugin.cc
===================================================================
RCS file: /cvs/src/src/gold/plugin.cc,v
retrieving revision 1.41
diff -p -u -r1.41 plugin.cc
--- plugin.cc	5 Nov 2010 21:14:12 -0000	1.41
+++ plugin.cc	13 Dec 2010 20:52:15 -0000
@@ -60,6 +60,9 @@ static enum ld_plugin_status
 register_all_symbols_read(ld_plugin_all_symbols_read_handler handler);
 
 static enum ld_plugin_status
+register_rescan_archive(ld_plugin_rescan_archive_handler handler);
+
+static enum ld_plugin_status
 register_cleanup(ld_plugin_cleanup_handler handler);
 
 static enum ld_plugin_status
@@ -130,7 +133,7 @@ Plugin::load()
   sscanf(ver, "%d.%d", &major, &minor);
 
   // Allocate and populate a transfer vector.
-  const int tv_fixed_size = 16;
+  const int tv_fixed_size = 17;
   int tv_size = this->args_.size() + tv_fixed_size;
   ld_plugin_tv* tv = new ld_plugin_tv[tv_size];
 
@@ -209,6 +212,10 @@ Plugin::load()
   tv[i].tv_u.tv_set_extra_library_path = set_extra_library_path;
 
   ++i;
+  tv[i].tv_tag = LDPT_REGISTER_RESCAN_ARCHIVE_HOOK;
+  tv[i].tv_u.tv_register_rescan_archive = register_rescan_archive;
+
+  ++i;
   tv[i].tv_tag = LDPT_NULL;
   tv[i].tv_u.tv_val = 0;
 
@@ -246,6 +253,21 @@ Plugin::all_symbols_read()
     (*this->all_symbols_read_handler_)();
 }
 
+// Call the rescan-archive handler.
+
+inline bool
+Plugin::rescan_archive(struct ld_plugin_input_file* plugin_input_file)
+{
+  if (this->rescan_archive_handler_ != NULL)
+    {
+      int rescan = 0;
+      (*this->rescan_archive_handler_)(plugin_input_file, &rescan);
+      if (rescan)
+	return true;
+    }
+  return false;
+}
+
 // Call the cleanup handler.
 
 inline void
@@ -324,6 +346,34 @@ Plugin_manager::claim_file(Input_file* i
   return NULL;
 }
 
+// Call the plugin rescan-archive handlers in turn to see if any want
+// to rescan the archive.
+
+bool
+Plugin_manager::rescan_archive(Input_file* input_file, off_t offset,
+			       off_t filesize)
+{
+  if (this->in_replacement_phase_)
+    return false;
+
+  this->input_file_ = input_file;
+  this->plugin_input_file_.name = input_file->filename().c_str();
+  this->plugin_input_file_.fd = input_file->file().descriptor();
+  this->plugin_input_file_.offset = offset;
+  this->plugin_input_file_.filesize = filesize;
+  this->plugin_input_file_.handle = NULL;
+
+  for (this->current_ = this->plugins_.begin();
+       this->current_ != this->plugins_.end();
+       ++this->current_)
+    {
+      if ((*this->current_)->rescan_archive(&this->plugin_input_file_))
+	return true;
+    }
+
+  return false;
+}
+
 // Call the all-symbols-read handlers.
 
 void
@@ -348,6 +398,27 @@ Plugin_manager::all_symbols_read(Workque
        ++this->current_)
     (*this->current_)->all_symbols_read();
 
+  for (std::vector<const Input_argument*>::const_iterator p =
+	 this->rescan_.begin();
+       p != this->rescan_.end();
+       ++p)
+    {
+      Task_token* next_blocker = new Task_token(true);
+      next_blocker->add_blocker();
+      workqueue->queue_soon(new Read_symbols(input_objects,
+					     symtab,
+					     layout,
+					     dirpath,
+					     0,
+					     mapfile,
+					     *p,
+					     NULL,
+					     NULL,
+					     this->this_blocker_,
+					     next_blocker));
+      this->this_blocker_ = next_blocker;
+    }
+
   *last_blocker = this->this_blocker_;
 }
 
@@ -994,6 +1065,16 @@ register_all_symbols_read(ld_plugin_all_
   return LDPS_OK;
 }
 
+// Register a rescan-archive handler.
+
+static enum ld_plugin_status
+register_rescan_archive(ld_plugin_rescan_archive_handler handler)
+{
+  gold_assert(parameters->options().has_plugins());
+  parameters->options().plugins()->set_rescan_archive_handler(handler);
+  return LDPS_OK;
+}
+
 // Register a cleanup handler.
 
 static enum ld_plugin_status
Index: plugin.h
===================================================================
RCS file: /cvs/src/src/gold/plugin.h,v
retrieving revision 1.15
diff -p -u -r1.15 plugin.h
--- plugin.h	25 Aug 2010 08:36:54 -0000	1.15
+++ plugin.h	13 Dec 2010 20:52:15 -0000
@@ -54,6 +54,7 @@ class Plugin
       args_(),
       claim_file_handler_(NULL),
       all_symbols_read_handler_(NULL),
+      rescan_archive_handler_(NULL),
       cleanup_handler_(NULL),
       cleanup_done_(false)
   { }
@@ -73,6 +74,11 @@ class Plugin
   void
   all_symbols_read();
 
+  // Call the rescan-archive handler.  Return true if the archive
+  // should be rescanned.
+  bool
+  rescan_archive(struct ld_plugin_input_file* plugin_input_file);
+
   // Call the cleanup handler.
   void
   cleanup();
@@ -87,6 +93,11 @@ class Plugin
   set_all_symbols_read_handler(ld_plugin_all_symbols_read_handler handler)
   { this->all_symbols_read_handler_ = handler; }
 
+  // Register a rescan archive handler.
+  void
+  set_rescan_archive_handler(ld_plugin_rescan_archive_handler handler)
+  { this->rescan_archive_handler_ = handler; }
+
   // Register a claim-file handler.
   void
   set_cleanup_handler(ld_plugin_cleanup_handler handler)
@@ -112,6 +123,7 @@ class Plugin
   // The plugin's event handlers.
   ld_plugin_claim_file_handler claim_file_handler_;
   ld_plugin_all_symbols_read_handler all_symbols_read_handler_;
+  ld_plugin_rescan_archive_handler rescan_archive_handler_;
   ld_plugin_cleanup_handler cleanup_handler_;
   // TRUE if the cleanup handlers have been called.
   bool cleanup_done_;
@@ -127,7 +139,7 @@ class Plugin_manager
       plugin_input_file_(), in_replacement_phase_(false),
       options_(options), workqueue_(NULL), task_(NULL), input_objects_(NULL),
       symtab_(NULL), layout_(NULL), dirpath_(NULL), mapfile_(NULL),
-      this_blocker_(NULL), extra_search_path_()
+      this_blocker_(NULL), extra_search_path_(), rescan_()
   { this->current_ = plugins_.end(); }
 
   ~Plugin_manager();
@@ -160,6 +172,11 @@ class Plugin_manager
                    Layout* layout, Dirsearch* dirpath, Mapfile* mapfile,
                    Task_token** last_blocker);
 
+  // Call the rescan-archive handlers.  Return true if the archive
+  // should be rescanned.
+  bool
+  rescan_archive(Input_file* input_file, off_t offset, off_t filesize);
+
   // Run deferred layout.
   void
   layout_deferred_objects();
@@ -184,6 +201,14 @@ class Plugin_manager
     (*this->current_)->set_all_symbols_read_handler(handler);
   }
 
+  // Register a rescan archive handler.
+  void
+  set_rescan_archive_handler(ld_plugin_rescan_archive_handler handler)
+  {
+    gold_assert(this->current_ != plugins_.end());
+    (*this->current_)->set_rescan_archive_handler(handler);
+  }
+
   // Register a claim-file handler.
   void
   set_cleanup_handler(ld_plugin_cleanup_handler handler)
@@ -232,6 +257,12 @@ class Plugin_manager
   ld_plugin_status
   add_input_file(const char* pathname, bool is_lib);
 
+  // Add an archive or group to be rescanned after the
+  // all-symbols-read handler has been called.
+  void
+  add_rescan(const Input_argument* input_argument)
+  { this->rescan_.push_back(input_argument); }
+
   // Set the extra library path.
   ld_plugin_status
   set_extra_library_path(const char* path);
@@ -283,6 +314,8 @@ class Plugin_manager
   // An extra directory to seach for the libraries passed by
   // add_input_library.
   std::string extra_search_path_;
+  // A list of archives to rescan.
+  std::vector<const Input_argument*> rescan_;
 };
 
 
Index: readsyms.cc
===================================================================
RCS file: /cvs/src/src/gold/readsyms.cc,v
retrieving revision 1.45
diff -p -u -r1.45 readsyms.cc
--- readsyms.cc	20 Aug 2010 00:35:12 -0000	1.45
+++ readsyms.cc	13 Dec 2010 20:52:16 -0000
@@ -316,6 +316,21 @@ Read_symbols::do_read_symbols(Workqueue*
 							this->input_group_,
 							this->this_blocker_,
 							this->next_blocker_));
+
+	  if (!this->input_argument_->file().options().whole_archive()
+	      && parameters->options().has_plugins()
+	      && parameters->options().plugins()->rescan_archive(input_file,
+								 0, filesize))
+	    {
+	      if (this->input_group_ != NULL)
+		this->input_group_->set_needs_rescan();
+	      else
+		{
+		  Plugin_manager* plugins = parameters->options().plugins();
+		  plugins->add_rescan(this->input_argument_);
+		}
+	    }
+
 	  return true;
 	}
     }
@@ -456,6 +471,7 @@ Read_symbols::do_group(Workqueue* workqu
 						this->symtab_,
 						this->layout_,
 						this->mapfile_,
+						this->input_argument_,
 						input_group,
 						this->next_blocker_);
 
@@ -680,6 +696,12 @@ Finish_group::run(Workqueue*)
 	}
     }
 
+  if (this->input_group_->needs_rescan())
+    {
+      gold_assert(parameters->options().has_plugins());
+      parameters->options().plugins()->add_rescan(this->input_argument_);
+    }
+
   // Now that we're done with the archives, record the incremental layout
   // information, then delete them.
   for (Input_group::const_iterator p = this->input_group_->begin();
Index: readsyms.h
===================================================================
RCS file: /cvs/src/src/gold/readsyms.h,v
retrieving revision 1.21
diff -p -u -r1.21 readsyms.h
--- readsyms.h	22 Mar 2010 14:18:24 -0000	1.21
+++ readsyms.h	13 Dec 2010 20:52:16 -0000
@@ -188,7 +188,7 @@ class Input_group
   typedef Archives::const_iterator const_iterator;
 
   Input_group()
-    : archives_()
+    : archives_(), needs_rescan_(false)
   { }
 
   // Add an archive to the group.
@@ -196,6 +196,17 @@ class Input_group
   add_archive(Archive* arch)
   { this->archives_.push_back(arch); }
 
+  // Whether some plugin has requested that this group be rescanned
+  // after all symbols have been read.
+  bool
+  needs_rescan() const
+  { return this->needs_rescan_; }
+
+  // Note that some plugin has requested that this group be rescanned.
+  void
+  set_needs_rescan()
+  { this->needs_rescan_ = true; }
+
   // Loop over the archives in the group.
 
   const_iterator
@@ -208,6 +219,8 @@ class Input_group
 
  private:
   Archives archives_;
+  // Whether a plugin has asked to rescan this group.
+  bool needs_rescan_;
 };
 
 // This class starts the handling of a group.  It exists only to pick
@@ -254,11 +267,13 @@ class Finish_group : public Task
 {
  public:
   Finish_group(Input_objects* input_objects, Symbol_table* symtab,
-	       Layout* layout, Mapfile* mapfile, Input_group* input_group,
+	       Layout* layout, Mapfile* mapfile,
+	       const Input_argument* input_argument, Input_group* input_group,
 	       Task_token* next_blocker)
     : input_objects_(input_objects), symtab_(symtab),
-      layout_(layout), mapfile_(mapfile), input_group_(input_group),
-      saw_undefined_(0), this_blocker_(NULL), next_blocker_(next_blocker)
+      layout_(layout), mapfile_(mapfile), input_argument_(input_argument),
+      input_group_(input_group), saw_undefined_(0), this_blocker_(NULL),
+      next_blocker_(next_blocker)
   { }
 
   ~Finish_group();
@@ -297,6 +312,7 @@ class Finish_group : public Task
   Symbol_table* symtab_;
   Layout* layout_;
   Mapfile* mapfile_;
+  const Input_argument* input_argument_;
   Input_group* input_group_;
   size_t saw_undefined_;
   Task_token* this_blocker_;

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: lto-plugin patch --]
[-- Type: text/x-diff, Size: 2199 bytes --]

Index: lto-plugin/lto-plugin.c
===================================================================
--- lto-plugin/lto-plugin.c	(revision 167736)
+++ lto-plugin/lto-plugin.c	(working copy)
@@ -129,6 +129,7 @@ enum symbol_style
 static char *arguments_file_name;
 static ld_plugin_register_claim_file register_claim_file;
 static ld_plugin_register_all_symbols_read register_all_symbols_read;
+static ld_plugin_register_rescan_archive register_rescan_archive;
 static ld_plugin_get_symbols get_symbols;
 static ld_plugin_register_cleanup register_cleanup;
 static ld_plugin_add_input_file add_input_file;
@@ -622,7 +623,7 @@ all_symbols_read_handler (void)
 
   free (lto_argv);
 
-  if (pass_through_items)
+  if (pass_through_items != NULL && register_rescan_archive == NULL)
     {
       unsigned int i;
       for (i = 0; i < num_pass_through_items; i++)
@@ -913,6 +914,24 @@ claim_file_handler (const struct ld_plug
   return LDPS_OK;
 }
 
+/* Callback used by gold to see if an archive should be rescanned
+   after all the symbols have been read.  We use this to direct gold
+   to rescan the archives which may contain symbols definition
+   introduced by the gcc optimizers.  */
+
+static enum ld_plugin_status
+rescan_archive_handler (const struct ld_plugin_input_file *file, int *rescan)
+{
+  const char *base;
+
+  base = lbasename (file->name);
+  if (strcmp (base, "libgcc.a") == 0
+      || strcmp (base, "libc.a") == 0
+      || strcmp (base, "libm.a") == 0)
+    *rescan = 1;
+  return LDPS_OK;
+}
+
 /* Parse the plugin options. */
 
 static void
@@ -998,6 +1017,9 @@ onload (struct ld_plugin_tv *tv)
 	case LDPT_OPTION:
 	  process_option (p->tv_u.tv_string);
 	  break;
+	case LDPT_REGISTER_RESCAN_ARCHIVE_HOOK:
+	  register_rescan_archive = p->tv_u.tv_register_rescan_archive;
+	  break;
 	default:
 	  break;
 	}
@@ -1025,5 +1047,12 @@ onload (struct ld_plugin_tv *tv)
 	     "could not register the all_symbols_read callback");
     }
 
+  if (register_rescan_archive)
+    {
+      status = register_rescan_archive (rescan_archive_handler);
+      check (status == LDPS_OK, LDPL_FATAL,
+	     "could not register the rescan_archive callback");
+    }
+
   return LDPS_OK;
 }

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-13 21:58                     ` Ian Lance Taylor
@ 2010-12-13 22:18                       ` Cary Coutant
  2011-01-19  0:28                       ` Ian Lance Taylor
  1 sibling, 0 replies; 37+ messages in thread
From: Cary Coutant @ 2010-12-13 22:18 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: H.J. Lu, Dave Korn, GCC Development, Binutils, Tristan Gingold

> Here is an alternative proposal, with a patch for gold.
>
> We add a new plugin vector: LDPT_REGISTER_RESCAN_ARCHIVE_HOOK.  Like
> LDPT_REGISTER_CLAIM_FILE_HOOK, this gives the plugin the address of a
> function which can register a plugin function: rescan_archive.
>
> typedef
> enum ld_plugin_status
> (*ld_plugin_rescan_archive_handler) (
>  const struct ld_plugin_input_file *file, int *rescan);
>
> If the plugin registers this hook, the linker will call the hook for
> each archive that it sees.  If the hook sets the *rescan variable to a
> non-zero value, then the linker will rescan the archive after calling
> the all_symbols_read hook.  The archive will be rescanned using the same
> position dependent options as when it was originally scanned.  In
> particular, if the archive occurred within --start-group/--end-group,
> the entire group will be rescanned.
>
> The point of this patch is that the known problems with the LTO plugin
> are when the plugin introduces a previously unknown symbol which must be
> satisfied from some archive.  The new symbol is introduced when the
> all_symbols_hook read hook calls add_input_file to add a new object file
> to the link, and the new object file refers to the new symbol.  Since
> the symbol was not previously seen, if the definition should come from
> an archive, the archive will not have been searched.  Hence the test
> case in GCC PR 42690 comment #32.
>
> Fortunately, we know that gcc is not going to introduce arbitrary new
> symbol references.  The current system hacks around the problem by using
> a special -pass-through option which directs the plugin to add specific
> archives to the link, namely -lc and -lgcc.  That works for most cases,
> but not when gcc introduces a symbol which comes from -lm.  Still,
> despite the -lm issue, we do not have to concern ourselves with
> arbitrary archives, because gcc is not going to act arbitrarily.
>
> Also we know that the problem can only occur with archives, not with
> shared objects.
>
> The rescan_archive proposal fixes the problems and obviates the need to
> use -pass-through.  It avoids the unnecessary cost of a complete relink.
>
> I've appended a patch for gold which implements this proposal.  I've
> also appended a patch for lto-plugin.  This patched lto-plugin does not
> use -pass-through when rescan_archive is available.  It rescans libc.a,
> libgcc.a, and libm.a.  It handles the PR 42690 comment #32 test case
> correctly.
>
> Any thoughts on this approach?

Looks good to me. I'd still prefer something based on a list of symbol
names that the backend might introduce calls to, but I'll concede that
this is far more practical.

I think Dave mentioned in the other thread that libgcov, libssp, and
libmudflap might also need to be rescanned:

>>> My suspicion is that the LTO plugin can only introduce a small bounded
>>> set of new symbol references, namely those which we assume can be
>>> satisified from -lc or -lgcc.  Is that true?
>>
>> Exactly.
>
> Potentially also gcov, ssp, mudflap?

Should we mark the pass-through option in lto-plugin as obsolescent?

-cary

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2010-12-13 21:58                     ` Ian Lance Taylor
  2010-12-13 22:18                       ` Cary Coutant
@ 2011-01-19  0:28                       ` Ian Lance Taylor
  2011-01-19  1:38                         ` H.J. Lu
                                           ` (3 more replies)
  1 sibling, 4 replies; 37+ messages in thread
From: Ian Lance Taylor @ 2011-01-19  0:28 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Dave Korn, Cary Coutant, GCC Development, Binutils

[-- Attachment #1: Type: text/plain, Size: 3019 bytes --]

After some discussion on IRC, here is another approach to resolving the
issue with static linking and LTO.

In this approach, the linker keeps track of all archives found after the
first file claimed by the plugin.  If the plugin adds any object files,
and the object files refer to any symbols which are not yet defined,
then the linker will scan all the saved archives, in order, for a
definition of the symbol.  If a definition is found, the linker will
pull in the appropriate object from the archive.  If that object, in
turn, has any undefined symbols, the linker will pull in the appropriate
object from that archive or any later ones, and so forth.  The linker
will honor --start-group/--end-group while rescanning.

This should ensure that any previously unseen undefined symbols
introduced by the compiler are handled correctly.  I think it is
appropriate to do this unconditionally when using plugins, as there is
no other reasonable way to handle undefined symbols in a file added by a
plugin.

I've appended a patch to gold which implements this approach.  The patch
is reasonably efficient and introduces no unnecessary file I/O.  With
this patch to gold, and no change to gcc, the problematic -static test
cases which I know about pass.  Also all the current lto.exp tests pass.
All those tests also pass if I edit the gcc LTO plugin to ignore the
-pass-through option, as that option is not necessary when the linker
implements this approach.

As this patch does not require any changes to gcc, and fixes some cases
which are clearly bugs, I plan to commit this patch to binutils mainline
and to binutils 2.21 branch after a few days if I don't hear any
comments.

Ian


2011-01-18  Ian Lance Taylor  <iant@google.com>

	* plugin.cc (class Plugin_rescan): Define new class.
	(Plugin_manager::claim_file): Set any_claimed_.
	(Plugin_manager::save_archive): New function.
	(Plugin_manager::save_input_group): New function.
	(Plugin_manager::all_symbols_read): Create Plugin_rescan task if
	necessary.
	(Plugin_manager::new_undefined_symbol): New function.
	(Plugin_manager::rescan): New function.
	(Plugin_manager::rescannable_defines): New function.
	(Plugin_manager::add_input_file): Set any_added_.
	* plugin.h (class Plugin_manager): define new fields rescannable_,
	undefined_symbols_, any_claimed_, and any_added_.  Declare
	Plugin_rescan as friend.  Declare new functions.
	(Plugin_manager::Rescannable): Define type.
	(Plugin_manager::Rescannable_list): Define type.
	(Plugin_manager::Undefined_symbol_list): Define type.
	(Plugin_manager::Plugin_manager): Initialize new fields.
	* archive.cc (Archive::defines_symbol): New function.
	(Add_archive_symbols::run): Pass archive to plugins if any.
	* archive.h (class Archive): Declare defines_symbol.
	* readsyms.cc (Input_group::~Input_group): New function.
	(Finish_group::run): Pass input_group to plugins if any.
	* readsyms.h (class Input_group): Declare destructor.
	* symtab.cc (add_from_object): Pass undefined symbol to plugins if
	any.



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gold --]
[-- Type: text/x-diff, Size: 15942 bytes --]

Index: archive.h
===================================================================
RCS file: /cvs/src/src/gold/archive.h,v
retrieving revision 1.32
diff -u -p -r1.32 archive.h
--- archive.h	14 Dec 2010 19:03:29 -0000	1.32
+++ archive.h	18 Jan 2011 23:19:23 -0000
@@ -152,6 +152,10 @@ class Archive
   bool
   add_symbols(Symbol_table*, Layout*, Input_objects*, Mapfile*);
 
+  // Return whether the archive defines the symbol.
+  bool
+  defines_symbol(Symbol*) const;
+
   // Dump statistical information to stderr.
   static void
   print_stats();
Index: archive.cc
===================================================================
RCS file: /cvs/src/src/gold/archive.cc,v
retrieving revision 1.62
diff -u -p -r1.62 archive.cc
--- archive.cc	14 Dec 2010 19:03:29 -0000	1.62
+++ archive.cc	18 Jan 2011 23:19:24 -0000
@@ -779,6 +779,42 @@ Archive::add_symbols(Symbol_table* symta
   return true;
 }
 
+// Return whether the archive includes a member which defines the
+// symbol SYM.
+
+bool
+Archive::defines_symbol(Symbol* sym) const
+{
+  const char* symname = sym->name();
+  size_t symname_len = strlen(symname);
+  size_t armap_size = this->armap_.size();
+  for (size_t i = 0; i < armap_size; ++i)
+    {
+      if (this->armap_checked_[i])
+	continue;
+      const char* archive_symname = (this->armap_names_.data()
+				     + this->armap_[i].name_offset);
+      if (strncmp(archive_symname, symname, symname_len) != 0)
+	continue;
+      char c = archive_symname[symname_len];
+      if (c == '\0' && sym->version() == NULL)
+	return true;
+      if (c == '@')
+	{
+	  const char* ver = archive_symname + symname_len + 1;
+	  if (*ver == '@')
+	    {
+	      if (sym->version() == NULL)
+		return true;
+	      ++ver;
+	    }
+	  if (sym->version() != NULL && strcmp(sym->version(), ver) == 0)
+	    return true;
+	}
+    }
+  return false;
+}
+
 // Include all the archive members in the link.  This is for --whole-archive.
 
 bool
@@ -1001,8 +1037,18 @@ Add_archive_symbols::run(Workqueue* work
       if (incremental_inputs != NULL)
 	incremental_inputs->report_archive_end(this->archive_);
 
-      // We no longer need to know about this archive.
-      delete this->archive_;
+      if (!parameters->options().has_plugins()
+	  || this->archive_->input_file()->options().whole_archive())
+	{
+	  // We no longer need to know about this archive.
+	  delete this->archive_;
+	}
+      else
+	{
+	  // The plugin interface may want to rescan this archive.
+	  parameters->options().plugins()->save_archive(this->archive_);
+	}
+
       this->archive_ = NULL;
     }
 }
Index: plugin.h
===================================================================
RCS file: /cvs/src/src/gold/plugin.h,v
retrieving revision 1.15
diff -u -p -r1.15 plugin.h
--- plugin.h	25 Aug 2010 08:36:54 -0000	1.15
+++ plugin.h	18 Jan 2011 23:19:24 -0000
@@ -36,12 +36,17 @@ namespace gold
 class General_options;
 class Input_file;
 class Input_objects;
+class Archive;
+class Input_group;
+class Symbol;
 class Symbol_table;
 class Layout;
 class Dirsearch;
 class Mapfile;
+class Task;
 class Task_token;
 class Pluginobj;
+class Plugin_rescan;
 
 // This class represents a single plugin library.
 
@@ -124,7 +129,8 @@ class Plugin_manager
  public:
   Plugin_manager(const General_options& options)
     : plugins_(), objects_(), deferred_layout_objects_(), input_file_(NULL),
-      plugin_input_file_(), in_replacement_phase_(false),
+      plugin_input_file_(), rescannable_(), undefined_symbols_(),
+      any_claimed_(false), in_replacement_phase_(false), any_added_(false),
       options_(options), workqueue_(NULL), task_(NULL), input_objects_(NULL),
       symtab_(NULL), layout_(NULL), dirpath_(NULL), mapfile_(NULL),
       this_blocker_(NULL), extra_search_path_()
@@ -153,6 +159,16 @@ class Plugin_manager
   Pluginobj*
   claim_file(Input_file* input_file, off_t offset, off_t filesize);
 
+  // Let the plugin manager save an archive for later rescanning.
+  // This takes ownership of the Archive pointer.
+  void
+  save_archive(Archive*);
+
+  // Let the plugin manager save an input group for later rescanning.
+  // This takes ownership of the Input_group pointer.
+  void
+  save_input_group(Input_group*);
+
   // Call the all-symbols-read handlers.
   void
   all_symbols_read(Workqueue* workqueue, Task* task,
@@ -160,6 +176,11 @@ class Plugin_manager
                    Layout* layout, Dirsearch* dirpath, Mapfile* mapfile,
                    Task_token** last_blocker);
 
+  // Tell the plugin manager that we've a new undefined symbol which
+  // may require rescanning.
+  void
+  new_undefined_symbol(Symbol*);
+
   // Run deferred layout.
   void
   layout_deferred_objects();
@@ -245,9 +266,42 @@ class Plugin_manager
   Plugin_manager(const Plugin_manager&);
   Plugin_manager& operator=(const Plugin_manager&);
 
+  // Plugin_rescan is a Task which calls the private rescan method.
+  friend class Plugin_rescan;
+
+  // An archive or input group which may have to be rescanned if a
+  // plugin adds a new file.
+  struct Rescannable
+  {
+    bool is_archive;
+    union
+    {
+      Archive* archive;
+      Input_group* input_group;
+    } u;
+
+    Rescannable(Archive* archive)
+      : is_archive(true)
+    { this->u.archive = archive; }
+
+    Rescannable(Input_group* input_group)
+      : is_archive(false)
+    { this->u.input_group = input_group; }
+  };
+
   typedef std::list<Plugin*> Plugin_list;
   typedef std::vector<Pluginobj*> Object_list;
   typedef std::vector<Relobj*> Deferred_layout_list;
+  typedef std::vector<Rescannable> Rescannable_list;
+  typedef std::vector<Symbol*> Undefined_symbol_list;
+
+  // Rescan archives for undefined symbols.
+  void
+  rescan(Task*);
+
+  // See whether the rescannable at index I defines SYM.
+  bool
+  rescannable_defines(size_t i, Symbol* sym);
 
   // The list of plugin libraries.
   Plugin_list plugins_;
@@ -265,11 +319,24 @@ class Plugin_manager
   Input_file* input_file_;
   struct ld_plugin_input_file plugin_input_file_;
 
-  // TRUE after the all symbols read event; indicates that we are
-  // processing replacement files whose symbols should replace the
+  // A list of archives and input groups being saved for possible
+  // later rescanning.
+  Rescannable_list rescannable_;
+
+  // A list of undefined symbols found in added files.
+  Undefined_symbol_list undefined_symbols_;
+
+  // Whether any input files have been claimed by a plugin.
+  bool any_claimed_;
+
+  // Set to true after the all symbols read event; indicates that we
+  // are processing replacement files whose symbols should replace the
   // placeholder symbols from the Pluginobj objects.
   bool in_replacement_phase_;
 
+  // Whether any input files or libraries were added by a plugin.
+  bool any_added_;
+
   const General_options& options_;
   Workqueue* workqueue_;
   Task* task_;
Index: plugin.cc
===================================================================
RCS file: /cvs/src/src/gold/plugin.cc,v
retrieving revision 1.41
diff -u -p -r1.41 plugin.cc
--- plugin.cc	5 Nov 2010 21:14:12 -0000	1.41
+++ plugin.cc	18 Jan 2011 23:19:24 -0000
@@ -261,6 +261,45 @@ Plugin::cleanup()
     }
 }
 
+// This task is used to rescan archives as needed.
+
+class Plugin_rescan : public Task
+{
+ public:
+  Plugin_rescan(Task_token* this_blocker, Task_token* next_blocker)
+    : this_blocker_(this_blocker), next_blocker_(next_blocker)
+  { }
+
+  ~Plugin_rescan()
+  {
+    delete this->this_blocker_;
+  }
+
+  Task_token*
+  is_runnable()
+  {
+    if (this->this_blocker_->is_blocked())
+      return this->this_blocker_;
+    return NULL;
+  }
+
+  void
+  locks(Task_locker* tl)
+  { tl->add(this, this->next_blocker_); }
+
+  void
+  run(Workqueue*)
+  { parameters->options().plugins()->rescan(this); }
+
+  std::string
+  get_name() const
+  { return "Plugin_rescan"; }
+
+ private:
+  Task_token* this_blocker_;
+  Task_token* next_blocker_;
+};
+
 // Plugin_manager methods.
 
 Plugin_manager::~Plugin_manager()
@@ -311,6 +350,8 @@ Plugin_manager::claim_file(Input_file* i
     {
       if ((*this->current_)->claim_file(&this->plugin_input_file_))
         {
+	  this->any_claimed_ = true;
+
           if (this->objects_.size() > handle)
             return this->objects_[handle];
 
@@ -324,6 +365,31 @@ Plugin_manager::claim_file(Input_file* i
   return NULL;
 }
 
+// Save an archive.  This is used so that a plugin can add a file
+// which refers to a symbol which was not previously referenced.  In
+// that case we want to pretend that the symbol was referenced before,
+// and pull in the archive object.
+
+void
+Plugin_manager::save_archive(Archive* archive)
+{
+  if (this->in_replacement_phase_ || !this->any_claimed_)
+    delete archive;
+  else
+    this->rescannable_.push_back(Rescannable(archive));
+}
+
+// Save an Input_group.  This is like save_archive.
+
+void
+Plugin_manager::save_input_group(Input_group* input_group)
+{
+  if (this->in_replacement_phase_ || !this->any_claimed_)
+    delete input_group;
+  else
+    this->rescannable_.push_back(Rescannable(input_group));
+}
+
 // Call the all-symbols-read handlers.
 
 void
@@ -348,9 +414,146 @@ Plugin_manager::all_symbols_read(Workque
        ++this->current_)
     (*this->current_)->all_symbols_read();
 
+  if (this->any_added_)
+    {
+      Task_token* next_blocker = new Task_token(true);
+      next_blocker->add_blocker();
+      workqueue->queue(new Plugin_rescan(this->this_blocker_, next_blocker));
+      this->this_blocker_ = next_blocker;
+    }
+
   *last_blocker = this->this_blocker_;
 }
 
+// This is called when we see a new undefined symbol.  If we are in
+// the replacement phase, this means that we may need to rescan some
+// archives we have previously seen.
+
+void
+Plugin_manager::new_undefined_symbol(Symbol* sym)
+{
+  if (this->in_replacement_phase_)
+    this->undefined_symbols_.push_back(sym);
+}
+
+// Rescan archives as needed.  This handles the case where a new
+// object file added by a plugin has an undefined reference to some
+// symbol defined in an archive.
+
+void
+Plugin_manager::rescan(Task* task)
+{
+  size_t rescan_pos = 0;
+  size_t rescan_size = this->rescannable_.size();
+  while (!this->undefined_symbols_.empty())
+    {
+      if (rescan_pos >= rescan_size)
+	{
+	  this->undefined_symbols_.clear();
+	  return;
+	}
+
+      Undefined_symbol_list undefs;
+      undefs.reserve(this->undefined_symbols_.size());
+      this->undefined_symbols_.swap(undefs);
+
+      size_t min_rescan_pos = rescan_size;
+
+      for (Undefined_symbol_list::const_iterator p = undefs.begin();
+	   p != undefs.end();
+	   ++p)
+	{
+	  if (!(*p)->is_undefined())
+	    continue;
+
+	  this->undefined_symbols_.push_back(*p);
+
+	  // Find the first rescan archive which defines this symbol,
+	  // starting at the current rescan position.  The rescan position
+	  // exists so that given -la -lb -lc we don't look for undefined
+	  // symbols in -lb back in -la, but instead get the definition
+	  // from -lc.  Don't bother to look past the current minimum
+	  // rescan position.
+	  for (size_t i = rescan_pos; i < min_rescan_pos; ++i)
+	    {
+	      if (this->rescannable_defines(i, *p))
+		{
+		  min_rescan_pos = i;
+		  break;
+		}
+	    }
+	}
+
+      if (min_rescan_pos >= rescan_size)
+	{
+	  // We didn't find any rescannable archives which define any
+	  // undefined symbols.
+	  return;
+	}
+
+      const Rescannable& r(this->rescannable_[min_rescan_pos]);
+      if (r.is_archive)
+	{
+	  Task_lock_obj<Archive> tl(task, r.u.archive);
+	  r.u.archive->add_symbols(this->symtab_, this->layout_,
+				   this->input_objects_, this->mapfile_);
+	}
+      else
+	{
+	  size_t next_saw_undefined = this->symtab_->saw_undefined();
+	  size_t saw_undefined;
+	  do
+	    {
+	      saw_undefined = next_saw_undefined;
+
+	      for (Input_group::const_iterator p = r.u.input_group->begin();
+		   p != r.u.input_group->end();
+		   ++p)
+		{
+		  Task_lock_obj<Archive> tl(task, *p);
+
+		  (*p)->add_symbols(this->symtab_, this->layout_,
+				    this->input_objects_, this->mapfile_);
+		}
+
+	      next_saw_undefined = this->symtab_->saw_undefined();
+	    }
+	  while (saw_undefined != next_saw_undefined);
+	}
+
+      for (size_t i = rescan_pos; i < min_rescan_pos + 1; ++i)
+	{
+	  if (this->rescannable_[i].is_archive)
+	    delete this->rescannable_[i].u.archive;
+	  else
+	    delete this->rescannable_[i].u.input_group;
+	}
+
+      rescan_pos = min_rescan_pos + 1;
+    }
+}
+
+// Return whether the rescannable at index I defines SYM.
+
+bool
+Plugin_manager::rescannable_defines(size_t i, Symbol* sym)
+{
+  const Rescannable& r(this->rescannable_[i]);
+  if (r.is_archive)
+    return r.u.archive->defines_symbol(sym);
+  else
+    {
+      for (Input_group::const_iterator p = r.u.input_group->begin();
+	   p != r.u.input_group->end();
+	   ++p)
+	{
+	  if ((*p)->defines_symbol(sym))
+	    return true;
+	}
+      return false;
+    }
+}
+
 // Layout deferred objects.
 
 void
@@ -473,6 +676,7 @@ Plugin_manager::add_input_file(const cha
                                                 this->this_blocker_,
                                                 next_blocker));
   this->this_blocker_ = next_blocker;
+  this->any_added_ = true;
   return LDPS_OK;
 }
 
Index: readsyms.h
===================================================================
RCS file: /cvs/src/src/gold/readsyms.h,v
retrieving revision 1.21
diff -u -p -r1.21 readsyms.h
--- readsyms.h	22 Mar 2010 14:18:24 -0000	1.21
+++ readsyms.h	18 Jan 2011 23:19:24 -0000
@@ -191,6 +191,8 @@ class Input_group
     : archives_()
   { }
 
+  ~Input_group();
+
   // Add an archive to the group.
   void
   add_archive(Archive* arch)
Index: readsyms.cc
===================================================================
RCS file: /cvs/src/src/gold/readsyms.cc,v
retrieving revision 1.46
diff -u -p -r1.46 readsyms.cc
--- readsyms.cc	14 Dec 2010 19:03:30 -0000	1.46
+++ readsyms.cc	18 Jan 2011 23:19:24 -0000
@@ -599,6 +599,19 @@ Add_symbols::run(Workqueue*)
     }
 }
 
+// Class Input_group.
+
+// When we delete an Input_group we can delete the archive
+// information.
+
+Input_group::~Input_group()
+{
+  for (Input_group::const_iterator p = this->begin();
+       p != this->end();
+       ++p)
+    delete *p;
+}
+
 // Class Start_group.
 
 Start_group::~Start_group()
@@ -680,8 +693,8 @@ Finish_group::run(Workqueue*)
 	}
     }
 
-  // Now that we're done with the archives, record the incremental layout
-  // information, then delete them.
+  // Now that we're done with the archives, record the incremental
+  // layout information.
   for (Input_group::const_iterator p = this->input_group_->begin();
        p != this->input_group_->end();
        ++p)
@@ -691,10 +704,12 @@ Finish_group::run(Workqueue*)
           this->layout_->incremental_inputs();
       if (incremental_inputs != NULL)
 	incremental_inputs->report_archive_end(*p);
-
-      delete *p;
     }
-  delete this->input_group_;
+
+  if (parameters->options().has_plugins())
+    parameters->options().plugins()->save_input_group(this->input_group_);
+  else
+    delete this->input_group_;
 }
 
 // Class Read_script
Index: symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.145
diff -u -p -r1.145 symtab.cc
--- symtab.cc	2 Oct 2010 09:35:19 -0000	1.145
+++ symtab.cc	18 Jan 2011 23:19:24 -0000
@@ -1002,7 +1002,11 @@ Symbol_table::add_from_object(Object* ob
   // Record every time we see a new undefined symbol, to speed up
   // archive groups.
   if (!was_undefined && ret->is_undefined())
-    ++this->saw_undefined_;
+    {
+      ++this->saw_undefined_;
+      if (parameters->options().has_plugins())
+	parameters->options().plugins()->new_undefined_symbol(ret);
+    }
 
   // Keep track of common symbols, to speed up common symbol
   // allocation.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19  0:28                       ` Ian Lance Taylor
@ 2011-01-19  1:38                         ` H.J. Lu
  2011-01-19 10:25                         ` Richard Guenther
                                           ` (2 subsequent siblings)
  3 siblings, 0 replies; 37+ messages in thread
From: H.J. Lu @ 2011-01-19  1:38 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: Dave Korn, Cary Coutant, GCC Development, Binutils

On Tue, Jan 18, 2011 at 4:27 PM, Ian Lance Taylor <iant@google.com> wrote:
> After some discussion on IRC, here is another approach to resolving the
> issue with static linking and LTO.
>
> In this approach, the linker keeps track of all archives found after the
> first file claimed by the plugin.  If the plugin adds any object files,
> and the object files refer to any symbols which are not yet defined,
> then the linker will scan all the saved archives, in order, for a
> definition of the symbol.  If a definition is found, the linker will
> pull in the appropriate object from the archive.  If that object, in
> turn, has any undefined symbols, the linker will pull in the appropriate
> object from that archive or any later ones, and so forth.  The linker
> will honor --start-group/--end-group while rescanning.
>
> This should ensure that any previously unseen undefined symbols
> introduced by the compiler are handled correctly.  I think it is
> appropriate to do this unconditionally when using plugins, as there is
> no other reasonable way to handle undefined symbols in a file added by a
> plugin.
>
> I've appended a patch to gold which implements this approach.  The patch
> is reasonably efficient and introduces no unnecessary file I/O.  With
> this patch to gold, and no change to gcc, the problematic -static test
> cases which I know about pass.  Also all the current lto.exp tests pass.
> All those tests also pass if I edit the gcc LTO plugin to ignore the
> -pass-through option, as that option is not necessary when the linker
> implements this approach.

I think -pass-through is a hack and linker should simply ignore it.

>
> As this patch does not require any changes to gcc, and fixes some cases
> which are clearly bugs, I plan to commit this patch to binutils mainline
> and to binutils 2.21 branch after a few days if I don't hear any
> comments.
>

It sounds a real progress.

BTW, can you check if the modified gold works on

	PR ld/12248
	PR ld/12277
	PR ld/12291
	PR ld/12323
	PR ld/12365
	PR ld/12369

Thanks.


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19  0:28                       ` Ian Lance Taylor
  2011-01-19  1:38                         ` H.J. Lu
@ 2011-01-19 10:25                         ` Richard Guenther
  2011-01-19 14:57                           ` Ian Lance Taylor
  2011-01-20  0:11                           ` Jan Hubicka
  2011-01-24 21:55                         ` Ian Lance Taylor
  2011-04-15  4:20                         ` Alan Modra
  3 siblings, 2 replies; 37+ messages in thread
From: Richard Guenther @ 2011-01-19 10:25 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: H.J. Lu, Dave Korn, Cary Coutant, GCC Development, Binutils

On Wed, Jan 19, 2011 at 1:27 AM, Ian Lance Taylor <iant@google.com> wrote:
> After some discussion on IRC, here is another approach to resolving the
> issue with static linking and LTO.
>
> In this approach, the linker keeps track of all archives found after the
> first file claimed by the plugin.  If the plugin adds any object files,
> and the object files refer to any symbols which are not yet defined,
> then the linker will scan all the saved archives, in order, for a
> definition of the symbol.  If a definition is found, the linker will
> pull in the appropriate object from the archive.  If that object, in
> turn, has any undefined symbols, the linker will pull in the appropriate
> object from that archive or any later ones, and so forth.  The linker
> will honor --start-group/--end-group while rescanning.
>
> This should ensure that any previously unseen undefined symbols
> introduced by the compiler are handled correctly.  I think it is
> appropriate to do this unconditionally when using plugins, as there is
> no other reasonable way to handle undefined symbols in a file added by a
> plugin.
>
> I've appended a patch to gold which implements this approach.  The patch
> is reasonably efficient and introduces no unnecessary file I/O.  With
> this patch to gold, and no change to gcc, the problematic -static test
> cases which I know about pass.  Also all the current lto.exp tests pass.
> All those tests also pass if I edit the gcc LTO plugin to ignore the
> -pass-through option, as that option is not necessary when the linker
> implements this approach.
>
> As this patch does not require any changes to gcc, and fixes some cases
> which are clearly bugs, I plan to commit this patch to binutils mainline
> and to binutils 2.21 branch after a few days if I don't hear any
> comments.

Nice.  Can we on the GCC side somehow identify Gold versions
that support this and avoid -pass-through handling in that case?
I'm not sure if with your patch the add_input_library or
add_input_file plugin hooks are completely useless (and thus
gold could simply ignore those at all).

Richard.

> Ian
>
>
> 2011-01-18  Ian Lance Taylor  <iant@google.com>
>
>        * plugin.cc (class Plugin_rescan): Define new class.
>        (Plugin_manager::claim_file): Set any_claimed_.
>        (Plugin_manager::save_archive): New function.
>        (Plugin_manager::save_input_group): New function.
>        (Plugin_manager::all_symbols_read): Create Plugin_rescan task if
>        necessary.
>        (Plugin_manager::new_undefined_symbol): New function.
>        (Plugin_manager::rescan): New function.
>        (Plugin_manager::rescannable_defines): New function.
>        (Plugin_manager::add_input_file): Set any_added_.
>        * plugin.h (class Plugin_manager): define new fields rescannable_,
>        undefined_symbols_, any_claimed_, and any_added_.  Declare
>        Plugin_rescan as friend.  Declare new functions.
>        (Plugin_manager::Rescannable): Define type.
>        (Plugin_manager::Rescannable_list): Define type.
>        (Plugin_manager::Undefined_symbol_list): Define type.
>        (Plugin_manager::Plugin_manager): Initialize new fields.
>        * archive.cc (Archive::defines_symbol): New function.
>        (Add_archive_symbols::run): Pass archive to plugins if any.
>        * archive.h (class Archive): Declare defines_symbol.
>        * readsyms.cc (Input_group::~Input_group): New function.
>        (Finish_group::run): Pass input_group to plugins if any.
>        * readsyms.h (class Input_group): Declare destructor.
>        * symtab.cc (add_from_object): Pass undefined symbol to plugins if
>        any.
>
>
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19 10:25                         ` Richard Guenther
@ 2011-01-19 14:57                           ` Ian Lance Taylor
  2011-01-20  0:20                             ` Cary Coutant
  2011-01-20  0:11                           ` Jan Hubicka
  1 sibling, 1 reply; 37+ messages in thread
From: Ian Lance Taylor @ 2011-01-19 14:57 UTC (permalink / raw)
  To: Richard Guenther
  Cc: H.J. Lu, Dave Korn, Cary Coutant, GCC Development, Binutils

Richard Guenther <richard.guenther@gmail.com> writes:

> Nice.  Can we on the GCC side somehow identify Gold versions
> that support this and avoid -pass-through handling in that case?

Sure.  The LDPT_GOLD_VERSION tag, which lto-plugin currently ignores,
will return the version of gold.  I can bump that version when I commit
this patch, and lto-plugin can check it.

> I'm not sure if with your patch the add_input_library or
> add_input_file plugin hooks are completely useless (and thus
> gold could simply ignore those at all).

The plugin does need to use the add_input_file callback.  In any case
I'm not sure it's a great idea for gold to ignore a hook, there might be
some need for it in the future.

Ian

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19 10:25                         ` Richard Guenther
  2011-01-19 14:57                           ` Ian Lance Taylor
@ 2011-01-20  0:11                           ` Jan Hubicka
  1 sibling, 0 replies; 37+ messages in thread
From: Jan Hubicka @ 2011-01-20  0:11 UTC (permalink / raw)
  To: Richard Guenther
  Cc: Ian Lance Taylor, H.J. Lu, Dave Korn, Cary Coutant,
	GCC Development, Binutils

> On Wed, Jan 19, 2011 at 1:27 AM, Ian Lance Taylor <iant@google.com> wrote:
> > After some discussion on IRC, here is another approach to resolving the
> > issue with static linking and LTO.
> >
> > In this approach, the linker keeps track of all archives found after the
> > first file claimed by the plugin.  If the plugin adds any object files,
> > and the object files refer to any symbols which are not yet defined,
> > then the linker will scan all the saved archives, in order, for a
> > definition of the symbol.  If a definition is found, the linker will
> > pull in the appropriate object from the archive.  If that object, in
> > turn, has any undefined symbols, the linker will pull in the appropriate
> > object from that archive or any later ones, and so forth.  The linker
> > will honor --start-group/--end-group while rescanning.
> >
> > This should ensure that any previously unseen undefined symbols
> > introduced by the compiler are handled correctly.  I think it is
> > appropriate to do this unconditionally when using plugins, as there is
> > no other reasonable way to handle undefined symbols in a file added by a
> > plugin.
> >
> > I've appended a patch to gold which implements this approach.  The patch
> > is reasonably efficient and introduces no unnecessary file I/O.  With
> > this patch to gold, and no change to gcc, the problematic -static test
> > cases which I know about pass.  Also all the current lto.exp tests pass.
> > All those tests also pass if I edit the gcc LTO plugin to ignore the
> > -pass-through option, as that option is not necessary when the linker
> > implements this approach.
> >
> > As this patch does not require any changes to gcc, and fixes some cases
> > which are clearly bugs, I plan to commit this patch to binutils mainline
> > and to binutils 2.21 branch after a few days if I don't hear any
> > comments.
> 
> Nice.  Can we on the GCC side somehow identify Gold versions
> that support this and avoid -pass-through handling in that case?
> I'm not sure if with your patch the add_input_library or
> add_input_file plugin hooks are completely useless (and thus
> gold could simply ignore those at all).

Nice indeed.  I think we should version tag the plugin interface and
handle this as version 1.1 or something.
We also want to add the PREVAILING_IRONLY_EXT and other bits that will
need some housekeeping of what plugin interface the linker provides.

Honza

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19 14:57                           ` Ian Lance Taylor
@ 2011-01-20  0:20                             ` Cary Coutant
  0 siblings, 0 replies; 37+ messages in thread
From: Cary Coutant @ 2011-01-20  0:20 UTC (permalink / raw)
  To: Ian Lance Taylor
  Cc: Richard Guenther, H.J. Lu, Dave Korn, GCC Development, Binutils

>> I'm not sure if with your patch the add_input_library or
>> add_input_file plugin hooks are completely useless (and thus
>> gold could simply ignore those at all).
>
> The plugin does need to use the add_input_file callback.  In any case
> I'm not sure it's a great idea for gold to ignore a hook, there might be
> some need for it in the future.

With this patch, the plugin won't need add_input_library, which was
added to support the pass-through option. It still needs
add_input_file, which is how the plugin inserts the newly-compiled
objects.

-cary

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19  0:28                       ` Ian Lance Taylor
  2011-01-19  1:38                         ` H.J. Lu
  2011-01-19 10:25                         ` Richard Guenther
@ 2011-01-24 21:55                         ` Ian Lance Taylor
  2011-04-15  4:20                         ` Alan Modra
  3 siblings, 0 replies; 37+ messages in thread
From: Ian Lance Taylor @ 2011-01-24 21:55 UTC (permalink / raw)
  To: binutils; +Cc: GCC Development, Binutils

Ian Lance Taylor <iant@google.com> writes:

> 2011-01-18  Ian Lance Taylor  <iant@google.com>
>
> 	* plugin.cc (class Plugin_rescan): Define new class.
> 	(Plugin_manager::claim_file): Set any_claimed_.
> 	(Plugin_manager::save_archive): New function.
> 	(Plugin_manager::save_input_group): New function.
> 	(Plugin_manager::all_symbols_read): Create Plugin_rescan task if
> 	necessary.
> 	(Plugin_manager::new_undefined_symbol): New function.
> 	(Plugin_manager::rescan): New function.
> 	(Plugin_manager::rescannable_defines): New function.
> 	(Plugin_manager::add_input_file): Set any_added_.
> 	* plugin.h (class Plugin_manager): define new fields rescannable_,
> 	undefined_symbols_, any_claimed_, and any_added_.  Declare
> 	Plugin_rescan as friend.  Declare new functions.
> 	(Plugin_manager::Rescannable): Define type.
> 	(Plugin_manager::Rescannable_list): Define type.
> 	(Plugin_manager::Undefined_symbol_list): Define type.
> 	(Plugin_manager::Plugin_manager): Initialize new fields.
> 	* archive.cc (Archive::defines_symbol): New function.
> 	(Add_archive_symbols::run): Pass archive to plugins if any.
> 	* archive.h (class Archive): Declare defines_symbol.
> 	* readsyms.cc (Input_group::~Input_group): New function.
> 	(Finish_group::run): Pass input_group to plugins if any.
> 	* readsyms.h (class Input_group): Declare destructor.
> 	* symtab.cc (add_from_object): Pass undefined symbol to plugins if
> 	any.

I have committed this gold patch to binutils mainline and binutils 2.21
branch.

Ian

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-01-19  0:28                       ` Ian Lance Taylor
                                           ` (2 preceding siblings ...)
  2011-01-24 21:55                         ` Ian Lance Taylor
@ 2011-04-15  4:20                         ` Alan Modra
  2011-04-15  6:08                           ` H.J. Lu
  3 siblings, 1 reply; 37+ messages in thread
From: Alan Modra @ 2011-04-15  4:20 UTC (permalink / raw)
  To: binutils

On Tue, Jan 18, 2011 at 04:27:42PM -0800, Ian Lance Taylor wrote:
> After some discussion on IRC, here is another approach to resolving the
> issue with static linking and LTO.
> 
> In this approach, the linker keeps track of all archives found after the
> first file claimed by the plugin.  If the plugin adds any object files,
> and the object files refer to any symbols which are not yet defined,
> then the linker will scan all the saved archives, in order, for a
> definition of the symbol.  If a definition is found, the linker will
> pull in the appropriate object from the archive.  If that object, in
> turn, has any undefined symbols, the linker will pull in the appropriate
> object from that archive or any later ones, and so forth.  The linker
> will honor --start-group/--end-group while rescanning.

This quite simple patch does the same for GNU ld, except that I don't
bother to differentiate between archives found before the first
claimed file and those after.  I'll address that with a followup patch.

	PR ld/12672
	* ldlang.c (enum open_bfd_mode): New.
	(open_input_bfds): Replace "force" param with "mode".  Reload
	archives for rescan.  Update all callers.
	(lang_process): Make another open_input_bfds pass for plugins.
	
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.364
diff -u -p -r1.364 ldlang.c
--- ld/ldlang.c	10 Mar 2011 10:26:26 -0000	1.364
+++ ld/ldlang.c	15 Apr 2011 02:18:49 -0000
@@ -3174,26 +3174,34 @@ init_opb (void)
 
 /* Open all the input files.  */
 
+enum open_bfd_mode
+  {
+    OPEN_BFD_NORMAL = 0,
+    OPEN_BFD_FORCE = 1,
+    OPEN_BFD_RESCAN = 2
+  };
+
 static void
-open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
+open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
 {
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
 	{
 	case lang_constructors_statement_enum:
-	  open_input_bfds (constructor_list.head, force);
+	  open_input_bfds (constructor_list.head, mode);
 	  break;
 	case lang_output_section_statement_enum:
-	  open_input_bfds (s->output_section_statement.children.head, force);
+	  open_input_bfds (s->output_section_statement.children.head, mode);
 	  break;
 	case lang_wild_statement_enum:
 	  /* Maybe we should load the file's symbols.  */
-	  if (s->wild_statement.filename
+	  if ((mode & OPEN_BFD_RESCAN) == 0
+	      && s->wild_statement.filename
 	      && !wildcardp (s->wild_statement.filename)
 	      && !archive_path (s->wild_statement.filename))
 	    lookup_name (s->wild_statement.filename);
-	  open_input_bfds (s->wild_statement.children.head, force);
+	  open_input_bfds (s->wild_statement.children.head, mode);
 	  break;
 	case lang_group_statement_enum:
 	  {
@@ -3206,7 +3214,8 @@ open_input_bfds (lang_statement_union_ty
 	    do
 	      {
 		undefs = link_info.hash->undefs_tail;
-		open_input_bfds (s->group_statement.children.head, TRUE);
+		open_input_bfds (s->group_statement.children.head,
+				 mode | OPEN_BFD_FORCE);
 	      }
 	    while (undefs != link_info.hash->undefs_tail);
 	  }
@@ -3225,8 +3234,8 @@ open_input_bfds (lang_statement_union_ty
 	      /* If we are being called from within a group, and this
 		 is an archive which has already been searched, then
 		 force it to be researched unless the whole archive
-		 has been loaded already.  */
-	      if (force
+		 has been loaded already.  Do the same for a rescan.  */
+	      if (mode != OPEN_BFD_NORMAL
 		  && !s->input_statement.whole_archive
 		  && s->input_statement.loaded
 		  && bfd_check_format (s->input_statement.the_bfd,
@@ -6468,7 +6477,7 @@ lang_process (void)
 
   /* Create a bfd for each input file.  */
   current_target = default_target;
-  open_input_bfds (statement_list.head, FALSE);
+  open_input_bfds (statement_list.head, OPEN_BFD_NORMAL);
 
 #ifdef ENABLE_PLUGINS
   if (plugin_active_plugins_p ())
@@ -6489,7 +6498,7 @@ lang_process (void)
 	einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
 	       plugin_error_plugin ());
       /* Open any newly added files, updating the file chains.  */
-      open_input_bfds (added.head, FALSE);
+      open_input_bfds (added.head, OPEN_BFD_NORMAL);
       /* Restore the global list pointer now they have all been added.  */
       lang_list_remove_tail (stat_ptr, &added);
       /* And detach the fresh ends of the file lists.  */
@@ -6519,6 +6528,8 @@ lang_process (void)
 	  else
 	    lang_list_insert_after (&file_chain, &files, &file_chain.head);
 	}
+      /* Rescan any archives in case new undefined symbols have appeared.  */
+      open_input_bfds (statement_list.head, OPEN_BFD_RESCAN);
     }
 #endif /* ENABLE_PLUGINS */
 

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: PATCH: 2 stage BFD linker for LTO plugin
  2011-04-15  4:20                         ` Alan Modra
@ 2011-04-15  6:08                           ` H.J. Lu
  0 siblings, 0 replies; 37+ messages in thread
From: H.J. Lu @ 2011-04-15  6:08 UTC (permalink / raw)
  To: binutils; +Cc: Alan Modra

On Thu, Apr 14, 2011 at 9:20 PM, Alan Modra <amodra@gmail.com> wrote:
> On Tue, Jan 18, 2011 at 04:27:42PM -0800, Ian Lance Taylor wrote:
>> After some discussion on IRC, here is another approach to resolving the
>> issue with static linking and LTO.
>>
>> In this approach, the linker keeps track of all archives found after the
>> first file claimed by the plugin.  If the plugin adds any object files,
>> and the object files refer to any symbols which are not yet defined,
>> then the linker will scan all the saved archives, in order, for a
>> definition of the symbol.  If a definition is found, the linker will
>> pull in the appropriate object from the archive.  If that object, in
>> turn, has any undefined symbols, the linker will pull in the appropriate
>> object from that archive or any later ones, and so forth.  The linker
>> will honor --start-group/--end-group while rescanning.
>
> This quite simple patch does the same for GNU ld, except that I don't
> bother to differentiate between archives found before the first
> claimed file and those after.  I'll address that with a followup patch.
>
>
>        PR ld/12672
>        * ldlang.c (enum open_bfd_mode): New.
>        (open_input_bfds): Replace "force" param with "mode".  Reload
>        archives for rescan.  Update all callers.
>        (lang_process): Make another open_input_bfds pass for plugins.

There are many LTO tests on davek/lto branch at

http://git.kernel.org/?p=devel/binutils/hjl/x86.git;a=summary


-- 
H.J.

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2011-04-15  6:08 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-04  6:07 PATCH: 2 stage BFD linker for LTO plugin H.J. Lu
2010-12-04 17:34 ` H.J. Lu
2010-12-05  0:44   ` H.J. Lu
2010-12-05 18:23     ` H.J. Lu
2010-12-06  0:11       ` H.J. Lu
2010-12-06  2:21         ` H.J. Lu
2010-12-06 16:59           ` Dave Korn
2010-12-06 17:20             ` H.J. Lu
2010-12-07 20:12               ` H.J. Lu
2010-12-08  5:55                 ` H.J. Lu
2010-12-06 17:44             ` H.J. Lu
2010-12-06 17:55               ` Dave Korn
2010-12-06 17:57                 ` H.J. Lu
2010-12-06 23:29                   ` Alan Modra
2010-12-06 23:32                     ` H.J. Lu
2010-12-06 23:55                       ` Ian Lance Taylor
2010-12-07  0:01                         ` H.J. Lu
2010-12-06 23:54                     ` Ian Lance Taylor
2010-12-07  0:00                       ` H.J. Lu
2010-12-07  0:09                         ` Ian Lance Taylor
2010-12-07  1:02                           ` H.J. Lu
2010-12-07  1:10                             ` H.J. Lu
2010-12-07  1:19                               ` Cary Coutant
2010-12-07  1:32                                 ` H.J. Lu
2010-12-13 21:58                     ` Ian Lance Taylor
2010-12-13 22:18                       ` Cary Coutant
2011-01-19  0:28                       ` Ian Lance Taylor
2011-01-19  1:38                         ` H.J. Lu
2011-01-19 10:25                         ` Richard Guenther
2011-01-19 14:57                           ` Ian Lance Taylor
2011-01-20  0:20                             ` Cary Coutant
2011-01-20  0:11                           ` Jan Hubicka
2011-01-24 21:55                         ` Ian Lance Taylor
2011-04-15  4:20                         ` Alan Modra
2011-04-15  6:08                           ` H.J. Lu
2010-12-07  8:33             ` Tristan Gingold
2010-12-07 10:35               ` Dave Korn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).