This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PATCH: Enable 2 stage linking and support "ld -r" on mixed IR objects in BFD linker


Hi,

This patch enables 2 stage linking and supports "ld -r" on mixed IR
objects in BFD linker. It provides transparent LTO support in BFD
linker.  No GCC changes are required.

Any comments?

Thanks.


H.J.
---
bfd/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* bfd.c (bfd_lto_object_type): New.
	(BFD_PLUGIN): Likewise.
	(BFD_PLUGIN_DUMMY): Likewise.
	(bfd_group_signature): Likewise.
	(BFD_FLAGS_SAVED): Add BFD_PLUGIN and BFD_PLUGIN_DUMMY.
	(BFD_FLAGS_FOR_BFD_USE_MASK): Likewise.
	(bfd): Add object_only_section and lto_type.

	* elf.c (special_sections_g): Add ".gnu.lto" and .gnu_object_only.
	(_bfd_elf_get_special_section): Allow `_' as separator.

	* elflink.c (elf_link_add_archive_symbols): Remove subsbfd.

	* format.c (bfd_set_lto_type): New.
	(bfd_check_format_matches): Use it.

	* section.c (GNU_OBJECT_ONLY_SECTION_NAME): New.

	* bfd-in2.h: Regenerated.

binutils/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* objcopy.c (group_signature): Removed.
	(is_strip_section): Replace group_signature with
	bfd_group_signature.
	(setup_section): Likewise.

	* readelf.c (get_section_type_name): Handle SHT_GNU_OBJECT_ONLY.

gas/testsuite/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* gas/elf/elf.exp: Run section9.

	* gas/elf/section9.d: New.
	* gas/elf/section9.s: Likewise.

include/

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

	* bfdlink.h (bfd_link_info): Add emit_gnu_object_only and
	emitting_gnu_object_only.

include/elf/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* common.h (SHT_GNU_OBJECT_ONLY): New.

ld/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* ldfile.c: Updated for 2 stage linking and support for mixed
	IR/non-IR objects.
	* ldlang.c: Likewise.

	* ldlang.h (lang_input_statement_struct): Add stage1.
	(lang_init): Take a bfd_boolean argument.
	(cmdline_enum_type): New.
	(cmdline_header_type): Likewise.
	(cmdline_input_statement_type): Likewise.
	(cmdline_file_type): Likewise.
	(cmdline_bfd_type): Likewise.
	(cmdline_union_type): Likewise.
	(cmdline_list_type): Likewise.
	(cmdline_option_append): Likewise.
	(cmdline_option_insert_claimed_output): Likewise.
	(cmdline_set_next_claimed_output): Likewise.
	(cmdline_claimed_archive_member_list_append): Likewise.
	(cmdline_emit_object_only_section): Likewise.
	(cmdline_check_object_only_section): Likewise.
	(cmdline_remove_object_only_files): Likewise.

	* ldmain.c (main): Pass FALSE to lang_init.  Use
	ld_parse_linker_script.  Set link_info.output_bfd to NULL
	after close.  Call cmdline_emit_object_only_section if needed.
	(ld_parse_linker_script): New.
	(add_archive_element): Updated for 2 stage linking and support
	for mixed IR/non-IR objects.

	* ldmain.h (ld_parse_linker_script): New.

	* plugin.c (IRONLY_SUFFIX_LEN): Removed.
	(plugin_opt_plugin_arg): Ignore -pass-through=.
	(plugin_get_ir_dummy_bfd): Set BFD_PLUGIN_DUMMY on dummy IR
	input.
	(is_ir_dummy_bfd): Check BFD_PLUGIN_DUMMY.
	(get_symbols): Re-indent.
	(add_input_file): Replace lang_add_input_file with
	cmdline_list_insert_claimed_output.
	(add_input_library): Likewise

	* emultempl/elf32.em (orphan_init_done): Moved out of
	gld${EMULATION_NAME}_place_orphan.
	(hold): Likewise.
	(gld${EMULATION_NAME}_finish): New.
	(ld_${EMULATION_NAME}_emulation): Use gld${EMULATION_NAME}_finish.

	* scripttempl/armbpabi.sc: Also discard .gnu_object_only
	sections.
	* scripttempl/elf.sc: Likewise.
	* scripttempl/elf32sh-symbian.sc: Likewise.
	* scripttempl/elf64hppa.sc: Likewise.
	* scripttempl/elfxtensa.sc: Likewise.
	* scripttempl/mep.sc: Likewise.
	* scripttempl/pe.sc: Likewise.
	* scripttempl/pep.sc: Likewise.

ld/testsuite/

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

	PR ld/12248
	PR ld/12277
	PR ld/12291
	* 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..bea54b1 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1539,6 +1539,9 @@ struct relax_table {
 #define BFD_COM_SECTION_NAME "*COM*"
 #define BFD_IND_SECTION_NAME "*IND*"
 
+/* GNU object-only section name.  */
+#define GNU_OBJECT_ONLY_SECTION_NAME ".gnu_object_only"
+
 /* The absolute section.  */
 extern asection bfd_abs_section;
 #define bfd_abs_section_ptr ((asection *) &bfd_abs_section)
@@ -4971,6 +4974,14 @@ enum bfd_direction
     both_direction = 3
   };
 
+enum bfd_lto_object_type
+  {
+    lto_non_object,
+    lto_non_ir_object,
+    lto_ir_object,
+    lto_mixed_object
+  };
+
 struct bfd
 {
   /* A unique identifier of the BFD  */
@@ -5085,14 +5096,22 @@ struct bfd
   /* Decompress sections in this BFD.  */
 #define BFD_DECOMPRESS 0x10000
 
+  /* This BFD has been processed by the linker plugin.  */
+#define BFD_PLUGIN 0x20000
+
+  /* This BFD is a dummy bfd used by the linker plugin.  */
+#define BFD_PLUGIN_DUMMY 0x40000
+
   /* 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 \
+   | BFD_PLUGIN_DUMMY)
 
   /* 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 \
+   | BFD_PLUGIN_DUMMY)
 
   /* Currently my_archive is tested before adding origin to
      anything. I believe that this can become always an add of
@@ -5115,6 +5134,9 @@ struct bfd
   /* The last section on the section list.  */
   struct bfd_section *section_last;
 
+  /* The object-only section on the section list.  */
+  struct bfd_section *object_only_section;
+
   /* The number of sections.  */
   unsigned int section_count;
 
@@ -5233,6 +5255,9 @@ struct bfd
   /* Set if only required symbols should be added in the link hash table for
      this object.  Used by VMS linkers.  */
   unsigned int selective_search : 1;
+
+  /* LTO object type.  */
+  unsigned int lto_type : 2;
 };
 
 typedef enum bfd_error
@@ -5437,6 +5462,8 @@ void bfd_emul_set_commonpagesize (const char *, bfd_vma);
 
 char *bfd_demangle (bfd *, const char *, int);
 
+asymbol *bfd_group_signature (asection *group, asymbol **isympp);
+
 /* Extracted from archive.c.  */
 symindex bfd_get_next_mapent
    (bfd *abfd, symindex previous, carsym **sym);
diff --git a/bfd/bfd.c b/bfd/bfd.c
index a9ce7cc..7cf8a4e 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -43,6 +43,14 @@ CODE_FRAGMENT
 .    both_direction = 3
 .  };
 .
+.enum bfd_lto_object_type
+.  {
+.    lto_non_object,
+.    lto_non_ir_object,
+.    lto_ir_object,
+.    lto_mixed_object
+.  };
+.
 .struct bfd
 .{
 .  {* A unique identifier of the BFD  *}
@@ -157,14 +165,22 @@ CODE_FRAGMENT
 .  {* Decompress sections in this BFD.  *}
 .#define BFD_DECOMPRESS 0x10000
 .
+.  {* This BFD has been processed by the linker plugin.  *}
+.#define BFD_PLUGIN 0x20000
+.
+.  {* This BFD is a dummy bfd used by the linker plugin.  *}
+.#define BFD_PLUGIN_DUMMY 0x40000
+.
 .  {* 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 \
+.   | BFD_PLUGIN_DUMMY)
 .
 .  {* 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 \
+.   | BFD_PLUGIN_DUMMY)
 .
 .  {* Currently my_archive is tested before adding origin to
 .     anything. I believe that this can become always an add of
@@ -187,6 +203,9 @@ CODE_FRAGMENT
 .  {* The last section on the section list.  *}
 .  struct bfd_section *section_last;
 .
+.  {* The object-only section on the section list.  *}
+.  struct bfd_section *object_only_section;
+.
 .  {* The number of sections.  *}
 .  unsigned int section_count;
 .
@@ -305,6 +324,9 @@ CODE_FRAGMENT
 .  {* Set if only required symbols should be added in the link hash table for
 .     this object.  Used by VMS linkers.  *}
 .  unsigned int selective_search : 1;
+.
+.  {* LTO object type.  *}
+.  unsigned int lto_type : 2;
 .};
 .
 */
@@ -1929,3 +1951,36 @@ bfd_demangle (bfd *abfd, const char *name, int options)
 
   return res;
 }
+
+/*
+FUNCTION
+	bfd_group_signature
+
+SYNOPSIS
+	asymbol *bfd_group_signature (asection *group, asymbol **isympp);
+
+DESCRIPTION
+	Return a pointer to the symbol used as a signature for GROUP.
+*/
+
+asymbol *
+bfd_group_signature (asection *group, asymbol **isympp)
+{
+  bfd *abfd = group->owner;
+  Elf_Internal_Shdr *ghdr;
+
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return NULL;
+
+  ghdr = &elf_section_data (group)->this_hdr;
+  if (ghdr->sh_link < elf_numsections (abfd))
+    {
+      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link];
+
+      if (symhdr->sh_type == SHT_SYMTAB
+	  && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym)
+	return isympp[ghdr->sh_info - 1];
+    }
+  return NULL;
+}
diff --git a/bfd/elf.c b/bfd/elf.c
index 075a668..2e38029 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -2082,6 +2082,7 @@ static const struct bfd_elf_special_section special_sections_f[] =
 static const struct bfd_elf_special_section special_sections_g[] =
 {
   { STRING_COMMA_LEN (".gnu.linkonce.b"), -2, SHT_NOBITS,      SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".gnu.lto"),        -2, SHT_PROGBITS,    SHF_EXCLUDE },
   { STRING_COMMA_LEN (".got"),             0, SHT_PROGBITS,    SHF_ALLOC + SHF_WRITE },
   { STRING_COMMA_LEN (".gnu.version"),     0, SHT_GNU_versym,  0 },
   { STRING_COMMA_LEN (".gnu.version_d"),   0, SHT_GNU_verdef,  0 },
@@ -2089,6 +2090,7 @@ static const struct bfd_elf_special_section special_sections_g[] =
   { STRING_COMMA_LEN (".gnu.liblist"),     0, SHT_GNU_LIBLIST, SHF_ALLOC },
   { STRING_COMMA_LEN (".gnu.conflict"),    0, SHT_RELA,        SHF_ALLOC },
   { STRING_COMMA_LEN (".gnu.hash"),        0, SHT_GNU_HASH,    SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu_object_only"), 0, SHT_GNU_OBJECT_ONLY, SHF_EXCLUDE },
   { NULL,                        0,        0, 0,               0 }
 };
 
@@ -2220,6 +2222,7 @@ _bfd_elf_get_special_section (const char *name,
 	      if (suffix_len == 0)
 		continue;
 	      if (name[prefix_len] != '.'
+		  && name[prefix_len] != '_' 
 		  && (suffix_len == -2
 		      || (rela && spec[i].type == SHT_REL)))
 		continue;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 32575d9..a8aac7c 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -5011,7 +5011,6 @@ elf_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
 	{
 	  struct elf_link_hash_entry *h;
 	  bfd *element;
-	  bfd *subsbfd = NULL;
 	  struct bfd_link_hash_entry *undefs_tail;
 	  symindex mark;
 
@@ -5075,11 +5074,9 @@ elf_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
 	  undefs_tail = info->hash->undefs_tail;
 
 	  if (! (*info->callbacks->add_archive_element)
-				(info, element, symdef->name, &subsbfd))
+				(info, element, symdef->name, &element))
 	    goto error_return;
-	  /* Potentially, the add_archive_element hook may have set a
-	     substitute BFD for us.  */
-	  if (! bfd_link_add_symbols (subsbfd ? subsbfd : element, info))
+	  if (! bfd_link_add_symbols (element, info))
 	    goto error_return;
 
 	  /* If there are any new undefined symbols, we need to make
diff --git a/bfd/format.c b/bfd/format.c
index 4d89a85..6feac37 100644
--- a/bfd/format.c
+++ b/bfd/format.c
@@ -95,6 +95,33 @@ bfd_check_format (bfd *abfd, bfd_format format)
   return bfd_check_format_matches (abfd, format, NULL);
 }
 
+/* Set lto_type in ABFD.  */
+
+static void
+bfd_set_lto_type (bfd *abfd)
+{
+  if (abfd->format == bfd_object
+      && abfd->lto_type == lto_non_object
+      && (abfd->flags & (DYNAMIC | EXEC_P)) == 0)
+    {
+      asection *sec;
+      enum bfd_lto_object_type type = lto_non_ir_object;
+      for (sec = abfd->sections; sec != NULL; sec = sec->next)
+	{
+	  if (strcmp (sec->name, GNU_OBJECT_ONLY_SECTION_NAME) == 0)
+	    {
+	      type = lto_mixed_object;
+	      abfd->object_only_section = sec;
+	      break;
+	    }
+	  else if (type != lto_ir_object
+		   && strncmp (sec->name, ".gnu.lto_", 9) == 0)
+	    type = lto_ir_object;
+	}
+      abfd->lto_type = type;
+    }
+}
+
 /*
 FUNCTION
 	bfd_check_format_matches
@@ -136,7 +163,10 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
     }
 
   if (abfd->format != bfd_unknown)
-    return abfd->format == format;
+    {
+      bfd_set_lto_type (abfd);
+      return abfd->format == format;
+    }
 
   /* Since the target type was defaulted, check them
      all in the hope that one will be uniquely recognized.  */
@@ -300,6 +330,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
 
       if (matching_vector)
 	free (matching_vector);
+
+      bfd_set_lto_type (abfd);
+
       return TRUE;			/* File position has moved, BTW.  */
     }
 
diff --git a/bfd/section.c b/bfd/section.c
index bff8adf..940e89e 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -546,6 +546,9 @@ CODE_FRAGMENT
 .#define BFD_COM_SECTION_NAME "*COM*"
 .#define BFD_IND_SECTION_NAME "*IND*"
 .
+.{* GNU object-only section name.  *}
+.#define GNU_OBJECT_ONLY_SECTION_NAME ".gnu_object_only"
+.
 .{* The absolute section.  *}
 .extern asection bfd_abs_section;
 .#define bfd_abs_section_ptr ((asection *) &bfd_abs_section)
diff --git a/binutils/objcopy.c b/binutils/objcopy.c
index ac176df..4c8b48b 100644
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -901,30 +901,6 @@ is_specified_symbol (const char *name, htab_t htab)
   return htab_find (htab, name) != NULL;
 }
 
-/* Return a pointer to the symbol used as a signature for GROUP.  */
-
-static asymbol *
-group_signature (asection *group)
-{
-  bfd *abfd = group->owner;
-  Elf_Internal_Shdr *ghdr;
-
-  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
-    return NULL;
-
-  ghdr = &elf_section_data (group)->this_hdr;
-  if (ghdr->sh_link < elf_numsections (abfd))
-    {
-      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-      Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link];
-
-      if (symhdr->sh_type == SHT_SYMTAB
-	  && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym)
-	return isympp[ghdr->sh_info - 1];
-    }
-  return NULL;
-}
-
 /* See if a section is being removed.  */
 
 static bfd_boolean
@@ -963,7 +939,7 @@ is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
       /* PR binutils/3181
 	 If we are going to strip the group signature symbol, then
 	 strip the group section too.  */
-      gsym = group_signature (sec);
+      gsym = bfd_group_signature (sec, isympp);
       if (gsym != NULL)
 	gname = gsym->name;
       else
@@ -2537,7 +2513,7 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
 
   if ((isection->flags & SEC_GROUP) != 0)
     {
-      asymbol *gsym = group_signature (isection);
+      asymbol *gsym = bfd_group_signature (isection, isympp);
 
       if (gsym != NULL)
 	{
diff --git a/binutils/readelf.c b/binutils/readelf.c
index b8ab55b..6d0a368 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -2999,6 +2999,7 @@ get_section_type_name (unsigned int sh_type)
     case 0x7ffffffd:		return "AUXILIARY";
     case 0x7fffffff:		return "FILTER";
     case SHT_GNU_LIBLIST:	return "GNU_LIBLIST";
+    case SHT_GNU_OBJECT_ONLY:	return "GNU_OBJECT_ONLY";
 
     default:
       if ((sh_type >= SHT_LOPROC) && (sh_type <= SHT_HIPROC))
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 3babe0b..7f86266 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -174,6 +174,7 @@ if { ([istarget "*-*-*elf*"]
     run_dump_test "section6"
     run_dump_test "section7"
     run_dump_test "section8"
+    run_dump_test "section9"
     run_dump_test "dwarf2-1"
     run_dump_test "dwarf2-2"
     run_dump_test "dwarf2-3"
diff --git a/gas/testsuite/gas/elf/section9.d b/gas/testsuite/gas/elf/section9.d
new file mode 100644
index 0000000..c1df902
--- /dev/null
+++ b/gas/testsuite/gas/elf/section9.d
@@ -0,0 +1,7 @@
+#readelf: -S --wide
+#name: section flags
+
+#...
+[ 	]*\[.*\][ 	]+\.gnu\.lto_main[ 	]+PROGBITS.*[ 	]+E[   ]+.*
+[ 	]*\[.*\][ 	]+\.gnu\.lto_.pureconst[ 	]+PROGBITS.*[ 	]+E[   ]+.*
+#pass
diff --git a/gas/testsuite/gas/elf/section9.s b/gas/testsuite/gas/elf/section9.s
new file mode 100644
index 0000000..6b8b107
--- /dev/null
+++ b/gas/testsuite/gas/elf/section9.s
@@ -0,0 +1,4 @@
+	.section .gnu.lto_main,"",%progbits
+	.byte 0,0,0,0
+	.section .gnu.lto_.pureconst,"",%progbits
+	.byte 0,0,0,0
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 0d6e9f8..8be47df 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -351,6 +351,12 @@ struct bfd_link_info
      --dynamic-list command line options.  */
   unsigned int dynamic: 1;
 
+  /* TRUE if .gnu_object_only section should be created.  */
+  unsigned int emit_gnu_object_only: 1;
+
+  /* TRUE if .gnu_object_only section is being created.  */
+  unsigned int emitting_gnu_object_only: 1;
+
   /* Non-NULL if .note.gnu.build-id section should be created.  */
   char *emit_note_gnu_build_id;
 
diff --git a/include/elf/common.h b/include/elf/common.h
index 01c519d..335420c 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -467,6 +467,7 @@
 #define SHT_GNU_ATTRIBUTES 0x6ffffff5	/* Object attributes */
 #define SHT_GNU_HASH	0x6ffffff6	/* GNU style symbol hash table */
 #define SHT_GNU_LIBLIST	0x6ffffff7	/* List of prelink dependencies */
+#define SHT_GNU_OBJECT_ONLY 0x6ffffff8	/* Object only */
 
 /* The next three section types are defined by Solaris, and are named
    SHT_SUNW*.  We use them in GNU code, so we also define SHT_GNU*
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index 34cc82c..6bdea51 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -1755,6 +1755,64 @@ output_rel_find (asection *sec, int isdyn)
   return last;
 }
 
+static int orphan_init_done = 0;
+
+static struct orphan_save orig_hold[] =
+  {
+    { ".text",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
+      0, 0, 0, 0 },
+    { ".rodata",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".data",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".bss",
+      SEC_ALLOC,
+      0, 0, 0, 0 },
+    { 0,
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".interp",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".sdata",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA,
+      0, 0, 0, 0 },
+    { 0,
+      SEC_HAS_CONTENTS,
+      0, 0, 0, 0 },
+  };
+
+static struct orphan_save hold[] =
+  {
+    { ".text",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
+      0, 0, 0, 0 },
+    { ".rodata",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".data",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".bss",
+      SEC_ALLOC,
+      0, 0, 0, 0 },
+    { 0,
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".interp",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+      0, 0, 0, 0 },
+    { ".sdata",
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA,
+      0, 0, 0, 0 },
+    { 0,
+      SEC_HAS_CONTENTS,
+      0, 0, 0, 0 },
+  };
+
 /* Place an orphan section.  We use this to put random SHF_ALLOC
    sections in the right segment.  */
 
@@ -1763,33 +1821,6 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
 				   const char *secname,
 				   int constraint)
 {
-  static struct orphan_save hold[] =
-    {
-      { ".text",
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
-	0, 0, 0, 0 },
-      { ".rodata",
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
-	0, 0, 0, 0 },
-      { ".data",
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA,
-	0, 0, 0, 0 },
-      { ".bss",
-	SEC_ALLOC,
-	0, 0, 0, 0 },
-      { 0,
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
-	0, 0, 0, 0 },
-      { ".interp",
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
-	0, 0, 0, 0 },
-      { ".sdata",
-	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA,
-	0, 0, 0, 0 },
-      { 0,
-	SEC_HAS_CONTENTS,
-	0, 0, 0, 0 },
-    };
   enum orphan_save_index
     {
       orphan_text = 0,
@@ -1801,7 +1832,6 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
       orphan_sdata,
       orphan_nonalloc
     };
-  static int orphan_init_done = 0;
   struct orphan_save *place;
   lang_output_section_statement_type *after;
   lang_output_section_statement_type *os;
@@ -1964,7 +1994,24 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
 
   return lang_insert_orphan (s, secname, constraint, after, place, NULL, NULL);
 }
+
+/* Final emulation specific call.  */
+
+static void
+gld${EMULATION_NAME}_finish (void)
+{
+  /* Support the object-only output.  */
+  if (link_info.emit_gnu_object_only)
+    {
+      memcpy (hold, orig_hold, sizeof (orig_hold));
+      orphan_init_done = 0;
+    }
+
+  finish_default ();
+}
 EOF
+elif test x"$LDEMUL_FINISH" != xgld"$EMULATION_NAME"_finish; then
+  LDEMUL_FINISH=finish_default
 fi
 
 if test x"$LDEMUL_AFTER_ALLOCATION" != xgld"$EMULATION_NAME"_after_allocation; then
@@ -2478,7 +2525,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_GET_SCRIPT-gld${EMULATION_NAME}_get_script},
   "${EMULATION_NAME}",
   "${OUTPUT_FORMAT}",
-  ${LDEMUL_FINISH-finish_default},
+  ${LDEMUL_FINISH-gld${EMULATION_NAME}_finish},
   ${LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS-NULL},
   ${LDEMUL_OPEN_DYNAMIC_ARCHIVE-gld${EMULATION_NAME}_open_dynamic_archive},
   ${LDEMUL_PLACE_ORPHAN-gld${EMULATION_NAME}_place_orphan},
diff --git a/ld/ldfile.c b/ld/ldfile.c
index a9a6954..e1568d6 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -312,45 +312,68 @@ 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)
+		{
+		  cmdline_set_next_claimed_output (NULL);
+		  cmdline_check_object_only_section (entry->the_bfd,
+						     TRUE);
+		  /* 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);
+		  entry->the_bfd->flags |= BFD_PLUGIN_DUMMY;
+		}
+	      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;
+	}
     }
+  else
 #endif /* ENABLE_PLUGINS */
+    cmdline_check_object_only_section (entry->the_bfd, FALSE);
+
+  /* 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.  */
diff --git a/ld/ldlang.c b/ld/ldlang.c
index e804a53..2302f57 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -37,6 +37,7 @@
 #include "ldctor.h"
 #include "ldfile.h"
 #include "ldemul.h"
+#include "ldwrite.h"
 #include "fnmatch.h"
 #include "demangle.h"
 #include "hashtab.h"
@@ -45,6 +46,9 @@
 #include "plugin.h"
 #endif /* ENABLE_PLUGINS */
 
+/* FIXME: Put it here to avoid NAME conflict from ldgram.h.  */
+#include "elf-bfd.h"
+
 #ifndef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
 #endif
@@ -70,6 +74,12 @@ 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_options;
+static cmdline_list_type cmdline_object_only_file_list;
+static cmdline_list_type cmdline_object_only_archive_list;
+static cmdline_list_type cmdline_temp_object_only_list;
+static cmdline_list_type cmdline_claimed_archive_member_list;
+static cmdline_union_type **cmdline_next_claimed_output;
 
 /* Forward declarations.  */
 static void exp_init_os (etree_type *);
@@ -90,6 +100,12 @@ 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_lists_init (void);
+static void cmdline_get_stage2_input_files (void);
+static void cmdline_get_object_only_input_files (void);
+static void print_cmdline_list (cmdline_union_type *);
+static bfd_boolean cmdline_claimed_archive_member_p (bfd *);
+static bfd_boolean cmdline_on_object_only_archive_list_p (bfd *);
 
 /* Exported variables.  */
 const char *output_target;
@@ -1003,6 +1019,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')
@@ -1169,14 +1186,17 @@ output_section_statement_table_free (void)
 /* Build enough state so that the parser can build its tree.  */
 
 void
-lang_init (void)
+lang_init (bfd_boolean object_only)
 {
-  obstack_begin (&stat_obstack, 1000);
+  if (!object_only)
+    obstack_begin (&stat_obstack, 1000);
 
   stat_ptr = &statement_list;
 
   output_section_statement_table_init ();
 
+  cmdline_lists_init ();
+
   lang_list_init (stat_ptr);
 
   lang_list_init (&input_file_chain);
@@ -1195,10 +1215,11 @@ lang_init (void)
      simpler to re-use working machinery than using a linked list in terms
      of code-complexity here in ld, besides the initialization which just
      looks like other code here.  */
-  if (!bfd_hash_table_init_n (&lang_definedness_table,
-			      lang_definedness_newfunc,
-			      sizeof (struct lang_definedness_hash_entry),
-			      3))
+  if (!object_only
+      && !bfd_hash_table_init_n (&lang_definedness_table,
+				 lang_definedness_newfunc,
+				 sizeof (struct lang_definedness_hash_entry),
+				 3))
     einfo (_("%P%F: can not create hash table: %E\n"));
 }
 
@@ -2717,15 +2738,22 @@ load_symbols (lang_input_statement_type *entry,
 		  loaded = FALSE;
 		}
 
-	      subsbfd = NULL;
+	      if (link_info.emitting_gnu_object_only)
+		{
+		  if (!cmdline_on_object_only_archive_list_p (member))
+		      continue;
+		}
+	      else if (cmdline_claimed_archive_member_p (member))
+		continue;
+
+	      subsbfd = member;
 	      if (! ((*link_info.callbacks->add_archive_element)
 		     (&link_info, member, "--whole-archive", &subsbfd)))
 		abort ();
 
 	      /* Potentially, the add_archive_element hook may have set a
 		 substitute BFD for us.  */
-	      if (! bfd_link_add_symbols (subsbfd ? subsbfd : member,
-					&link_info))
+	      if (! bfd_link_add_symbols (member, &link_info))
 		{
 		  einfo (_("%F%B: could not read symbols: %E\n"), member);
 		  loaded = FALSE;
@@ -6249,10 +6277,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,23 +6451,113 @@ 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)
+	    {
+	      info_msg (_("Stage 2 command line:\n "));
+	      print_cmdline_list (cmdline_options.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);
+	}
     }
+  else
 #endif /* ENABLE_PLUGINS */
+    if (link_info.relocatable)
+    {
+      /* Check if .gnu_object_only section should be created.  */
+      bfd *p;
+      int object_type;
+
+      object_type = 0;
+      for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
+	{
+	  object_type |= 1 << p->lto_type;
+	  if ((object_type & (1 << lto_mixed_object)) != 0
+	      || ((object_type
+		   & (1 << lto_non_ir_object
+		      | 1 << lto_ir_object))
+		  == (1 << lto_non_ir_object | 1 << lto_ir_object)))
+	    {
+	      link_info.emit_gnu_object_only = TRUE;
+	      break;
+	    }
+	}
+
+      if (trace_file_tries
+	  && (cmdline_object_only_file_list.head
+	      || cmdline_object_only_archive_list.head))
+	{
+	  info_msg (_("Object-only input files:\n "));
+	  print_cmdline_list (cmdline_object_only_file_list.head);
+	  print_cmdline_list (cmdline_object_only_archive_list.head);
+	}
+    }
 
   link_info.gc_sym_list = &entry_symbol;
   if (entry_symbol.name == NULL)
@@ -7852,3 +7975,1162 @@ lang_append_dynamic_list_cpp_new (void)
 
   lang_append_dynamic_list (dynamic);
 }
+
+static void
+cmdline_lists_init (void)
+{
+  cmdline_options.tail = &cmdline_options.head;
+  cmdline_object_only_file_list.tail
+    = &cmdline_object_only_file_list.head;
+  cmdline_object_only_archive_list.tail
+    = &cmdline_object_only_archive_list.head;
+  cmdline_temp_object_only_list.tail
+    = &cmdline_temp_object_only_list.head;
+  cmdline_claimed_archive_member_list.tail
+    = &cmdline_claimed_archive_member_list.head;
+}
+
+/* Append an item with TYPE and DATA to LIST.  */
+
+static void
+cmdline_list_append (cmdline_list_type *list, 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;
+  *list->tail = new_opt;
+  list->tail = &new_opt->header.next;
+  switch (type)
+    {
+    default:
+      break;
+    case cmdline_is_lang_input_statement_enum:
+      new_opt->input_statement.input = (lang_input_statement_type *) data;
+      break;
+    case cmdline_is_file_enum:
+      new_opt->file.filename = (const char *) data;
+      break;
+    case cmdline_is_bfd_enum:
+      new_opt->abfd.abfd = (bfd *) data;
+      break;
+    }
+}
+
+/* Append a cmmand line option to cmdline_options list.  */
+
+void
+cmdline_option_append (cmdline_enum_type type, void *data)
+{
+  cmdline_list_append (&cmdline_options, 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_options.head;
+      for (c = cmdline_options.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 FILENAME after cmdline_next_claimed_output.  */
+
+void
+cmdline_option_insert_claimed_output (const char *filename)
+{
+  cmdline_union_type *new_opt, *next;
+
+  if (trace_files || trace_file_tries)
+    info_msg (_("insert plugin claimed output: %s\n"), filename);
+
+  new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+  new_opt->header.type = cmdline_is_file_enum;
+  new_opt->file.filename = filename;
+
+  next = *cmdline_next_claimed_output;
+  *cmdline_next_claimed_output = new_opt;
+  new_opt->header.next = next;
+  if (cmdline_options.tail == cmdline_next_claimed_output)
+    {
+      cmdline_next_claimed_output = &new_opt->header.next;
+      cmdline_options.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_options.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_file_enum:
+	lang_add_input_file (c->file.filename,
+			     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
+print_cmdline_list (cmdline_union_type *c)
+{
+  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_file_enum:
+	info_msg (" %s", c->file.filename);
+	break;
+      case cmdline_is_enter_group_enum:
+	info_msg (" --start-group");
+	break;
+      case cmdline_is_leave_group_enum:
+	info_msg (" --end-group");
+	break;
+      case cmdline_is_bfd_enum:
+	info_msg (" [%B]", c->abfd.abfd);
+	break;
+      }
+
+  info_msg ("\n");
+}
+
+/* Append a BFD to cmdline_claimed_archive_member_list.  */
+
+void
+cmdline_claimed_archive_member_list_append (bfd *abfd)
+{
+  cmdline_list_append (&cmdline_claimed_archive_member_list,
+		       cmdline_is_bfd_enum, abfd);
+}
+
+/* Return TRUE if ABFD has been claimed by plugin.  */
+
+static bfd_boolean
+cmdline_claimed_archive_member_p (bfd *abfd)
+{
+  cmdline_union_type *c = cmdline_claimed_archive_member_list.head;
+  bfd *archive = bfd_my_archive (abfd);
+  bfd *calimed, *claimed_archive;
+
+  if (!archive)
+    return FALSE;
+
+  for (; c != NULL; c = c->header.next)
+    switch (c->header.type)
+      {
+      default:
+	abort ();
+      case cmdline_is_bfd_enum:
+	calimed = c->abfd.abfd;
+	claimed_archive = bfd_my_archive (calimed);
+	if (strcmp (archive->filename, claimed_archive->filename) == 0
+	    && abfd->origin == calimed->origin
+	    && arelt_size (abfd) == arelt_size (calimed))
+	  return TRUE;
+	break;
+      }
+
+  return FALSE;
+}
+
+/* Return TRUE if ABFD is on cmdline_object_only_archive_list.  */
+
+static bfd_boolean
+cmdline_on_object_only_archive_list_p (bfd *abfd)
+{
+  cmdline_union_type *c, *next;
+  bfd *archive, *obfd, *oarchive;
+  ufile_ptr origin = abfd->origin;
+
+  archive = bfd_my_archive (abfd);
+  for (c = cmdline_object_only_archive_list.head; c != NULL; c = next)
+    {
+      if (c->header.type != cmdline_is_bfd_enum)
+	abort ();
+
+      next = c->header.next;
+      obfd = c->abfd.abfd;
+      oarchive = bfd_my_archive (obfd);
+
+      /* The list is grouped by archive file name and sorted by member
+	 origin.  */
+      if (strcmp (archive->filename, oarchive->filename) != 0)
+	continue;
+
+      if (origin == obfd->origin)
+	return TRUE;
+      else if (origin < obfd->origin)
+	return FALSE;
+    }
+
+  return FALSE;
+}
+
+/* Append an item with TYPE and DATA to cmdline_object_only_file_list
+   or cmdline_object_only_archive_list if needed.  */
+
+static void
+cmdline_object_only_list_append (cmdline_enum_type type, void *data)
+{
+  cmdline_union_type *c;
+  cmdline_union_type *new_opt, *next, **prev;
+  bfd *abfd, *archive;
+  bfd *obfd, *oarchive;
+  bfd *nbfd, *narchive;
+  ufile_ptr origin, norigin;
+
+  /* Put it on cmdline_object_only_file_list if it isn't an archive
+     member.  */
+  switch (type)
+    {
+    default:
+      abort ();
+    case cmdline_is_bfd_enum:
+      abfd = (bfd *) data;
+      archive = bfd_my_archive (abfd);
+      if (archive)
+	break;
+    case cmdline_is_file_enum:
+      cmdline_list_append (&cmdline_object_only_file_list, type, data);
+      return;
+    }
+
+  /* Put archive member on cmdline_object_only_archive_list and sort
+     the list by archive name and archive member origin.  */
+  new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+  new_opt->header.type = cmdline_is_bfd_enum;
+  new_opt->abfd.abfd = (bfd *) data;
+
+  c = cmdline_object_only_archive_list.head;
+  if (c == NULL)
+    {
+      cmdline_object_only_archive_list.head = new_opt;
+      cmdline_object_only_archive_list.tail = &new_opt->header.next;
+      return;
+    }
+
+  prev = NULL;
+  origin = abfd->origin;
+  for (; c != NULL; c = next)
+    {
+      if (c->header.type != cmdline_is_bfd_enum)
+	abort ();
+
+      next = c->header.next;
+
+      obfd = c->abfd.abfd;
+      oarchive = bfd_my_archive (obfd);
+
+      if (strcmp (archive->filename, oarchive->filename) == 0)
+	{
+	  bfd_boolean after;
+
+	  if (origin < obfd->origin)
+	    {
+	      /* Insert it before the current.  */
+	      new_opt->header.next = c;
+	      if (prev)
+		*prev = new_opt;
+	      else
+		cmdline_object_only_archive_list.head = new_opt;
+	      return;
+	    }
+
+	  after = TRUE;
+
+	  /* Check origin.  */
+	  while (next)
+	    {
+	      if (next->header.type != cmdline_is_bfd_enum)
+		abort ();
+
+	      nbfd = next->abfd.abfd;
+	      norigin = nbfd->origin;
+	      if (origin > norigin)
+		{
+		  /* Insert it after NEXT.  */
+		  break;
+		}
+
+	      narchive = bfd_my_archive (nbfd);
+	      if (strcmp (archive->filename, narchive->filename) != 0)
+		{
+		  /* Insert it befor NEXT.  */
+		  after = FALSE;
+		  break;
+		}
+
+	      c = next;
+	      next = next->header.next;
+	    }
+
+	  if (after && next)
+	    {
+	      c = next;
+	      next = next->header.next;
+	    }
+
+	  if (*cmdline_object_only_archive_list.tail == c->header.next)
+	    cmdline_object_only_archive_list.tail
+	      = &new_opt->header.next;
+
+	  prev = &c->header.next;
+	  new_opt->header.next = next;
+	  *prev = new_opt;
+	  return;
+	}
+
+      prev = &c->header.next;
+    }
+
+  *cmdline_object_only_archive_list.tail = new_opt;
+  cmdline_object_only_archive_list.tail = &new_opt->header.next;
+}
+
+/* Get object-only input files.  */
+
+static void
+cmdline_get_object_only_input_files (void)
+{
+  cmdline_union_type *c, *next;
+  bfd *abfd, *archive;
+  bfd *nbfd, *narchive;
+
+  /* Add files first.  */
+  for (c = cmdline_object_only_file_list.head;
+       c != NULL; c = c->header.next)
+    switch (c->header.type)
+      {
+      default:
+	abort ();
+      case cmdline_is_file_enum:
+	lang_add_input_file (c->file.filename,
+			     lang_input_file_is_file_enum, NULL);
+	break;
+      case cmdline_is_bfd_enum:
+	abfd = c->abfd.abfd;
+	if (bfd_my_archive (abfd))
+	  abort ();
+	lang_add_input_file (abfd->filename,
+			     lang_input_file_is_file_enum, NULL);
+	break;
+      }
+
+  /* Add archive members next.  */
+  for (c = cmdline_object_only_archive_list.head; c != NULL; c = next)
+    {
+      if (c->header.type != cmdline_is_bfd_enum)
+	abort ();
+
+      next = c->header.next;
+
+      abfd = c->abfd.abfd;
+      archive = bfd_my_archive (abfd);
+   
+      /* Add the first archive of the archive member group.  */
+      lang_add_input_file (archive->filename,
+			   lang_input_file_is_file_enum, NULL);
+
+      /* Skip the rest members in the archive member group.  */
+      do
+	{
+	  if (!next)
+	    break;
+
+	  if (next->header.type != cmdline_is_bfd_enum)
+	    abort ();
+
+	  next = next->header.next;
+	  if (!next)
+	    break;
+	  nbfd = next->abfd.abfd;
+	  narchive = bfd_my_archive (nbfd);
+	}
+      while (strcmp (archive->filename, narchive->filename) == 0);
+    }
+}
+
+struct cmdline_arg
+{
+  bfd *obfd;
+  asymbol **isympp;
+  int status;
+};
+
+/* Create a section in OBFD with the same
+   name and attributes as ISECTION in IBFD.  */
+
+static void
+setup_section (bfd *ibfd, sec_ptr isection, void *p)
+{
+  struct cmdline_arg *arg = (struct cmdline_arg *) p;
+  bfd *obfd = arg->obfd;
+  asymbol **isympp = arg->isympp;
+  const char *name = isection->name;
+  sec_ptr osection;
+  const char *err;
+
+  /* Skip the object-only section.  */
+  if (ibfd->object_only_section == isection)
+    return;
+
+  /* If we have already failed earlier on, do not keep on generating
+     complaints now.  */
+  if (arg->status)
+    return;
+
+  osection = bfd_make_section_anyway_with_flags (obfd, name,
+						 isection->flags);
+
+  if (osection == NULL)
+    {
+      err = _("failed to create output section");
+      goto loser;
+    }
+
+  osection->size = isection->size;
+  osection->vma = isection->vma;
+  osection->lma = isection->lma;
+  osection->alignment_power = isection->alignment_power;
+
+  /* Copy merge entity size.  */
+  osection->entsize = isection->entsize;
+
+  /* This used to be mangle_section; we do here to avoid using
+     bfd_get_section_by_name since some formats allow multiple
+     sections with the same name.  */
+  isection->output_section = osection;
+  isection->output_offset = 0;
+
+  if ((isection->flags & SEC_GROUP) != 0)
+    {
+      asymbol *gsym = bfd_group_signature (isection, isympp);
+
+      if (gsym != NULL)
+	{
+	  gsym->flags |= BSF_KEEP;
+	  if (ibfd->xvec->flavour == bfd_target_elf_flavour)
+	    elf_group_id (isection) = gsym;
+	}
+    }
+
+  /* Allow the BFD backend to copy any private data it understands
+     from the input section to the output section.  */
+  if (!bfd_copy_private_section_data (ibfd, isection, obfd, osection))
+    {
+      err = _("failed to copy private data");
+      goto loser;
+    }
+
+  /* All went well.  */
+  return;
+
+loser:
+  arg->status = 1;
+  einfo (_("%P%F: setup_section: %s: %s\n"), err, name);
+}
+
+/* Copy the data of input section ISECTION of IBFD
+   to an output section with the same name in OBFD.
+   If stripping then don't copy any relocation info.  */
+
+static void
+copy_section (bfd *ibfd, sec_ptr isection, void *p)
+{
+  struct cmdline_arg *arg = (struct cmdline_arg *) p;
+  bfd *obfd = arg->obfd;
+  asymbol **isympp = arg->isympp;
+  arelent **relpp;
+  long relcount;
+  sec_ptr osection;
+  bfd_size_type size;
+  long relsize;
+  flagword flags;
+  const char *err;
+
+  /* Skip the object-only section.  */
+  if (ibfd->object_only_section == isection)
+    return;
+
+  /* If we have already failed earlier on, do not keep on generating
+     complaints now.  */
+  if (arg->status)
+    return;
+
+  flags = bfd_get_section_flags (ibfd, isection);
+  if ((flags & SEC_GROUP) != 0)
+    return;
+
+  osection = isection->output_section;
+  size = bfd_get_section_size (isection);
+
+  if (size == 0 || osection == 0)
+    return;
+
+  relsize = bfd_get_reloc_upper_bound (ibfd, isection);
+
+  if (relsize < 0)
+    {
+      /* Do not complain if the target does not support relocations.  */
+      if (relsize == -1
+	  && bfd_get_error () == bfd_error_invalid_operation)
+	relsize = 0;
+      else
+	{
+	  err = bfd_errmsg (bfd_get_error ());
+	  goto loser;
+	}
+    }
+
+  if (relsize == 0)
+    bfd_set_reloc (obfd, osection, NULL, 0);
+  else
+    {
+      relpp = (arelent **) xmalloc (relsize);
+      relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp);
+      if (relcount < 0)
+	{
+	  err = _("relocation count is negative");
+	  goto loser;
+	}
+
+      bfd_set_reloc (obfd, osection,
+		     relcount == 0 ? NULL : relpp, relcount);
+      if (relcount == 0)
+	free (relpp);
+    }
+
+  if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS)
+    {
+      bfd_byte *memhunk = NULL;
+
+      if (!bfd_get_full_section_contents (ibfd, isection, &memhunk))
+	{
+	  err = bfd_errmsg (bfd_get_error ());
+	  goto loser;
+	}
+
+      if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size))
+	{
+	  err = bfd_errmsg (bfd_get_error ());
+	  goto loser;
+	}
+      free (memhunk);
+    }
+
+  /* All went well.  */
+  return;
+
+loser:
+  einfo (_("%P%F: copy_section: %s: %s\n"), err, isection->name);
+}
+/* Open the temporary bfd created in the same directory as PATH.  */
+
+static bfd *
+cmdline_fopen_temp (const char *path, const char *target,
+		    const char *mode)
+{
+#define template "ldXXXXXX"
+  const char *slash = strrchr (path, '/');
+  char *tmpname;
+  size_t len;
+  int fd;
+
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+  {
+    /* We could have foo/bar\\baz, or foo\\bar, or d:bar.  */
+    char *bslash = strrchr (path, '\\');
+
+    if (slash == NULL || (bslash != NULL && bslash > slash))
+      slash = bslash;
+    if (slash == NULL && path[0] != '\0' && path[1] == ':')
+      slash = path + 1;
+  }
+#endif
+
+  if (slash != (char *) NULL)
+    {
+      len = slash - path;
+      tmpname = (char *) xmalloc (len + sizeof (template) + 2);
+      memcpy (tmpname, path, len);
+
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+      /* If tmpname is "X:", appending a slash will make it a root
+	 directory on drive X, which is NOT the same as the current
+	 directory on drive X.  */
+      if (len == 2 && tmpname[1] == ':')
+	tmpname[len++] = '.';
+#endif
+      tmpname[len++] = '/';
+    }
+  else
+    {
+      tmpname = (char *) xmalloc (sizeof (template));
+      len = 0;
+    }
+
+  memcpy (tmpname + len, template, sizeof (template));
+#undef template
+
+#ifdef HAVE_MKSTEMP
+  fd = mkstemp (tmpname);
+#else
+  tmpname = mktemp (tmpname);
+  if (tmpname == NULL)
+    return NULL;
+  fd = open (tmpname, O_RDWR | O_CREAT | O_EXCL, 0600);
+#endif
+  if (fd == -1)
+    return NULL;
+  return bfd_fopen (tmpname, target, mode, fd);
+}
+
+/* Add the object-only section.  */
+
+static void
+cmdline_add_object_only_section (bfd_byte *contents, size_t size)
+{
+  bfd_vma start;
+  flagword flags;
+  enum bfd_architecture iarch;
+  unsigned int imach;
+  long symcount;
+  long symsize;
+  asymbol **isympp = NULL;
+  asymbol **osympp = NULL;
+  bfd *obfd = NULL, *ibfd;
+  const char *err;
+  struct arg
+    {
+      bfd *obfd;
+      asymbol **isympp;
+      int status;
+    } arg;
+  char **matching;
+  const char *ofilename = NULL;
+  asection *sec;
+
+  ibfd = bfd_openr (output_filename, output_target);
+  if (!ibfd)
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  if (!bfd_check_format_matches (ibfd, bfd_object, &matching))
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  obfd = cmdline_fopen_temp (output_filename, output_target, "w");
+  if (!obfd)
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+  ofilename = bfd_get_filename (obfd);
+
+  if (!bfd_set_format (obfd, bfd_object))
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  /* Copy the start address, flags and architecture of input file to
+     output file.  */
+  flags = bfd_get_file_flags (ibfd);
+  start = bfd_get_start_address (ibfd);
+  iarch = bfd_get_arch (ibfd);
+  imach = bfd_get_mach (ibfd);
+  if (!bfd_set_start_address (obfd, start)
+      || !bfd_set_file_flags (obfd, flags)
+      || !bfd_set_arch_mach (obfd, iarch, imach))
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+	
+  symsize = bfd_get_symtab_upper_bound (ibfd);
+  if (symsize < 0)
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  isympp = (asymbol **) xmalloc (symsize);
+  symcount = bfd_canonicalize_symtab (ibfd, isympp);
+  if (symcount < 0)
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  arg.obfd = obfd;
+  arg.isympp = isympp;
+  arg.status = 0;
+
+  /* BFD mandates that all output sections be created and sizes set before
+     any output is done.  Thus, we traverse all sections multiple times.  */
+  bfd_map_over_sections (ibfd, setup_section, &arg);
+
+  if (arg.status)
+    {
+      err = _("error setting up sections");
+      goto loser;
+    }
+
+  /* Allow the BFD backend to copy any private data it understands
+     from the input section to the output section.  */
+  if (! bfd_copy_private_header_data (ibfd, obfd))
+    {
+      err = _("error copying private header data");
+      goto loser;
+    }
+
+  /* Create the object-only section.  */
+  sec = bfd_make_section_with_flags (obfd,
+				     GNU_OBJECT_ONLY_SECTION_NAME,
+				     (SEC_HAS_CONTENTS
+				      | SEC_READONLY
+				      | SEC_DATA
+				      | SEC_LINKER_CREATED));
+  if (sec == NULL)
+    {
+      err = _("can't create object-only section");
+      goto loser;
+    }
+
+  if (! bfd_set_section_size (obfd, sec, size))
+    {
+      err = _("can't set object-only section size");
+      goto loser;
+    }
+
+  if (ibfd->object_only_section)
+    {
+      /* Filter out the object-only section symbol.  */
+      long src_count = 0, dst_count = 0;
+      asymbol **from, **to;
+
+      osympp = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
+      from = isympp;
+      to = osympp;
+      for (; src_count < symcount; src_count++)
+	{
+	  asymbol *sym = from[src_count];
+	  if (bfd_get_section (sym) != ibfd->object_only_section)
+	    to[dst_count++] = sym;
+	}
+      to[dst_count] = NULL;
+      symcount = dst_count;
+      bfd_set_symtab (obfd, osympp, symcount);
+    }
+  else
+    bfd_set_symtab (obfd, isympp, symcount);
+
+  /* This has to happen after the symbol table has been set.  */
+  bfd_map_over_sections (ibfd, copy_section, &arg);
+
+  if (arg.status)
+    {
+      err = _("error copying sections");
+      goto loser;
+    }
+
+  /* Copy the object-only section to the output.  */
+  if (! bfd_set_section_contents (obfd, sec, contents, 0, size))
+    {
+      err = _("error adding object-only section");
+      goto loser;
+    }
+
+  /* Allow the BFD backend to copy any private data it understands
+     from the input BFD to the output BFD.  This is done last to
+     permit the routine to look at the filtered symbol table, which is
+     important for the ECOFF code at least.  */
+  if (! bfd_copy_private_bfd_data (ibfd, obfd))
+    {
+      err = _("error copying private BFD data");
+      goto loser;
+    }
+
+  if (!bfd_close (obfd))
+    {
+      unlink (ofilename);
+      einfo (_("%P%F: failed to finish output with object-only section\n"));
+    }
+
+  /* Must be freed after bfd_close ().  */
+  free (isympp);
+  if (osympp)
+    free (osympp);
+
+  if (rename (ofilename, output_filename))
+    {
+      unlink (ofilename);
+      einfo (_("%P%F: failed to rename output with object-only section\n"));
+    }
+
+  return;
+
+loser:
+  if (isympp)
+    free (isympp);
+  if (osympp)
+    free (osympp);
+  if (obfd)
+    bfd_close (obfd);
+  if (ofilename)
+    unlink (ofilename);
+  einfo (_("%P%F: failed to add object-only section: %s\n"), err);
+}
+
+/* Emit the final output with object-only section.  */
+
+void
+cmdline_emit_object_only_section (void)
+{
+  const char *saved_output_filename = output_filename;
+  int fd;
+  size_t size, off;
+  bfd_byte *contents;
+  struct stat st;
+
+  /* Get a temporary object-only file.  */
+  output_filename = make_temp_file (".obj-only.o");
+
+  had_output_filename = FALSE;
+  link_info.input_bfds = NULL;
+  link_info.input_bfds_tail = &link_info.input_bfds;
+
+  lang_init (TRUE);
+
+  ld_parse_linker_script ();
+
+  /* Set up the object-only output. */
+  lang_final ();
+
+  /* Open the object-only file for output.  */
+  lang_for_each_statement (ldlang_open_output);
+
+  ldemul_create_output_section_statements ();
+
+  if (!bfd_section_already_linked_table_init ())
+    einfo (_("%P%F: Failed to create hash table\n"));
+
+  /* Call cmdline_on_object_only_archive_list_p to check which member
+     should be loaded.  */
+  whole_archive = TRUE;
+
+  /* Set it to avoid adding more to cmdline lists.  */
+  link_info.emitting_gnu_object_only = TRUE;
+
+  /* Get object-only input files.  */
+  cmdline_get_object_only_input_files ();
+
+  /* Open object-only input files.  */
+  open_input_bfds (statement_list.head, FALSE);
+
+  ldemul_after_open ();
+
+  bfd_section_already_linked_table_free ();
+
+  /* Make sure that we're not mixing architectures.  We call this
+     after all the input files have been opened, but before we do any
+     other processing, so that any operations merge_private_bfd_data
+     does on the output file will be known during the rest of the
+     link.  */
+  lang_check ();
+
+  /* Size up the common data.  */
+  lang_common ();
+
+  /* Update wild statements.  */
+  update_wild_statements (statement_list.head);
+
+  /* Run through the contours of the script and attach input sections
+     to the correct output sections.  */
+  map_input_to_output_sections (statement_list.head, NULL, NULL);
+
+  /* Find any sections not attached explicitly and handle them.  */
+  lang_place_orphans ();
+
+  /* Do anything special before sizing sections.  This is where ELF
+     and other back-ends size dynamic sections.  */
+  ldemul_before_allocation ();
+
+  /* Size up the sections.  */
+  lang_size_sections (NULL, ! RELAXATION_ENABLED);
+
+  /* See if anything special should be done now we know how big
+     everything is.  This is where relaxation is done.  */
+  ldemul_after_allocation ();
+
+  ldemul_finish ();
+
+  /* Make sure that the section addresses make sense.  */
+  if (command_line.check_section_addresses)
+    lang_check_section_addresses ();
+
+  lang_end ();
+  
+  ldwrite ();
+
+  lang_finish ();
+
+  if (! bfd_close (link_info.output_bfd))
+    einfo (_("%P%F:%s: final close failed on object-only output: %E\n"),
+	   output_filename);
+
+  /* Read in the object-only file.  */
+  fd = open (output_filename, O_RDONLY | O_BINARY);
+  if (fd < 0)
+    {
+      bfd_set_error (bfd_error_system_call);
+      einfo (_("%P%F:%s: cannot open object-only output: %E"),
+	     output_filename);
+    }
+
+  /* Get the object-only file size.  */
+  if (fstat (fd, &st) != 0)
+    {
+      bfd_set_error (bfd_error_system_call);
+      einfo (_("%P%F:%s: cannot stat object-only output: %E"),
+	     output_filename);
+    }
+
+  size = st.st_size;
+  off = 0;
+  contents = (bfd_byte *) xmalloc (size);
+  while (off != size)
+    {
+      ssize_t got;
+
+      got = read (fd, contents + off, size - off);
+      if (got < 0)
+	{
+	  bfd_set_error (bfd_error_system_call);
+	  einfo (_("%P%F:%s: read failed on object-only output: %E"),
+		 output_filename);
+	}
+
+      off += got;
+    }
+
+  close (fd);
+
+  /* Remove the temporary object-only file.  */ 
+  unlink (output_filename);
+
+  output_filename = saved_output_filename;
+
+  cmdline_add_object_only_section (contents, size);
+
+  free (contents);
+}
+
+/* Extract the object-only section.  */
+
+static const char *
+cmdline_extract_object_only_section (bfd *abfd)
+{
+  asection *sec = abfd->object_only_section;
+  const char *name;
+  int fd;
+  bfd_byte *memhunk = NULL;
+  size_t off, size;
+  const char *err;
+
+  /* Get a temporary object-only file.  */
+  name = make_temp_file (".obj-only.o");
+
+  /* Open the object-only file.  */
+  fd = open (name, O_WRONLY | O_BINARY);
+  if (fd < 0)
+    {
+      bfd_set_error (bfd_error_system_call);
+      einfo (_("%P%F:%s: cannot open object-only output: %E"), name);
+    }
+
+  if (!bfd_get_full_section_contents (abfd, sec, &memhunk))
+    {
+      err = bfd_errmsg (bfd_get_error ());
+      goto loser;
+    }
+
+  off = 0;
+  size = sec->size;
+  while (off != size)
+    {
+      ssize_t written;
+
+      written = write (fd, memhunk + off, size - off);
+      if (written < 0)
+	{
+	  bfd_set_error (bfd_error_system_call);
+	  einfo (_("%P%F:%s: write failed on object-only output: %E"),
+		 name);
+	}
+
+      off += written;
+    }
+
+  free (memhunk);
+
+  close (fd);
+
+  /* It should be removed after it is done.  */
+  cmdline_list_append (&cmdline_temp_object_only_list,
+		       cmdline_is_file_enum, (void *) name);
+
+  return name;
+
+loser:
+  einfo (_("%P%F: failed to extract object-only section: %s\n"),
+	 err);
+
+  return NULL;
+}
+
+/* Check and handle the object-only section.   */
+
+void
+cmdline_check_object_only_section (bfd *abfd, bfd_boolean lto)
+{
+  const char *filename;
+
+  if (link_info.emitting_gnu_object_only
+      || abfd->format != bfd_object)
+    return;
+
+  if (lto)
+    {
+      /* For LTO link, we only need to extract object-only section
+	 from the mixed object and put it on LTO claimed output.  */
+      switch (abfd->lto_type)
+	{
+	default:
+	  abort ();
+	case lto_mixed_object:
+	  filename = cmdline_extract_object_only_section (abfd);
+	  cmdline_option_insert_claimed_output (filename);
+	  break;
+	case lto_non_ir_object:
+	case lto_ir_object:
+	  break;
+	}
+    }
+  else if (link_info.relocatable)
+    {
+      /* For non-LTO relocatable link, we need to append non-IR object
+	 file and the object file in object-only section to the object
+	 only list.  */
+      switch (abfd->lto_type)
+	{
+	default:
+	  abort ();
+	case lto_mixed_object:
+	  filename = cmdline_extract_object_only_section (abfd);
+	  cmdline_object_only_list_append (cmdline_is_file_enum,
+					  (void *) filename);
+	  break;
+	case lto_non_ir_object:
+	  cmdline_object_only_list_append (cmdline_is_bfd_enum, abfd);
+	  break;
+	case lto_ir_object:
+	  break;
+	}
+    }
+}
+
+/* Remove temporary object-only files.  */
+
+void
+cmdline_remove_object_only_files (void)
+{
+  cmdline_union_type *c = cmdline_temp_object_only_list.head;
+
+  for (; c != NULL; c = c->header.next)
+    switch (c->header.type)
+      {
+      default:
+	abort ();
+      case cmdline_is_file_enum:
+	unlink (c->file.filename);
+	break;
+      }
+}
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 15e5587..b8dfbb6 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;
 
@@ -473,7 +476,7 @@ extern int lang_statement_iteration;
 extern bfd_boolean missing_file;
 
 extern void lang_init
-  (void);
+  (bfd_boolean);
 extern void lang_finish
   (void);
 extern lang_memory_region_type * lang_memory_region_lookup
@@ -650,4 +653,59 @@ 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_file_enum,
+  cmdline_is_enter_group_enum,
+  cmdline_is_leave_group_enum,
+  cmdline_is_bfd_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_file_struct
+{
+  cmdline_header_type header;
+  const char *filename;
+} cmdline_file_type;
+
+typedef struct cmdline_bfd_struct
+{
+  cmdline_header_type header;
+  bfd *abfd;
+} cmdline_bfd_type;
+
+typedef union cmdline_union
+{
+  cmdline_header_type header;
+  cmdline_input_statement_type input_statement;
+  cmdline_file_type file;
+  cmdline_bfd_type abfd;
+} cmdline_union_type;
+
+typedef struct cmdline_list
+{
+  cmdline_union_type *head;
+  cmdline_union_type **tail;
+} cmdline_list_type;
+
+extern void cmdline_option_append (cmdline_enum_type, void *);
+extern void cmdline_option_insert_claimed_output (const char *);
+extern void cmdline_set_next_claimed_output (bfd *);
+extern void cmdline_claimed_archive_member_list_append (bfd *);
+extern void cmdline_emit_object_only_section (void);
+extern void cmdline_check_object_only_section (bfd *, bfd_boolean);
+extern void cmdline_remove_object_only_files (void);
+
 #endif
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 5530dca..cc852f2 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -213,6 +213,9 @@ main (int argc, char **argv)
 
   xatexit (remove_output);
 
+  /* Remove temporary object-only files.  */
+  xatexit (cmdline_remove_object_only_files);
+
   /* Set up the sysroot directory.  */
   ld_sysroot = get_sysroot (argc, argv);
   if (*ld_sysroot)
@@ -289,7 +292,7 @@ main (int argc, char **argv)
   default_target = ldemul_choose_target (argc, argv);
   config.maxpagesize = bfd_emul_get_maxpagesize (default_target);
   config.commonpagesize = bfd_emul_get_commonpagesize (default_target);
-  lang_init ();
+  lang_init (FALSE);
   ldemul_before_parse ();
   lang_has_input_file = FALSE;
   parse_args (argc, argv);
@@ -365,34 +368,7 @@ main (int argc, char **argv)
 	link_info.discard = discard_all;
     }
 
-  /* If we have not already opened and parsed a linker script,
-     try the default script from command line first.  */
-  if (saved_script_handle == NULL
-      && command_line.default_script != NULL)
-    {
-      ldfile_open_command_file (command_line.default_script);
-      parser_input = input_script;
-      yyparse ();
-    }
-
-  /* If we have not already opened and parsed a linker script
-     read the emulation's appropriate default script.  */
-  if (saved_script_handle == NULL)
-    {
-      int isfile;
-      char *s = ldemul_get_script (&isfile);
-
-      if (isfile)
-	ldfile_open_default_command_file (s);
-      else
-	{
-	  lex_string = s;
-	  lex_redirect (s);
-	}
-      parser_input = input_script;
-      yyparse ();
-      lex_string = NULL;
-    }
+  ld_parse_linker_script ();
 
   if (trace_file_tries)
     {
@@ -496,6 +472,8 @@ main (int argc, char **argv)
       if (! bfd_close (link_info.output_bfd))
 	einfo (_("%F%B: final close failed: %E\n"), link_info.output_bfd);
 
+      link_info.output_bfd = NULL;
+
       /* If the --force-exe-suffix is enabled, and we're making an
 	 executable file and it doesn't end in .exe, copy it to one
 	 which does.  */
@@ -542,6 +520,9 @@ main (int argc, char **argv)
 	}
     }
 
+  if (link_info.emit_gnu_object_only)
+    cmdline_emit_object_only_section ();
+
   END_PROGRESS (program_name);
 
   if (config.stats)
@@ -791,6 +772,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 +784,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,11 +822,17 @@ 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);
+	      cmdline_check_object_only_section (abfd, TRUE);
 	      /* Substitute the dummy BFD.  */
 	      input->the_bfd = file.handle;
 	      input->claimed = TRUE;
 	      bfd_make_readable (input->the_bfd);
+	      input->the_bfd->flags |= BFD_PLUGIN_DUMMY;
 	      *subsbfd = input->the_bfd;
+	      cmdline_claimed_archive_member_list_append (abfd);
 	    }
 	  else
 	    {
@@ -841,9 +840,14 @@ 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;
 	}
     }
+  else
 #endif /* ENABLE_PLUGINS */
+    cmdline_check_object_only_section (input->the_bfd, FALSE);
 
   ldlang_add_file (input);
 
@@ -891,16 +895,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);
 	}
@@ -1477,3 +1481,38 @@ notice (struct bfd_link_info *info,
 
   return TRUE;
 }
+
+/* Parse the linker script.  */
+
+void
+ld_parse_linker_script (void)
+{
+  /* If we have not already opened and parsed a linker script,
+     try the default script from command line first.  */
+  if (saved_script_handle == NULL
+      && command_line.default_script != NULL)
+    {
+      ldfile_open_command_file (command_line.default_script);
+      parser_input = input_script;
+      yyparse ();
+    }
+
+  /* If we have not already opened and parsed a linker script
+     read the emulation's appropriate default script.  */
+  if (saved_script_handle == NULL)
+    {
+      int isfile;
+      char *s = ldemul_get_script (&isfile);
+
+      if (isfile)
+	ldfile_open_default_command_file (s);
+      else
+	{
+	  lex_string = s;
+	  lex_redirect (s);
+	}
+      parser_input = input_script;
+      yyparse ();
+      lex_string = NULL;
+    }
+}
diff --git a/ld/ldmain.h b/ld/ldmain.h
index 57ce8d7..22f9885 100644
--- a/ld/ldmain.h
+++ b/ld/ldmain.h
@@ -46,4 +46,6 @@ extern void add_ysym (const char *);
 extern void add_wrap (const char *);
 extern void add_keepsyms_file (const char *);
 
+extern void ld_parse_linker_script (void);
+
 #endif
diff --git a/ld/lexsup.c b/ld/lexsup.c
index acb63fb..30e36db 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_option_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_option_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_option_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_option_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_option_append (cmdline_is_leave_group_enum, NULL);
 	  ingroup--;
 	  break;
 
diff --git a/ld/plugin.c b/ld/plugin.c
index e4943c2..771a39f 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -41,10 +41,6 @@
    plugin.  */
 #define IRONLY_SUFFIX		".ironly\004"
 
-/* This is sizeof an array of chars, not sizeof a const char *.  We
-   also have to avoid inadvertently counting the trailing NUL.  */
-#define IRONLY_SUFFIX_LEN	(sizeof (IRONLY_SUFFIX) - 1)
-
 /* Stores a single argument passed to a plugin.  */
 typedef struct plugin_arg
 {
@@ -214,6 +210,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;
@@ -237,6 +244,7 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
 		     srctemplate);
   bfd_set_arch_info (abfd, bfd_get_arch_info (srctemplate));
   bfd_make_writable (abfd);
+  abfd->flags |= BFD_PLUGIN_DUMMY;
   /* Create a minimal set of sections to own the symbols.  */
   sec = bfd_make_section_old_way (abfd, ".text");
   bfd_set_section_flags (abfd, sec,
@@ -251,14 +259,9 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
 static bfd_boolean
 is_ir_dummy_bfd (const bfd *abfd)
 {
-  size_t namlen;
-
   if (abfd == NULL)
     return FALSE;
-  namlen = strlen (abfd->filename);
-  if (namlen < IRONLY_SUFFIX_LEN)
-    return FALSE;
-  return !strcmp (abfd->filename + namlen - IRONLY_SUFFIX_LEN, IRONLY_SUFFIX);
+  return (abfd->flags & BFD_PLUGIN_DUMMY) != 0;
 }
 
 /* Helpers to convert between BFD and GOLD symbol formats.  */
@@ -532,9 +535,9 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
 	}
 
       /* Was originally def, weakdef, or common, but has been pre-empted.  */
-      syms[n].resolution = is_ir_dummy_bfd (owner_sec->owner)
-	? LDPR_PREEMPTED_IR
-	: LDPR_PREEMPTED_REG;
+      syms[n].resolution = (is_ir_dummy_bfd (owner_sec->owner)
+			    ? LDPR_PREEMPTED_IR
+			    : LDPR_PREEMPTED_REG);
     }
   return LDPS_OK;
 }
@@ -544,9 +547,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_option_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
@@ -555,9 +556,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_option_insert_claimed_output (xstrdup (pathname));
   return LDPS_OK;
 }
 
diff --git a/ld/scripttempl/armbpabi.sc b/ld/scripttempl/armbpabi.sc
index 3049f36..4072584 100644
--- a/ld/scripttempl/armbpabi.sc
+++ b/ld/scripttempl/armbpabi.sc
@@ -30,7 +30,7 @@ INTERP=".interp       0 : { *(.interp) }"
 PLT=".plt          ${RELOCATING-0} : { *(.plt) }"
 RODATA=".rodata       ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
 DATARELRO=".data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink)  *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }"
 if test -z "${NO_SMALL_DATA}"; then
   SBSS=".sbss         ${RELOCATING-0} :
   {
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index f020a66..14cd287 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -149,7 +149,7 @@ RELA_IPLT=".rela.iplt    ${RELOCATING-0} :
 DYNAMIC=".dynamic      ${RELOCATING-0} : { *(.dynamic) }"
 RODATA=".${RODATA_NAME}       ${RELOCATING-0} : { *(.${RODATA_NAME}${RELOCATING+ .${RODATA_NAME}.* .gnu.linkonce.r.*}) }"
 DATARELRO=".data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }"
 if test -z "${NO_SMALL_DATA}"; then
   SBSS=".${SBSS_NAME}         ${RELOCATING-0} :
   {
diff --git a/ld/scripttempl/elf32sh-symbian.sc b/ld/scripttempl/elf32sh-symbian.sc
index b8fbcbc..c629c89 100644
--- a/ld/scripttempl/elf32sh-symbian.sc
+++ b/ld/scripttempl/elf32sh-symbian.sc
@@ -83,7 +83,7 @@ fi
     PLT=".plt            : { *(.plt) } :dynamic :dyn"
 DYNAMIC=".dynamic        : { *(.dynamic) } :dynamic :dyn"
  RODATA=".rodata    ALIGN(4) : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.directive)  *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.directive)  *(.gnu.lto_*) *(.gnu_object_only) }"
 test -z "$GOT" && GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.got) } :dynamic :dyn"
 INIT_ARRAY=".init_array   ${RELOCATING-0} :
   {
diff --git a/ld/scripttempl/elf64hppa.sc b/ld/scripttempl/elf64hppa.sc
index 5cbf212..c669996 100644
--- a/ld/scripttempl/elf64hppa.sc
+++ b/ld/scripttempl/elf64hppa.sc
@@ -127,7 +127,7 @@ fi
 DYNAMIC=".dynamic      ${RELOCATING-0} : { *(.dynamic) }"
 RODATA=".rodata       ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
 DATARELRO=".data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink)  *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }"
 if test -z "${NO_SMALL_DATA}"; then
   SBSS=".sbss         ${RELOCATING-0} :
   {
diff --git a/ld/scripttempl/elfxtensa.sc b/ld/scripttempl/elfxtensa.sc
index 7d5d5ee..3253e20 100644
--- a/ld/scripttempl/elfxtensa.sc
+++ b/ld/scripttempl/elfxtensa.sc
@@ -140,7 +140,7 @@ fi
 DYNAMIC=".dynamic      ${RELOCATING-0} : { *(.dynamic) }"
 RODATA=".rodata       ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
 DATARELRO=".data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink)  *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }"
 INIT_LIT=".init.literal 0 : { *(.init.literal)	}"
 INIT=".init         0 : { *(.init)		}"
 FINI_LIT=".fini.literal 0 : { *(.fini.literal)	}"
diff --git a/ld/scripttempl/mep.sc b/ld/scripttempl/mep.sc
index 82a2b25..3ecf144 100644
--- a/ld/scripttempl/mep.sc
+++ b/ld/scripttempl/mep.sc
@@ -114,7 +114,7 @@ fi
 DYNAMIC=".dynamic      ${RELOCATING-0} : { *(.dynamic) }"
 RODATA=".rodata       ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
 DATARELRO=".data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }"
-DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink)  *(.gnu.lto_*) }"
+DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }"
 if test -z "${NO_SMALL_DATA}"; then
   SBSS=".sbss         ${RELOCATING-0} :
   {
diff --git a/ld/scripttempl/pe.sc b/ld/scripttempl/pe.sc
index 7d52cc5..2e2f2f2 100644
--- a/ld/scripttempl/pe.sc
+++ b/ld/scripttempl/pe.sc
@@ -144,6 +144,7 @@ SECTIONS
     *(.drectve)
     ${RELOCATING+ *(.note.GNU-stack)}
     ${RELOCATING+ *(.gnu.lto_*)}
+    ${RELOCATING+ *(.gnu_object_only)}
   }
 
   .idata ${RELOCATING+BLOCK(__section_alignment__)} :
diff --git a/ld/scripttempl/pep.sc b/ld/scripttempl/pep.sc
index 8fa5f05..f69a5b8 100644
--- a/ld/scripttempl/pep.sc
+++ b/ld/scripttempl/pep.sc
@@ -150,6 +150,7 @@ SECTIONS
     *(.drectve)
     ${RELOCATING+ *(.note.GNU-stack)}
     ${RELOCATING+ *(.gnu.lto_*)}
+    ${RELOCATING+ *(.gnu_object_only)}
   }
 
   .idata ${RELOCATING+BLOCK(__section_alignment__)} :
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
 }
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]