public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* PATCH: Support mixing COMDAT and linkonce
@ 2004-05-14 21:07 H. J. Lu
  2004-05-15  2:13 ` Alan Modra
  0 siblings, 1 reply; 10+ messages in thread
From: H. J. Lu @ 2004-05-14 21:07 UTC (permalink / raw)
  To: binutils

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

When there are mixed COMDAT and linkonce inputs, linker doesn't handle
them gracefully:

http://sources.redhat.com/bugzilla/show_bug.cgi?id=161

This patch tries to fix it.


H.J.

[-- Attachment #2: bfd-comdat-linkonce-1.patch --]
[-- Type: text/plain, Size: 4494 bytes --]

2004-05-14  H.J. Lu  <hongjiu.lu@intel.com>

	* elflink.c (find_group_section): New function.
	(elf_merge_comdat_linkonce): Likewise. Combine .gnu.linkonce
	section with a comdat group.
	(_bfd_elf_merge_symbol): Use it.

--- bfd/elflink.c.linkonce	2004-05-11 13:34:11.000000000 -0700
+++ bfd/elflink.c	2004-05-14 13:48:44.000000000 -0700
@@ -674,6 +674,106 @@ _bfd_elf_link_renumber_dynsyms (bfd *out
   return elf_hash_table (info)->dynsymcount = dynsymcount;
 }
 
+/* Rerurn TRUE if SEC is the group section which contains the section
+   INF.  */
+
+static bfd_boolean
+find_group_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+		    void *inf)
+{
+  asection *elt;
+
+  if (elf_section_type (sec) != SHT_GROUP)
+    return FALSE;
+
+  elt = elf_next_in_group (sec);
+  while (elt != NULL)
+    {
+      if (elt == (asection *) inf)
+	return TRUE;
+      elt = elf_next_in_group (sec);
+    }
+
+  return FALSE;
+}
+
+/* Combine .gnu.linkonce section with a comdat group.  */
+
+static void
+elf_merge_comdat_linkonce (bfd *newbfd, bfd *oldbfd,
+			   asection *newsec, asection *oldsec,
+			   bfd_boolean *skip)
+{
+  /* Skip symbols in discarded section.  */
+  if (elf_discarded_section (newsec))
+    {
+      *skip = TRUE;
+      return;
+    }
+
+  /* Make sure that we have a perfect match.  */
+  if ((newsec->flags & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+       != (oldsec->flags & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+      && (((newsec->flags & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	   && elf_next_in_group (oldsec) == oldsec)
+	  || ((oldsec->flags
+	       & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	      && elf_next_in_group (newsec) == newsec))
+      && newsec->_raw_size == oldsec->_raw_size
+      && elf_section_type (newsec) == elf_section_type (oldsec)
+      && ((elf_section_flags (newsec) & ~SHF_GROUP)
+	  == (elf_section_flags (oldsec) & ~SHF_GROUP)))
+    {
+      bfd_byte *newcontents;
+      bfd_byte *oldcontents;
+      bfd_boolean comdat = elf_section_flags (newsec) & SHF_GROUP;
+      asection *group;
+
+      /* Find the group section.  */
+      group = bfd_sections_find_if (comdat ? newbfd : oldbfd,
+				    find_group_section,
+				    (void *) (comdat ? newsec : oldsec));
+      if (!group)
+	{
+	  (*_bfd_error_handler)
+	    (_("%s: warning: group member `%s' [%d] doesn't belong to any group"),
+	     bfd_archive_filename (comdat ? newbfd : oldbfd),
+	     comdat ? newsec->name : oldsec->name,
+	     (comdat ? newsec->index : oldsec->index) + 1);
+	  return;
+	}
+
+      /* Check if it is a comdat group.  */
+      if ((group->flags
+	   & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	  != (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	return;
+
+      newcontents = bfd_malloc (newsec->_raw_size);
+      oldcontents = bfd_malloc (newsec->_raw_size);
+      if (newcontents
+	  && oldcontents
+	  && bfd_get_section_contents (newbfd, newsec, newcontents,
+				       0, newsec->_raw_size)
+	  && bfd_get_section_contents (oldbfd, oldsec, oldcontents,
+				       0, newsec->_raw_size)
+	  && memcmp (newcontents, oldcontents, newsec->_raw_size) == 0)
+	{
+	  newsec->output_section = bfd_abs_section_ptr;
+	  *skip = TRUE;
+
+	  /* Discard the comdat group section if needed. */
+	  if (comdat)
+	    group->output_section = bfd_abs_section_ptr;
+	}
+
+      if (newcontents)
+	free (newcontents);
+      if (oldcontents)
+	free (oldcontents);
+    }
+}
+
 /* This function is called when we want to define a new symbol.  It
    handles the various cases which arise when we find a definition in
    a dynamic object, or when there is already a definition in a
@@ -1163,6 +1263,22 @@ _bfd_elf_merge_symbol (bfd *abfd,
 	h->verinfo.vertree = NULL;
     }
 
+  /* Handle the special case of a new non-weak definition in a
+     relocatable file merging with an old non-weak definition from
+     another relocatable file where only one of them is in a
+     .gnu.linkonce section and the other is in a section group by
+     itself. In this case, we discard the new definition along with
+     its section if 2 sections are the same.  */
+  if (ELF_ST_TYPE (sym->st_info) == h->type
+      && newdef
+      && !newdyn
+      && !newweak
+      && olddef
+      && !olddyn
+      && !oldweak)
+    elf_merge_comdat_linkonce (abfd, oldbfd, sec, h->root.u.def.section,
+			       skip);
+
   if (flip != NULL)
     {
       /* Handle the case where we had a versioned symbol in a dynamic

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-14 21:07 PATCH: Support mixing COMDAT and linkonce H. J. Lu
@ 2004-05-15  2:13 ` Alan Modra
  2004-05-15 15:44   ` H. J. Lu
  2004-05-17 21:24   ` H. J. Lu
  0 siblings, 2 replies; 10+ messages in thread
From: Alan Modra @ 2004-05-15  2:13 UTC (permalink / raw)
  To: H. J. Lu; +Cc: binutils

On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> them gracefully:

Ewww.  Should we even try?  I understand that such a patch might be
useful while gcc is emitting both comdat and linkonce, but once you've
completed the change to comdat it shouldn't be necessary.  Also, I'm not
really happy with where you have added this code.  At least, it is the
wrong place to be discarding duplicate sections.  That ought to happen
in ldlang.c:section_already_linked.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-15  2:13 ` Alan Modra
@ 2004-05-15 15:44   ` H. J. Lu
  2004-05-17 21:24   ` H. J. Lu
  1 sibling, 0 replies; 10+ messages in thread
From: H. J. Lu @ 2004-05-15 15:44 UTC (permalink / raw)
  To: binutils

On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > them gracefully:
> 
> Ewww.  Should we even try?  I understand that such a patch might be
> useful while gcc is emitting both comdat and linkonce, but once you've

We can't guarantee everyone will use the same gcc.

> completed the change to comdat it shouldn't be necessary.  Also, I'm not
> really happy with where you have added this code.  At least, it is the
> wrong place to be discarding duplicate sections.  That ought to happen
> in ldlang.c:section_already_linked.

I will see what I can do. I want to check if 2 comdat/linkonce sections 
define the same set of external symbols instead of comparing sizes.


H.J.

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-15  2:13 ` Alan Modra
  2004-05-15 15:44   ` H. J. Lu
@ 2004-05-17 21:24   ` H. J. Lu
  2004-05-18  1:06     ` H. J. Lu
  1 sibling, 1 reply; 10+ messages in thread
From: H. J. Lu @ 2004-05-17 21:24 UTC (permalink / raw)
  To: binutils

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

On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > them gracefully:
> 
> Ewww.  Should we even try?  I understand that such a patch might be
> useful while gcc is emitting both comdat and linkonce, but once you've
> completed the change to comdat it shouldn't be necessary.  Also, I'm not
> really happy with where you have added this code.  At least, it is the
> wrong place to be discarding duplicate sections.  That ought to happen
> in ldlang.c:section_already_linked.
> 

This patch implements it.


H.J.

[-- Attachment #2: bfd-comdat-linkonce-2.patch --]
[-- Type: text/plain, Size: 36013 bytes --]

bfd/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* aout-adobe.c (aout_32_bfd_match_symbols_in_sections): Defined.
	* aout-target.h (MY_bfd_match_symbols_in_sections): Likewise.
	* aout-tic30.c (MY_bfd_match_symbols_in_sections): Likewise.
	* binary.c (binary_bfd_match_symbols_in_sections): Likewise.
	* bout.c (b_out_bfd_match_symbols_in_sections): Likewise.
	* coff-alpha.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coff-mips.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coffcode.h (coff_bfd_match_symbols_in_sections): Likewise.
	* i386msdos.c (msdos_bfd_match_symbols_in_sections): Likewise.
	* i386os9k.c (os9k_bfd_match_symbols_in_sections): Likewise.
	* ieee.c (ieee_bfd_match_symbols_in_sections): Likewise.
	* ihex.c (ihex_bfd_match_symbols_in_sections): Likewise.
	* mach-o.c (bfd_mach_o_bfd_match_symbols_in_sections): Likewise.
	* mmo.c (mmo_bfd_discard_group): Likewise.
	* nlm-target.h (nlm_bfd_match_symbols_in_sections): Likewise.
	* oasys.c (oasys_bfd_match_symbols_in_sections): Likewise.
	* pef.c (bfd_pef_bfd_match_symbols_in_sections): Likewise.
	* ppcboot.c (ppcboot_bfd_match_symbols_in_sections): Likewise.
	* som.c (som_bfd_discard_group): Likewise.
	* srec.c (srec_bfd_match_symbols_in_sections): Likewise.
	* tekhex.c (tekhex_bfd_match_symbols_in_sections): Likewise.
	* versados.c (versados_bfd_match_symbols_in_sections): Likewise.
	* vms.c (vms_bfd_match_symbols_in_sections): Likewise.
	* coff-target.h (_bfd_xcoff_bfd_match_symbols_in_sections): Likewise.
	* xsym.c (bfd_sym_bfd_match_symbols_in_sections): Likewise.

	* bfd.c (bfd_match_symbols_in_sections): New.

	* coff-rs6000.c (rs6000coff_vec): Add
	_bfd_nolink_bfd_match_symbols_in_sections,
	(pmac_xcoff_vec): Likewise.
	* coff64-rs6000.c (rs6000coff64_vec): Likewise.
	(aix5coff64_vec): Likewise.

	* elf-bfd.h (bfd_elf_match_symbols_in_sections): New prototype.
	(_bfd_elf_setup_group_pointers): Likewise.

	* elf.c (_bfd_elf_setup_group_pointers): New function.
	(elf_sort_elf_symbol): Likewise.
	(elf_sym_name_compare): Likewise.
	(bfd_elf_match_symbols_in_sections): Likewise.

	* elfcode.h (elf_object_p): Call _bfd_elf_setup_group_pointers.

	* elfxx-target.h (bfd_elfNN_bfd_match_symbols_in_sections): Defined.

	* libbfd-in.h (_bfd_nolink_bfd_match_symbols_in_sections): Defined/

	* section.c (bfd_section): Add group, a pointer to section group.

	* section (STD_SECTION): Initialize group to NULL.
	* ecoff.c (bfd_debug_section): Likewise.

	* targets.c (bfd_target): Add _bfd_match_symbols_in_sections.
	(BFD_JUMP_TABLE_LINK): Updated.

	* bfd-in2.h: Regenerated.
	* libbfd.h: Regenerated.

ld/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* ldlang.c (already_linked_section): New structure.
	(try_match_symbols_in_sections): New function.
	(section_already_linked): Check if a member of a COMDAT group
	matches a .gnu.linkonce section.

--- binutils/bfd/aout-adobe.c.linkonce	2004-04-30 08:25:52.000000000 -0700
+++ binutils/bfd/aout-adobe.c	2004-05-17 13:27:55.000000000 -0700
@@ -519,6 +519,8 @@ aout_adobe_sizeof_headers (ignore_abfd, 
 #define aout_32_bfd_merge_sections	bfd_generic_merge_sections
 #define aout_32_bfd_is_group_section	bfd_generic_is_group_section
 #define aout_32_bfd_discard_group	bfd_generic_discard_group
+#define aout_32_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define aout_32_bfd_link_hash_table_create \
   _bfd_generic_link_hash_table_create
 #define aout_32_bfd_link_hash_table_free \
--- binutils/bfd/aout-target.h.linkonce	2004-04-30 08:25:53.000000000 -0700
+++ binutils/bfd/aout-target.h	2004-05-17 09:41:08.000000000 -0700
@@ -519,6 +519,10 @@ MY_bfd_final_link (abfd, info)
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup NAME(aout,reloc_type_lookup)
 #endif
--- binutils/bfd/aout-tic30.c.linkonce	2004-04-30 08:25:54.000000000 -0700
+++ binutils/bfd/aout-tic30.c	2004-05-17 09:41:32.000000000 -0700
@@ -976,6 +976,10 @@ tic30_aout_set_arch_mach (abfd, arch, ma
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup tic30_aout_reloc_type_lookup
 #endif
--- binutils/bfd/bfd.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/bfd/bfd.c	2004-05-17 09:40:43.000000000 -0700
@@ -1089,6 +1089,9 @@ DESCRIPTION
 .#define bfd_discard_group(abfd, sec) \
 .	BFD_SEND (abfd, _bfd_discard_group, (abfd, sec))
 .
+.#define bfd_match_symbols_in_sections(abfd, sec1, sec2) \
+.	BFD_SEND (abfd, _bfd_match_symbols_in_sections, (sec1, sec2))
+.
 .#define bfd_link_hash_table_create(abfd) \
 .	BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd))
 .
--- binutils/bfd/binary.c.linkonce	2004-04-30 08:26:01.000000000 -0700
+++ binutils/bfd/binary.c	2004-05-17 09:41:47.000000000 -0700
@@ -341,6 +341,8 @@ binary_sizeof_headers (abfd, exec)
 #define binary_bfd_merge_sections bfd_generic_merge_sections
 #define binary_bfd_is_group_section bfd_generic_is_group_section
 #define binary_bfd_discard_group bfd_generic_discard_group
+#define binary_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define binary_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define binary_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define binary_bfd_link_just_syms _bfd_generic_link_just_syms
--- binutils/bfd/bout.c.linkonce	2004-04-30 08:26:02.000000000 -0700
+++ binutils/bfd/bout.c	2004-05-17 13:28:43.000000000 -0700
@@ -1489,6 +1489,8 @@ b_out_bfd_get_relocated_section_contents
 #define b_out_bfd_merge_sections  bfd_generic_merge_sections
 #define b_out_bfd_is_group_section bfd_generic_is_group_section
 #define b_out_bfd_discard_group bfd_generic_discard_group
+#define b_out_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 #define aout_32_get_section_contents_in_window \
   _bfd_generic_get_section_contents_in_window
--- binutils/bfd/coff-alpha.c.linkonce	2004-04-30 08:26:04.000000000 -0700
+++ binutils/bfd/coff-alpha.c	2004-05-17 09:41:52.000000000 -0700
@@ -2361,6 +2361,8 @@ static const struct ecoff_backend_data a
 #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target ecoffalpha_little_vec =
 {
--- binutils/bfd/coff-mips.c.linkonce	2004-04-30 08:26:06.000000000 -0700
+++ binutils/bfd/coff-mips.c	2004-05-17 09:42:02.000000000 -0700
@@ -1395,6 +1395,8 @@ static const struct ecoff_backend_data m
 
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 extern const bfd_target ecoff_big_vec;
 
--- binutils/bfd/coff-rs6000.c.linkonce	2004-04-30 08:26:10.000000000 -0700
+++ binutils/bfd/coff-rs6000.c	2004-05-17 09:42:06.000000000 -0700
@@ -4198,6 +4198,7 @@ const bfd_target rs6000coff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -4442,6 +4443,7 @@ const bfd_target pmac_xcoff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coff64-rs6000.c.linkonce	2004-04-30 08:26:13.000000000 -0700
+++ binutils/bfd/coff64-rs6000.c	2004-05-17 09:42:18.000000000 -0700
@@ -2739,6 +2739,7 @@ const bfd_target rs6000coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -2984,6 +2985,7 @@ const bfd_target aix5coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coffcode.h.linkonce	2004-05-07 13:21:00.000000000 -0700
+++ binutils/bfd/coffcode.h	2004-05-17 09:42:24.000000000 -0700
@@ -5574,6 +5574,11 @@ static const bfd_coff_backend_data ticof
 #define coff_bfd_discard_group		    bfd_generic_discard_group
 #endif
 
+#ifndef coff_bfd_match_symbols_in_sections
+#define coff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
+
 #define CREATE_BIG_COFF_TARGET_VEC(VAR, NAME, EXTRA_O_FLAGS, EXTRA_S_FLAGS, UNDER, ALTERNATIVE, SWAP_TABLE)	\
 const bfd_target VAR =							\
 {									\
--- binutils/bfd/ecoff.c.linkonce	2004-04-26 21:11:29.000000000 -0700
+++ binutils/bfd/ecoff.c	2004-05-17 13:30:52.000000000 -0700
@@ -95,8 +95,8 @@ static asection bfd_debug_section =
      NULL,       NULL,        0,           0,       0,
   /* line_filepos, userdata, contents, lineno, lineno_count,       */
      0,            NULL,     NULL,     NULL,   0,
-  /* entsize, comdat, kept_section, moving_line_filepos,           */
-     0,       NULL,   NULL,         0,
+  /* entsize, comdat, group, kept_section, moving_line_filepos,    */
+     0,       NULL,   NULL,  NULL,         0,
   /* target_index, used_by_bfd, constructor_chain, owner,          */
      0,            NULL,        NULL,              NULL,
   /* symbol,                                                       */
--- binutils/bfd/elf-bfd.h.linkonce	2004-05-11 13:33:49.000000000 -0700
+++ binutils/bfd/elf-bfd.h	2004-05-17 12:32:04.000000000 -0700
@@ -1375,6 +1375,8 @@ extern bfd_boolean bfd_elf_is_group_sect
   (bfd *, const struct bfd_section *);
 extern bfd_boolean bfd_elf_discard_group
   (bfd *, struct bfd_section *);
+extern bfd_boolean bfd_elf_match_symbols_in_sections
+  (struct bfd_section *, struct bfd_section *);
 extern void bfd_elf_set_group_contents
   (bfd *, asection *, void *);
 extern void _bfd_elf_link_just_syms
@@ -1554,6 +1556,9 @@ extern bfd_boolean _bfd_elf_dynamic_symb
 extern bfd_boolean _bfd_elf_symbol_refs_local_p
   (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
 
+extern void _bfd_elf_setup_group_pointers
+  (bfd *);
+
 extern const bfd_target *bfd_elf32_object_p
   (bfd *);
 extern const bfd_target *bfd_elf32_core_file_p
--- binutils/bfd/elf.c.linkonce	2004-05-11 13:33:56.000000000 -0700
+++ binutils/bfd/elf.c	2004-05-17 14:14:58.000000000 -0700
@@ -612,6 +612,26 @@ setup_group (bfd *abfd, Elf_Internal_Shd
   return TRUE;
 }
 
+void
+_bfd_elf_setup_group_pointers (bfd *abfd)
+{
+  unsigned int i;
+  unsigned int num_group = elf_tdata (abfd)->num_group;
+
+  if (num_group == (unsigned) -1)
+    return;
+
+  for (i = 0; i < num_group; i++)
+    {
+      Elf_Internal_Shdr *shdr = elf_tdata (abfd)->group_sect_ptr[i];
+      Elf_Internal_Group *idx = (Elf_Internal_Group *) shdr->contents;
+      unsigned int n_elt = shdr->sh_size / 4;
+
+      while (--n_elt != 0)
+	(++idx)->shdr->bfd_section->group = shdr->bfd_section;
+    }
+}
+
 bfd_boolean
 bfd_elf_is_group_section (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec)
 {
@@ -7678,3 +7698,240 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd
 
   return n;
 }
+
+/* Sort symbol by binding and section. We want to put global
+   symbols sorted by section at the beginning.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1;
+  const Elf_Internal_Sym *s2;
+
+  /* Make sure local or undefined symbols are at the end.  */
+  s1 = (const Elf_Internal_Sym *) arg1;
+  if (s1->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s1->st_info) == STB_LOCAL)
+    return 1;
+  s2 = (const Elf_Internal_Sym *) arg2;
+  if (s2->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s2->st_info) == STB_LOCAL)
+    return -1;
+
+  /* Grouped by section index.  */
+  return s1->st_shndx - s2->st_shndx;
+}
+
+struct elf_symbol
+{
+  Elf_Internal_Sym *sym;
+  const char *name;
+};
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+  const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+  const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+  return strcmp (s1->name, s2->name);
+}
+
+/* Check if 2 sections export the same set of symbols.  */
+
+bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+{
+  bfd *bfd1, *bfd2;
+  const struct elf_backend_data *bed1, *bed2;
+  Elf_Internal_Shdr *hdr1, *hdr2;
+  bfd_size_type symcount1, symcount2;
+  bfd_size_type extsymcount1, extsymcount2;
+  bfd_size_type extsymoff1, extsymoff2;
+  Elf_Internal_Sym *isymbuf1, *isymbuf2;
+  Elf_Internal_Sym *isymstart1 = NULL, *isymstart2 = NULL, *isym;
+  Elf_Internal_Sym *isymend;
+  struct elf_symbol *symp, *symtable1 = NULL, *symtable2 = NULL;
+  bfd_size_type count1, count2, i;
+  int shndx1, shndx2;
+  bfd_boolean result;
+
+  bfd1 = sec1->owner;
+  bfd2 = sec2->owner;
+
+  /* Both sections have to be in ELF.  */
+  if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+      || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+    return FALSE;
+
+  /* One section has to be in a COMDAT group with only one section and
+     the other one has to be a .gnu.linkonce section.  */
+  if (!(elf_section_type (sec1) == elf_section_type (sec2)
+	&& ((elf_section_flags (sec1) & ~SHF_GROUP)
+	    == (elf_section_flags (sec2) & ~SHF_GROUP))
+	&& ((sec1->group != NULL
+	     && ((sec1->group->flags & (SEC_LINK_ONCE
+					| SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	     && elf_next_in_group (sec1) == sec1
+	     && sec2->group == NULL
+	     && ((sec2->flags & (SEC_LINK_ONCE
+				 | SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD)))
+	    || (sec1->group == NULL
+		&& sec2->group != NULL
+		&& ((sec2->group->flags & (SEC_LINK_ONCE
+					   | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+		&& elf_next_in_group (sec2) == sec2
+		&& ((sec1->flags & (SEC_LINK_ONCE
+				    | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))))))
+    return FALSE;
+
+  shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+  shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+  if (shndx1 == -1 || shndx2 == -1)
+    return FALSE;
+
+  bed1 = get_elf_backend_data (bfd1);
+  bed2 = get_elf_backend_data (bfd2);
+  hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+  symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+  hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+  symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+  if (elf_bad_symtab (bfd1))
+    {
+      extsymcount1 = symcount1;
+      extsymoff1 = 0;
+    }
+  else
+    {
+      extsymcount1 = symcount1 - hdr1->sh_info;
+      extsymoff1 = hdr1->sh_info;
+    }
+
+  if (elf_bad_symtab (bfd2))
+    {
+      extsymcount2 = symcount2;
+      extsymoff2 = 0;
+    }
+  else
+    {
+      extsymcount2 = symcount2 - hdr2->sh_info;
+      extsymoff2 = hdr2->sh_info;
+    }
+
+  if (extsymcount1 == 0 || extsymcount2 == 0)
+    return FALSE;
+
+  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, extsymcount1,
+				     extsymoff1, NULL, NULL, NULL);
+  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, extsymcount2,
+				     extsymoff2, NULL, NULL, NULL);
+  if (isymbuf1 == NULL || isymbuf2 == NULL)
+    return FALSE;
+
+  /* Sort symbols by binding and section. Global definitions are at
+     the beginning.  */
+  qsort (isymbuf1, extsymcount1, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+  qsort (isymbuf2, extsymcount2, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+
+  /* Count global symbols defined in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + extsymcount1;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx1)
+	{
+	  if (count1 == 0)
+	    isymstart1 = isym;
+	  count1++;
+	}
+
+      if (count1 && isym->st_shndx != (unsigned int) shndx1)
+	break;
+    }
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + extsymcount2;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx2)
+	{
+	  if (count2 == 0)
+	    isymstart2 = isym;
+	  count2++;
+	}
+
+      if (count2 && isym->st_shndx != (unsigned int) shndx2)
+	break;
+    }
+
+  if (count1 == 0 && count2 == 0)
+    {
+      result = TRUE;
+      goto done;
+    }
+
+  result = FALSE;
+  if (count1 != count2)
+    goto done;
+
+  symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+
+  if (symtable1 == NULL || symtable2 == NULL)
+    goto done;
+
+  symp = symtable1;
+  for (isym = isymstart1, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd1,
+						    hdr1->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+ 
+  symp = symtable2;
+  for (isym = isymstart2, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd2,
+						    hdr2->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+  
+  /* Sort symbol by name.  */
+  qsort (symtable1, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+  qsort (symtable2, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+
+  for (i = 0; i < count1; i++)
+    /* Two symbols must have the same binding, type and name.  */
+    if (symtable1 [i].sym->st_info != symtable2 [i].sym->st_info
+	|| symtable1 [i].sym->st_other != symtable2 [i].sym->st_other
+	|| strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+      goto done;
+
+  result = TRUE;
+
+done:
+  if (symtable1)
+    free (symtable1);
+  if (symtable2)
+    free (symtable2);
+  if (isymbuf1)
+    free (isymbuf1);
+  if (isymbuf2)
+    free (isymbuf2);
+
+  return result;
+}
--- binutils/bfd/elfcode.h.linkonce	2004-04-22 08:20:00.000000000 -0700
+++ binutils/bfd/elfcode.h	2004-05-17 12:30:41.000000000 -0700
@@ -742,6 +742,9 @@ elf_object_p (bfd *abfd)
 	  if (shindex == SHN_LORESERVE - 1)
 	    shindex += SHN_HIRESERVE + 1 - SHN_LORESERVE;
 	}
+
+      /* Set up group pointers.  */
+      _bfd_elf_setup_group_pointers (abfd);
     }
 
   /* Let the backend double check the format and override global
--- binutils/bfd/elfxx-target.h.linkonce	2004-04-30 08:26:46.000000000 -0700
+++ binutils/bfd/elfxx-target.h	2004-05-17 09:42:45.000000000 -0700
@@ -144,6 +144,11 @@
 #define bfd_elfNN_bfd_discard_group bfd_elf_discard_group
 #endif
 
+#ifndef bfd_elfNN_bfd_match_symbols_in_sections
+#define bfd_elfNN_bfd_match_symbols_in_sections \
+  bfd_elf_match_symbols_in_sections
+#endif
+
 #ifndef bfd_elfNN_bfd_make_debug_symbol
 #define bfd_elfNN_bfd_make_debug_symbol \
   ((asymbol * (*) (bfd *, void *, unsigned long)) bfd_nullvoidptr)
--- binutils/bfd/i386msdos.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386msdos.c	2004-05-17 09:42:50.000000000 -0700
@@ -178,6 +178,8 @@ msdos_set_section_contents (abfd, sectio
 #define msdos_bfd_merge_sections bfd_generic_merge_sections
 #define msdos_bfd_is_group_section bfd_generic_is_group_section
 #define msdos_bfd_discard_group bfd_generic_discard_group
+#define msdos_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define msdos_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define msdos_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define msdos_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/i386os9k.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386os9k.c	2004-05-17 13:33:11.000000000 -0700
@@ -335,6 +335,8 @@ os9k_sizeof_headers (ignore_abfd, ignore
 #define os9k_bfd_merge_sections bfd_generic_merge_sections
 #define os9k_bfd_is_group_section bfd_generic_is_group_section
 #define os9k_bfd_discard_group bfd_generic_discard_group
+#define os9k_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define os9k_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define os9k_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define os9k_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ieee.c.linkonce	2004-04-30 08:26:50.000000000 -0700
+++ binutils/bfd/ieee.c	2004-05-17 09:42:58.000000000 -0700
@@ -4039,6 +4039,8 @@ ieee_bfd_debug_info_accumulate (abfd, se
 #define ieee_bfd_merge_sections bfd_generic_merge_sections
 #define ieee_bfd_is_group_section bfd_generic_is_group_section
 #define ieee_bfd_discard_group bfd_generic_discard_group
+#define ieee_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ieee_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ieee_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ieee_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ihex.c.linkonce	2004-04-30 08:26:51.000000000 -0700
+++ binutils/bfd/ihex.c	2004-05-17 09:43:03.000000000 -0700
@@ -990,6 +990,8 @@ ihex_sizeof_headers (abfd, exec)
 #define ihex_bfd_merge_sections bfd_generic_merge_sections
 #define ihex_bfd_is_group_section bfd_generic_is_group_section
 #define ihex_bfd_discard_group bfd_generic_discard_group
+#define ihex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ihex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ihex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ihex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/libbfd-in.h.linkonce	2004-04-30 08:26:52.000000000 -0700
+++ binutils/bfd/libbfd-in.h	2004-05-17 09:43:08.000000000 -0700
@@ -361,6 +361,9 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_discard_group \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) \
    bfd_false)
+#define _bfd_nolink_bfd_match_symbols_in_sections \
+  ((bfd_boolean (*) (struct bfd_section *, struct bfd_section *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_link_hash_table_create \
   ((struct bfd_link_hash_table *(*) (bfd *)) bfd_nullvoidptr)
 #define _bfd_nolink_bfd_link_hash_table_free \
--- binutils/bfd/mach-o.c.linkonce	2004-04-30 08:26:56.000000000 -0700
+++ binutils/bfd/mach-o.c	2004-05-17 09:43:35.000000000 -0700
@@ -70,6 +70,8 @@
 #define bfd_mach_o_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_mach_o_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_mach_o_bfd_discard_group bfd_generic_discard_group
+#define bfd_mach_o_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 static bfd_boolean bfd_mach_o_bfd_copy_private_symbol_data
   PARAMS ((bfd *, asymbol *, bfd *, asymbol *));
--- binutils/bfd/mmo.c.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/mmo.c	2004-05-17 09:43:40.000000000 -0700
@@ -3288,6 +3288,8 @@ mmo_canonicalize_reloc (abfd, section, r
 #define mmo_bfd_merge_sections bfd_generic_merge_sections
 #define mmo_bfd_is_group_section bfd_generic_is_group_section
 #define mmo_bfd_discard_group bfd_generic_discard_group
+#define mmo_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 /* objcopy will be upset if we return -1 from bfd_get_reloc_upper_bound by
    using BFD_JUMP_TABLE_RELOCS (_bfd_norelocs) rather than 0.  FIXME: Most
--- binutils/bfd/nlm-target.h.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/nlm-target.h	2004-05-17 09:43:45.000000000 -0700
@@ -46,6 +46,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define nlm_bfd_merge_sections bfd_generic_merge_sections
 #define nlm_bfd_is_group_section bfd_generic_is_group_section
 #define nlm_bfd_discard_group bfd_generic_discard_group
+#define nlm_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define nlm_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define nlm_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define nlm_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/oasys.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/oasys.c	2004-05-17 09:43:49.000000000 -0700
@@ -1508,6 +1508,8 @@ oasys_sizeof_headers (abfd, exec)
 #define oasys_bfd_merge_sections bfd_generic_merge_sections
 #define oasys_bfd_is_group_section bfd_generic_is_group_section
 #define oasys_bfd_discard_group bfd_generic_discard_group
+#define oasys_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define oasys_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define oasys_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define oasys_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/pef.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/pef.c	2004-05-17 13:33:50.000000000 -0700
@@ -54,6 +54,7 @@
 #define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
 #define bfd_pef_bfd_discard_group                   bfd_generic_discard_group
+#define bfd_pef_bfd_match_symbols_in_sections	    _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_pef_bfd_link_hash_table_create          _bfd_generic_link_hash_table_create
 #define bfd_pef_bfd_link_hash_table_free            _bfd_generic_link_hash_table_free
 #define bfd_pef_bfd_link_add_symbols                _bfd_generic_link_add_symbols
--- binutils/bfd/ppcboot.c.linkonce	2004-04-30 08:27:02.000000000 -0700
+++ binutils/bfd/ppcboot.c	2004-05-17 09:44:21.000000000 -0700
@@ -471,6 +471,8 @@ ppcboot_bfd_print_private_bfd_data (abfd
 #define ppcboot_bfd_merge_sections bfd_generic_merge_sections
 #define ppcboot_bfd_is_group_section bfd_generic_is_group_section
 #define ppcboot_bfd_discard_group bfd_generic_discard_group
+#define ppcboot_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/section.c.linkonce	2004-05-17 07:37:17.000000000 -0700
+++ binutils/bfd/section.c	2004-05-17 07:38:59.000000000 -0700
@@ -493,6 +493,10 @@ CODE_FRAGMENT
 .  {* Optional information about a COMDAT entry; NULL if not COMDAT.  *}
 .  struct bfd_comdat_info *comdat;
 .
+.  {* Optional information about section group; NULL if it doesn't
+.     belongs to any section group.  *}
+.  struct bfd_section *group;
+.
 .  {* Points to the kept section if this section is a link-once section,
 .     and is discarded.  *}
 .  struct bfd_section *kept_section;
@@ -643,8 +647,8 @@ static const asymbol global_syms[] =
     /* line_filepos, userdata, contents, lineno, lineno_count,       */	\
        0,            NULL,     NULL,     NULL,   0,			\
 									\
-    /* entsize, comdat, kept_section, moving_line_filepos,           */	\
-       0,       NULL,   NULL,	      0,				\
+    /* entsize, comdat, group, kept_section, moving_line_filepos,    */	\
+       0,       NULL,   NULL,  NULL,	      0,			\
 									\
     /* target_index, used_by_bfd, constructor_chain, owner,          */	\
        0,            NULL,        NULL,              NULL,		\
--- binutils/bfd/som.c.linkonce	2004-05-03 09:00:44.000000000 -0700
+++ binutils/bfd/som.c	2004-05-17 09:44:25.000000000 -0700
@@ -6412,6 +6412,8 @@ som_bfd_link_split_section (abfd, sec)
 #define som_bfd_merge_sections		bfd_generic_merge_sections
 #define som_bfd_is_group_section	bfd_generic_is_group_section
 #define som_bfd_discard_group		bfd_generic_discard_group
+#define som_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target som_vec = {
   "som",			/* name */
--- binutils/bfd/srec.c.linkonce	2004-04-30 08:27:15.000000000 -0700
+++ binutils/bfd/srec.c	2004-05-17 09:44:33.000000000 -0700
@@ -1286,6 +1286,8 @@ srec_print_symbol (abfd, afile, symbol, 
 #define srec_bfd_merge_sections bfd_generic_merge_sections
 #define srec_bfd_is_group_section bfd_generic_is_group_section
 #define srec_bfd_discard_group bfd_generic_discard_group
+#define srec_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define srec_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define srec_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define srec_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/targets.c.linkonce	2004-05-14 09:00:50.000000000 -0700
+++ binutils/bfd/targets.c	2004-05-17 09:44:55.000000000 -0700
@@ -407,7 +407,8 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_gc_sections, \
 .  NAME##_bfd_merge_sections, \
 .  NAME##_bfd_is_group_section, \
-.  NAME##_bfd_discard_group
+.  NAME##_bfd_discard_group, \
+.  NAME##_bfd_match_symbols_in_sections \
 .
 .  int         (*_bfd_sizeof_headers) (bfd *, bfd_boolean);
 .  bfd_byte *  (*_bfd_get_relocated_section_contents)
@@ -450,6 +451,11 @@ BFD_JUMP_TABLE macros.
 .  {* Discard members of a group.  *}
 .  bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *);
 .
+.  {* Return TRUE if need to try if 2 sections export the same set of
+.     symbols.  *}
+.  bfd_boolean (*_bfd_match_symbols_in_sections)
+.    (struct bfd_section *, struct bfd_section *);
+.
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
 .  NAME##_get_dynamic_symtab_upper_bound, \
--- binutils/bfd/tekhex.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/tekhex.c	2004-05-17 09:45:02.000000000 -0700
@@ -1003,6 +1003,8 @@ tekhex_print_symbol (abfd, filep, symbol
 #define tekhex_bfd_merge_sections bfd_generic_merge_sections
 #define tekhex_bfd_is_group_section bfd_generic_is_group_section
 #define tekhex_bfd_discard_group bfd_generic_discard_group
+#define tekhex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define tekhex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define tekhex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define tekhex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/versados.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/versados.c	2004-05-17 09:45:10.000000000 -0700
@@ -874,6 +874,8 @@ versados_canonicalize_reloc (abfd, secti
 #define versados_bfd_merge_sections bfd_generic_merge_sections
 #define versados_bfd_is_group_section bfd_generic_is_group_section
 #define versados_bfd_discard_group bfd_generic_discard_group
+#define versados_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define versados_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define versados_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define versados_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/vms.c.linkonce	2004-04-30 08:27:20.000000000 -0700
+++ binutils/bfd/vms.c	2004-05-17 13:35:00.000000000 -0700
@@ -168,6 +168,8 @@ static bfd_boolean vms_bfd_set_private_f
 #define vms_bfd_link_just_syms _bfd_generic_link_just_syms
 #define vms_bfd_is_group_section bfd_generic_is_group_section
 #define vms_bfd_discard_group bfd_generic_discard_group
+#define vms_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 \f
 /*===========================================================================*/
 
--- binutils/bfd/xcoff-target.h.linkonce	2002-12-04 09:03:10.000000000 -0800
+++ binutils/bfd/xcoff-target.h	2004-05-17 09:45:17.000000000 -0700
@@ -99,6 +99,8 @@ extern int lynx_core_file_failing_signal
 #define _bfd_xcoff_bfd_gc_sections coff_bfd_gc_sections
 #define _bfd_xcoff_bfd_merge_sections coff_bfd_merge_sections
 #define _bfd_xcoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_xcoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define _bfd_xcoff_bfd_link_split_section coff_bfd_link_split_section
 
 /* XCOFF archives do not have anything which corresponds to an
--- binutils/bfd/xsym.c.linkonce	2004-04-30 08:27:22.000000000 -0700
+++ binutils/bfd/xsym.c	2004-05-17 09:45:22.000000000 -0700
@@ -44,6 +44,8 @@
 #define bfd_sym_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_sym_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_sym_bfd_discard_group bfd_generic_discard_group
+#define bfd_sym_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_sym_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define bfd_sym_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define bfd_sym_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/ld/ldlang.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/ld/ldlang.c	2004-05-17 13:57:34.000000000 -0700
@@ -853,6 +853,36 @@ struct already_linked
   asection *sec;
 };
 
+struct already_linked_section
+{
+  asection *sec;
+  asection *linked;
+};
+
+static bfd_boolean
+try_match_symbols_in_sections (struct already_linked_hash_entry *h,
+			       void *info)
+{
+  struct already_linked *l;
+  struct already_linked_section *s
+    = (struct already_linked_section *) info;
+
+  /* No need to check group section.  */
+  if ((s->sec->flags & SEC_GROUP) != 0)
+    return TRUE;
+
+  for (l = h->entry; l != NULL; l = l->next)
+    if ((l->sec->flags & SEC_GROUP) == 0
+	&& bfd_match_symbols_in_sections (l->sec->owner,
+					  l->sec, s->sec))
+      {
+	s->linked = l->sec;
+	return FALSE;
+      }
+
+  return TRUE;
+}
+
 /* The hash table.  */
 
 static struct bfd_hash_table already_linked_table;
@@ -865,6 +895,8 @@ section_already_linked (bfd *abfd, asect
   const char *name;
   struct already_linked *l;
   struct already_linked_hash_entry *already_linked_list;
+  struct already_linked_section result;
+  asection *group;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
@@ -876,7 +908,13 @@ section_already_linked (bfd *abfd, asect
 
   flags = bfd_get_section_flags (abfd, sec);
 
-  if ((flags & SEC_LINK_ONCE) == 0)
+  /* Check if it belongs to a section group.  */
+  group = sec->group;
+
+  if ((flags & SEC_LINK_ONCE) == 0
+      && (group == NULL
+	  || (group->flags
+	      & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD)) == 0))
     return;
 
   /* FIXME: When doing a relocatable link, we may have trouble
@@ -957,6 +995,25 @@ section_already_linked (bfd *abfd, asect
 	}
     }
 
+  /* When we get here, we must be either a group member or a
+     .gnu.linkonce section. Check if a member of a COMDAT group
+     matches a .gnu.linkonce section and vice versa.  */
+  result.sec = sec;
+  result.linked = NULL;
+  bfd_hash_traverse (&already_linked_table,
+		     (bfd_boolean (*) (struct bfd_hash_entry *, void *))
+		     try_match_symbols_in_sections,
+		     &result);
+  if (result.linked)
+    {
+      sec->output_section = bfd_abs_section_ptr;
+      sec->kept_section = result.linked;
+
+      /* Also discard the group section.  */
+      if (group != NULL)
+	group->output_section = bfd_abs_section_ptr;
+    }
+
   /* This is the first section with this name.  Record it.  Allocate
      the memory from the same obstack as the hash table is kept in.  */
 

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-17 21:24   ` H. J. Lu
@ 2004-05-18  1:06     ` H. J. Lu
  2004-05-18  2:13       ` H. J. Lu
  2004-05-18  3:41       ` H. J. Lu
  0 siblings, 2 replies; 10+ messages in thread
From: H. J. Lu @ 2004-05-18  1:06 UTC (permalink / raw)
  To: binutils

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

On Mon, May 17, 2004 at 02:24:23PM -0700, H. J. Lu wrote:
> On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> > On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > > them gracefully:
> > 
> > Ewww.  Should we even try?  I understand that such a patch might be
> > useful while gcc is emitting both comdat and linkonce, but once you've
> > completed the change to comdat it shouldn't be necessary.  Also, I'm not
> > really happy with where you have added this code.  At least, it is the
> > wrong place to be discarding duplicate sections.  That ought to happen
> > in ldlang.c:section_already_linked.
> > 
> 
> This patch implements it.
> 
> 

Here is an update. I should skip checking members of section groups
for already linked section.

I think there may be a bug in ldlang.c:section_already_linked. We
check section names for linkonce sections. But for group sections,
section names are meaningless. Compilers/assemblers don't have to
use group signature for section name.  Shouldn't we check for group
signatures instead?


H.J.

[-- Attachment #2: bfd-comdat-linkonce-3.patch --]
[-- Type: text/plain, Size: 36642 bytes --]

bfd/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* aout-adobe.c (aout_32_bfd_match_symbols_in_sections): Defined.
	* aout-target.h (MY_bfd_match_symbols_in_sections): Likewise.
	* aout-tic30.c (MY_bfd_match_symbols_in_sections): Likewise.
	* binary.c (binary_bfd_match_symbols_in_sections): Likewise.
	* bout.c (b_out_bfd_match_symbols_in_sections): Likewise.
	* coff-alpha.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coff-mips.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coffcode.h (coff_bfd_match_symbols_in_sections): Likewise.
	* i386msdos.c (msdos_bfd_match_symbols_in_sections): Likewise.
	* i386os9k.c (os9k_bfd_match_symbols_in_sections): Likewise.
	* ieee.c (ieee_bfd_match_symbols_in_sections): Likewise.
	* ihex.c (ihex_bfd_match_symbols_in_sections): Likewise.
	* mach-o.c (bfd_mach_o_bfd_match_symbols_in_sections): Likewise.
	* mmo.c (mmo_bfd_discard_group): Likewise.
	* nlm-target.h (nlm_bfd_match_symbols_in_sections): Likewise.
	* oasys.c (oasys_bfd_match_symbols_in_sections): Likewise.
	* pef.c (bfd_pef_bfd_match_symbols_in_sections): Likewise.
	* ppcboot.c (ppcboot_bfd_match_symbols_in_sections): Likewise.
	* som.c (som_bfd_discard_group): Likewise.
	* srec.c (srec_bfd_match_symbols_in_sections): Likewise.
	* tekhex.c (tekhex_bfd_match_symbols_in_sections): Likewise.
	* versados.c (versados_bfd_match_symbols_in_sections): Likewise.
	* vms.c (vms_bfd_match_symbols_in_sections): Likewise.
	* coff-target.h (_bfd_xcoff_bfd_match_symbols_in_sections): Likewise.
	* xsym.c (bfd_sym_bfd_match_symbols_in_sections): Likewise.

	* bfd.c (bfd_match_symbols_in_sections): New.

	* coff-rs6000.c (rs6000coff_vec): Add
	_bfd_nolink_bfd_match_symbols_in_sections,
	(pmac_xcoff_vec): Likewise.
	* coff64-rs6000.c (rs6000coff64_vec): Likewise.
	(aix5coff64_vec): Likewise.

	* elf-bfd.h (bfd_elf_match_symbols_in_sections): New prototype.
	(_bfd_elf_setup_group_pointers): Likewise.

	* elf.c (_bfd_elf_setup_group_pointers): New function.
	(elf_sort_elf_symbol): Likewise.
	(elf_sym_name_compare): Likewise.
	(bfd_elf_match_symbols_in_sections): Likewise.

	* elfcode.h (elf_object_p): Call _bfd_elf_setup_group_pointers.

	* elfxx-target.h (bfd_elfNN_bfd_match_symbols_in_sections): Defined.

	* libbfd-in.h (_bfd_nolink_bfd_match_symbols_in_sections): Defined/

	* section.c (bfd_section): Add group, a pointer to section group.

	* section (STD_SECTION): Initialize group to NULL.
	* ecoff.c (bfd_debug_section): Likewise.

	* targets.c (bfd_target): Add _bfd_match_symbols_in_sections.
	(BFD_JUMP_TABLE_LINK): Updated.

	* bfd-in2.h: Regenerated.
	* libbfd.h: Regenerated.

ld/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* ldlang.c (already_linked_section): New structure.
	(try_match_symbols_in_sections): New function.
	(section_already_linked): Check if a member of a COMDAT group
	matches a .gnu.linkonce section.

--- binutils/bfd/aout-adobe.c.linkonce	2004-04-30 08:25:52.000000000 -0700
+++ binutils/bfd/aout-adobe.c	2004-05-17 13:27:55.000000000 -0700
@@ -519,6 +519,8 @@ aout_adobe_sizeof_headers (ignore_abfd, 
 #define aout_32_bfd_merge_sections	bfd_generic_merge_sections
 #define aout_32_bfd_is_group_section	bfd_generic_is_group_section
 #define aout_32_bfd_discard_group	bfd_generic_discard_group
+#define aout_32_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define aout_32_bfd_link_hash_table_create \
   _bfd_generic_link_hash_table_create
 #define aout_32_bfd_link_hash_table_free \
--- binutils/bfd/aout-target.h.linkonce	2004-04-30 08:25:53.000000000 -0700
+++ binutils/bfd/aout-target.h	2004-05-17 09:41:08.000000000 -0700
@@ -519,6 +519,10 @@ MY_bfd_final_link (abfd, info)
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup NAME(aout,reloc_type_lookup)
 #endif
--- binutils/bfd/aout-tic30.c.linkonce	2004-04-30 08:25:54.000000000 -0700
+++ binutils/bfd/aout-tic30.c	2004-05-17 09:41:32.000000000 -0700
@@ -976,6 +976,10 @@ tic30_aout_set_arch_mach (abfd, arch, ma
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup tic30_aout_reloc_type_lookup
 #endif
--- binutils/bfd/bfd.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/bfd/bfd.c	2004-05-17 09:40:43.000000000 -0700
@@ -1089,6 +1089,9 @@ DESCRIPTION
 .#define bfd_discard_group(abfd, sec) \
 .	BFD_SEND (abfd, _bfd_discard_group, (abfd, sec))
 .
+.#define bfd_match_symbols_in_sections(abfd, sec1, sec2) \
+.	BFD_SEND (abfd, _bfd_match_symbols_in_sections, (sec1, sec2))
+.
 .#define bfd_link_hash_table_create(abfd) \
 .	BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd))
 .
--- binutils/bfd/binary.c.linkonce	2004-04-30 08:26:01.000000000 -0700
+++ binutils/bfd/binary.c	2004-05-17 09:41:47.000000000 -0700
@@ -341,6 +341,8 @@ binary_sizeof_headers (abfd, exec)
 #define binary_bfd_merge_sections bfd_generic_merge_sections
 #define binary_bfd_is_group_section bfd_generic_is_group_section
 #define binary_bfd_discard_group bfd_generic_discard_group
+#define binary_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define binary_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define binary_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define binary_bfd_link_just_syms _bfd_generic_link_just_syms
--- binutils/bfd/bout.c.linkonce	2004-04-30 08:26:02.000000000 -0700
+++ binutils/bfd/bout.c	2004-05-17 13:28:43.000000000 -0700
@@ -1489,6 +1489,8 @@ b_out_bfd_get_relocated_section_contents
 #define b_out_bfd_merge_sections  bfd_generic_merge_sections
 #define b_out_bfd_is_group_section bfd_generic_is_group_section
 #define b_out_bfd_discard_group bfd_generic_discard_group
+#define b_out_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 #define aout_32_get_section_contents_in_window \
   _bfd_generic_get_section_contents_in_window
--- binutils/bfd/coff-alpha.c.linkonce	2004-04-30 08:26:04.000000000 -0700
+++ binutils/bfd/coff-alpha.c	2004-05-17 09:41:52.000000000 -0700
@@ -2361,6 +2361,8 @@ static const struct ecoff_backend_data a
 #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target ecoffalpha_little_vec =
 {
--- binutils/bfd/coff-mips.c.linkonce	2004-04-30 08:26:06.000000000 -0700
+++ binutils/bfd/coff-mips.c	2004-05-17 09:42:02.000000000 -0700
@@ -1395,6 +1395,8 @@ static const struct ecoff_backend_data m
 
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 extern const bfd_target ecoff_big_vec;
 
--- binutils/bfd/coff-rs6000.c.linkonce	2004-04-30 08:26:10.000000000 -0700
+++ binutils/bfd/coff-rs6000.c	2004-05-17 09:42:06.000000000 -0700
@@ -4198,6 +4198,7 @@ const bfd_target rs6000coff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -4442,6 +4443,7 @@ const bfd_target pmac_xcoff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coff64-rs6000.c.linkonce	2004-04-30 08:26:13.000000000 -0700
+++ binutils/bfd/coff64-rs6000.c	2004-05-17 09:42:18.000000000 -0700
@@ -2739,6 +2739,7 @@ const bfd_target rs6000coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -2984,6 +2985,7 @@ const bfd_target aix5coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coffcode.h.linkonce	2004-05-07 13:21:00.000000000 -0700
+++ binutils/bfd/coffcode.h	2004-05-17 09:42:24.000000000 -0700
@@ -5574,6 +5574,11 @@ static const bfd_coff_backend_data ticof
 #define coff_bfd_discard_group		    bfd_generic_discard_group
 #endif
 
+#ifndef coff_bfd_match_symbols_in_sections
+#define coff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
+
 #define CREATE_BIG_COFF_TARGET_VEC(VAR, NAME, EXTRA_O_FLAGS, EXTRA_S_FLAGS, UNDER, ALTERNATIVE, SWAP_TABLE)	\
 const bfd_target VAR =							\
 {									\
--- binutils/bfd/ecoff.c.linkonce	2004-04-26 21:11:29.000000000 -0700
+++ binutils/bfd/ecoff.c	2004-05-17 13:30:52.000000000 -0700
@@ -95,8 +95,8 @@ static asection bfd_debug_section =
      NULL,       NULL,        0,           0,       0,
   /* line_filepos, userdata, contents, lineno, lineno_count,       */
      0,            NULL,     NULL,     NULL,   0,
-  /* entsize, comdat, kept_section, moving_line_filepos,           */
-     0,       NULL,   NULL,         0,
+  /* entsize, comdat, group, kept_section, moving_line_filepos,    */
+     0,       NULL,   NULL,  NULL,         0,
   /* target_index, used_by_bfd, constructor_chain, owner,          */
      0,            NULL,        NULL,              NULL,
   /* symbol,                                                       */
--- binutils/bfd/elf-bfd.h.linkonce	2004-05-11 13:33:49.000000000 -0700
+++ binutils/bfd/elf-bfd.h	2004-05-17 12:32:04.000000000 -0700
@@ -1375,6 +1375,8 @@ extern bfd_boolean bfd_elf_is_group_sect
   (bfd *, const struct bfd_section *);
 extern bfd_boolean bfd_elf_discard_group
   (bfd *, struct bfd_section *);
+extern bfd_boolean bfd_elf_match_symbols_in_sections
+  (struct bfd_section *, struct bfd_section *);
 extern void bfd_elf_set_group_contents
   (bfd *, asection *, void *);
 extern void _bfd_elf_link_just_syms
@@ -1554,6 +1556,9 @@ extern bfd_boolean _bfd_elf_dynamic_symb
 extern bfd_boolean _bfd_elf_symbol_refs_local_p
   (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
 
+extern void _bfd_elf_setup_group_pointers
+  (bfd *);
+
 extern const bfd_target *bfd_elf32_object_p
   (bfd *);
 extern const bfd_target *bfd_elf32_core_file_p
--- binutils/bfd/elf.c.linkonce	2004-05-11 13:33:56.000000000 -0700
+++ binutils/bfd/elf.c	2004-05-17 14:14:58.000000000 -0700
@@ -612,6 +612,26 @@ setup_group (bfd *abfd, Elf_Internal_Shd
   return TRUE;
 }
 
+void
+_bfd_elf_setup_group_pointers (bfd *abfd)
+{
+  unsigned int i;
+  unsigned int num_group = elf_tdata (abfd)->num_group;
+
+  if (num_group == (unsigned) -1)
+    return;
+
+  for (i = 0; i < num_group; i++)
+    {
+      Elf_Internal_Shdr *shdr = elf_tdata (abfd)->group_sect_ptr[i];
+      Elf_Internal_Group *idx = (Elf_Internal_Group *) shdr->contents;
+      unsigned int n_elt = shdr->sh_size / 4;
+
+      while (--n_elt != 0)
+	(++idx)->shdr->bfd_section->group = shdr->bfd_section;
+    }
+}
+
 bfd_boolean
 bfd_elf_is_group_section (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec)
 {
@@ -7678,3 +7698,240 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd
 
   return n;
 }
+
+/* Sort symbol by binding and section. We want to put global
+   symbols sorted by section at the beginning.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1;
+  const Elf_Internal_Sym *s2;
+
+  /* Make sure local or undefined symbols are at the end.  */
+  s1 = (const Elf_Internal_Sym *) arg1;
+  if (s1->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s1->st_info) == STB_LOCAL)
+    return 1;
+  s2 = (const Elf_Internal_Sym *) arg2;
+  if (s2->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s2->st_info) == STB_LOCAL)
+    return -1;
+
+  /* Grouped by section index.  */
+  return s1->st_shndx - s2->st_shndx;
+}
+
+struct elf_symbol
+{
+  Elf_Internal_Sym *sym;
+  const char *name;
+};
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+  const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+  const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+  return strcmp (s1->name, s2->name);
+}
+
+/* Check if 2 sections export the same set of symbols.  */
+
+bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+{
+  bfd *bfd1, *bfd2;
+  const struct elf_backend_data *bed1, *bed2;
+  Elf_Internal_Shdr *hdr1, *hdr2;
+  bfd_size_type symcount1, symcount2;
+  bfd_size_type extsymcount1, extsymcount2;
+  bfd_size_type extsymoff1, extsymoff2;
+  Elf_Internal_Sym *isymbuf1, *isymbuf2;
+  Elf_Internal_Sym *isymstart1 = NULL, *isymstart2 = NULL, *isym;
+  Elf_Internal_Sym *isymend;
+  struct elf_symbol *symp, *symtable1 = NULL, *symtable2 = NULL;
+  bfd_size_type count1, count2, i;
+  int shndx1, shndx2;
+  bfd_boolean result;
+
+  bfd1 = sec1->owner;
+  bfd2 = sec2->owner;
+
+  /* Both sections have to be in ELF.  */
+  if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+      || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+    return FALSE;
+
+  /* One section has to be in a COMDAT group with only one section and
+     the other one has to be a .gnu.linkonce section.  */
+  if (!(elf_section_type (sec1) == elf_section_type (sec2)
+	&& ((elf_section_flags (sec1) & ~SHF_GROUP)
+	    == (elf_section_flags (sec2) & ~SHF_GROUP))
+	&& ((sec1->group != NULL
+	     && ((sec1->group->flags & (SEC_LINK_ONCE
+					| SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	     && elf_next_in_group (sec1) == sec1
+	     && sec2->group == NULL
+	     && ((sec2->flags & (SEC_LINK_ONCE
+				 | SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD)))
+	    || (sec1->group == NULL
+		&& sec2->group != NULL
+		&& ((sec2->group->flags & (SEC_LINK_ONCE
+					   | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+		&& elf_next_in_group (sec2) == sec2
+		&& ((sec1->flags & (SEC_LINK_ONCE
+				    | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))))))
+    return FALSE;
+
+  shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+  shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+  if (shndx1 == -1 || shndx2 == -1)
+    return FALSE;
+
+  bed1 = get_elf_backend_data (bfd1);
+  bed2 = get_elf_backend_data (bfd2);
+  hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+  symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+  hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+  symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+  if (elf_bad_symtab (bfd1))
+    {
+      extsymcount1 = symcount1;
+      extsymoff1 = 0;
+    }
+  else
+    {
+      extsymcount1 = symcount1 - hdr1->sh_info;
+      extsymoff1 = hdr1->sh_info;
+    }
+
+  if (elf_bad_symtab (bfd2))
+    {
+      extsymcount2 = symcount2;
+      extsymoff2 = 0;
+    }
+  else
+    {
+      extsymcount2 = symcount2 - hdr2->sh_info;
+      extsymoff2 = hdr2->sh_info;
+    }
+
+  if (extsymcount1 == 0 || extsymcount2 == 0)
+    return FALSE;
+
+  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, extsymcount1,
+				     extsymoff1, NULL, NULL, NULL);
+  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, extsymcount2,
+				     extsymoff2, NULL, NULL, NULL);
+  if (isymbuf1 == NULL || isymbuf2 == NULL)
+    return FALSE;
+
+  /* Sort symbols by binding and section. Global definitions are at
+     the beginning.  */
+  qsort (isymbuf1, extsymcount1, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+  qsort (isymbuf2, extsymcount2, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+
+  /* Count global symbols defined in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + extsymcount1;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx1)
+	{
+	  if (count1 == 0)
+	    isymstart1 = isym;
+	  count1++;
+	}
+
+      if (count1 && isym->st_shndx != (unsigned int) shndx1)
+	break;
+    }
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + extsymcount2;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx2)
+	{
+	  if (count2 == 0)
+	    isymstart2 = isym;
+	  count2++;
+	}
+
+      if (count2 && isym->st_shndx != (unsigned int) shndx2)
+	break;
+    }
+
+  if (count1 == 0 && count2 == 0)
+    {
+      result = TRUE;
+      goto done;
+    }
+
+  result = FALSE;
+  if (count1 != count2)
+    goto done;
+
+  symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+
+  if (symtable1 == NULL || symtable2 == NULL)
+    goto done;
+
+  symp = symtable1;
+  for (isym = isymstart1, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd1,
+						    hdr1->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+ 
+  symp = symtable2;
+  for (isym = isymstart2, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd2,
+						    hdr2->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+  
+  /* Sort symbol by name.  */
+  qsort (symtable1, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+  qsort (symtable2, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+
+  for (i = 0; i < count1; i++)
+    /* Two symbols must have the same binding, type and name.  */
+    if (symtable1 [i].sym->st_info != symtable2 [i].sym->st_info
+	|| symtable1 [i].sym->st_other != symtable2 [i].sym->st_other
+	|| strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+      goto done;
+
+  result = TRUE;
+
+done:
+  if (symtable1)
+    free (symtable1);
+  if (symtable2)
+    free (symtable2);
+  if (isymbuf1)
+    free (isymbuf1);
+  if (isymbuf2)
+    free (isymbuf2);
+
+  return result;
+}
--- binutils/bfd/elfcode.h.linkonce	2004-04-22 08:20:00.000000000 -0700
+++ binutils/bfd/elfcode.h	2004-05-17 12:30:41.000000000 -0700
@@ -742,6 +742,9 @@ elf_object_p (bfd *abfd)
 	  if (shindex == SHN_LORESERVE - 1)
 	    shindex += SHN_HIRESERVE + 1 - SHN_LORESERVE;
 	}
+
+      /* Set up group pointers.  */
+      _bfd_elf_setup_group_pointers (abfd);
     }
 
   /* Let the backend double check the format and override global
--- binutils/bfd/elfxx-target.h.linkonce	2004-04-30 08:26:46.000000000 -0700
+++ binutils/bfd/elfxx-target.h	2004-05-17 09:42:45.000000000 -0700
@@ -144,6 +144,11 @@
 #define bfd_elfNN_bfd_discard_group bfd_elf_discard_group
 #endif
 
+#ifndef bfd_elfNN_bfd_match_symbols_in_sections
+#define bfd_elfNN_bfd_match_symbols_in_sections \
+  bfd_elf_match_symbols_in_sections
+#endif
+
 #ifndef bfd_elfNN_bfd_make_debug_symbol
 #define bfd_elfNN_bfd_make_debug_symbol \
   ((asymbol * (*) (bfd *, void *, unsigned long)) bfd_nullvoidptr)
--- binutils/bfd/i386msdos.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386msdos.c	2004-05-17 09:42:50.000000000 -0700
@@ -178,6 +178,8 @@ msdos_set_section_contents (abfd, sectio
 #define msdos_bfd_merge_sections bfd_generic_merge_sections
 #define msdos_bfd_is_group_section bfd_generic_is_group_section
 #define msdos_bfd_discard_group bfd_generic_discard_group
+#define msdos_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define msdos_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define msdos_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define msdos_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/i386os9k.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386os9k.c	2004-05-17 13:33:11.000000000 -0700
@@ -335,6 +335,8 @@ os9k_sizeof_headers (ignore_abfd, ignore
 #define os9k_bfd_merge_sections bfd_generic_merge_sections
 #define os9k_bfd_is_group_section bfd_generic_is_group_section
 #define os9k_bfd_discard_group bfd_generic_discard_group
+#define os9k_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define os9k_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define os9k_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define os9k_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ieee.c.linkonce	2004-04-30 08:26:50.000000000 -0700
+++ binutils/bfd/ieee.c	2004-05-17 09:42:58.000000000 -0700
@@ -4039,6 +4039,8 @@ ieee_bfd_debug_info_accumulate (abfd, se
 #define ieee_bfd_merge_sections bfd_generic_merge_sections
 #define ieee_bfd_is_group_section bfd_generic_is_group_section
 #define ieee_bfd_discard_group bfd_generic_discard_group
+#define ieee_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ieee_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ieee_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ieee_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ihex.c.linkonce	2004-04-30 08:26:51.000000000 -0700
+++ binutils/bfd/ihex.c	2004-05-17 09:43:03.000000000 -0700
@@ -990,6 +990,8 @@ ihex_sizeof_headers (abfd, exec)
 #define ihex_bfd_merge_sections bfd_generic_merge_sections
 #define ihex_bfd_is_group_section bfd_generic_is_group_section
 #define ihex_bfd_discard_group bfd_generic_discard_group
+#define ihex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ihex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ihex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ihex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/libbfd-in.h.linkonce	2004-04-30 08:26:52.000000000 -0700
+++ binutils/bfd/libbfd-in.h	2004-05-17 09:43:08.000000000 -0700
@@ -361,6 +361,9 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_discard_group \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) \
    bfd_false)
+#define _bfd_nolink_bfd_match_symbols_in_sections \
+  ((bfd_boolean (*) (struct bfd_section *, struct bfd_section *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_link_hash_table_create \
   ((struct bfd_link_hash_table *(*) (bfd *)) bfd_nullvoidptr)
 #define _bfd_nolink_bfd_link_hash_table_free \
--- binutils/bfd/mach-o.c.linkonce	2004-04-30 08:26:56.000000000 -0700
+++ binutils/bfd/mach-o.c	2004-05-17 09:43:35.000000000 -0700
@@ -70,6 +70,8 @@
 #define bfd_mach_o_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_mach_o_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_mach_o_bfd_discard_group bfd_generic_discard_group
+#define bfd_mach_o_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 static bfd_boolean bfd_mach_o_bfd_copy_private_symbol_data
   PARAMS ((bfd *, asymbol *, bfd *, asymbol *));
--- binutils/bfd/mmo.c.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/mmo.c	2004-05-17 09:43:40.000000000 -0700
@@ -3288,6 +3288,8 @@ mmo_canonicalize_reloc (abfd, section, r
 #define mmo_bfd_merge_sections bfd_generic_merge_sections
 #define mmo_bfd_is_group_section bfd_generic_is_group_section
 #define mmo_bfd_discard_group bfd_generic_discard_group
+#define mmo_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 /* objcopy will be upset if we return -1 from bfd_get_reloc_upper_bound by
    using BFD_JUMP_TABLE_RELOCS (_bfd_norelocs) rather than 0.  FIXME: Most
--- binutils/bfd/nlm-target.h.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/nlm-target.h	2004-05-17 09:43:45.000000000 -0700
@@ -46,6 +46,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define nlm_bfd_merge_sections bfd_generic_merge_sections
 #define nlm_bfd_is_group_section bfd_generic_is_group_section
 #define nlm_bfd_discard_group bfd_generic_discard_group
+#define nlm_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define nlm_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define nlm_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define nlm_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/oasys.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/oasys.c	2004-05-17 09:43:49.000000000 -0700
@@ -1508,6 +1508,8 @@ oasys_sizeof_headers (abfd, exec)
 #define oasys_bfd_merge_sections bfd_generic_merge_sections
 #define oasys_bfd_is_group_section bfd_generic_is_group_section
 #define oasys_bfd_discard_group bfd_generic_discard_group
+#define oasys_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define oasys_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define oasys_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define oasys_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/pef.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/pef.c	2004-05-17 13:33:50.000000000 -0700
@@ -54,6 +54,7 @@
 #define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
 #define bfd_pef_bfd_discard_group                   bfd_generic_discard_group
+#define bfd_pef_bfd_match_symbols_in_sections	    _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_pef_bfd_link_hash_table_create          _bfd_generic_link_hash_table_create
 #define bfd_pef_bfd_link_hash_table_free            _bfd_generic_link_hash_table_free
 #define bfd_pef_bfd_link_add_symbols                _bfd_generic_link_add_symbols
--- binutils/bfd/ppcboot.c.linkonce	2004-04-30 08:27:02.000000000 -0700
+++ binutils/bfd/ppcboot.c	2004-05-17 09:44:21.000000000 -0700
@@ -471,6 +471,8 @@ ppcboot_bfd_print_private_bfd_data (abfd
 #define ppcboot_bfd_merge_sections bfd_generic_merge_sections
 #define ppcboot_bfd_is_group_section bfd_generic_is_group_section
 #define ppcboot_bfd_discard_group bfd_generic_discard_group
+#define ppcboot_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/section.c.linkonce	2004-05-17 07:37:17.000000000 -0700
+++ binutils/bfd/section.c	2004-05-17 07:38:59.000000000 -0700
@@ -493,6 +493,10 @@ CODE_FRAGMENT
 .  {* Optional information about a COMDAT entry; NULL if not COMDAT.  *}
 .  struct bfd_comdat_info *comdat;
 .
+.  {* Optional information about section group; NULL if it doesn't
+.     belongs to any section group.  *}
+.  struct bfd_section *group;
+.
 .  {* Points to the kept section if this section is a link-once section,
 .     and is discarded.  *}
 .  struct bfd_section *kept_section;
@@ -643,8 +647,8 @@ static const asymbol global_syms[] =
     /* line_filepos, userdata, contents, lineno, lineno_count,       */	\
        0,            NULL,     NULL,     NULL,   0,			\
 									\
-    /* entsize, comdat, kept_section, moving_line_filepos,           */	\
-       0,       NULL,   NULL,	      0,				\
+    /* entsize, comdat, group, kept_section, moving_line_filepos,    */	\
+       0,       NULL,   NULL,  NULL,	      0,			\
 									\
     /* target_index, used_by_bfd, constructor_chain, owner,          */	\
        0,            NULL,        NULL,              NULL,		\
--- binutils/bfd/som.c.linkonce	2004-05-03 09:00:44.000000000 -0700
+++ binutils/bfd/som.c	2004-05-17 09:44:25.000000000 -0700
@@ -6412,6 +6412,8 @@ som_bfd_link_split_section (abfd, sec)
 #define som_bfd_merge_sections		bfd_generic_merge_sections
 #define som_bfd_is_group_section	bfd_generic_is_group_section
 #define som_bfd_discard_group		bfd_generic_discard_group
+#define som_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target som_vec = {
   "som",			/* name */
--- binutils/bfd/srec.c.linkonce	2004-04-30 08:27:15.000000000 -0700
+++ binutils/bfd/srec.c	2004-05-17 09:44:33.000000000 -0700
@@ -1286,6 +1286,8 @@ srec_print_symbol (abfd, afile, symbol, 
 #define srec_bfd_merge_sections bfd_generic_merge_sections
 #define srec_bfd_is_group_section bfd_generic_is_group_section
 #define srec_bfd_discard_group bfd_generic_discard_group
+#define srec_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define srec_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define srec_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define srec_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/targets.c.linkonce	2004-05-14 09:00:50.000000000 -0700
+++ binutils/bfd/targets.c	2004-05-17 09:44:55.000000000 -0700
@@ -407,7 +407,8 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_gc_sections, \
 .  NAME##_bfd_merge_sections, \
 .  NAME##_bfd_is_group_section, \
-.  NAME##_bfd_discard_group
+.  NAME##_bfd_discard_group, \
+.  NAME##_bfd_match_symbols_in_sections \
 .
 .  int         (*_bfd_sizeof_headers) (bfd *, bfd_boolean);
 .  bfd_byte *  (*_bfd_get_relocated_section_contents)
@@ -450,6 +451,11 @@ BFD_JUMP_TABLE macros.
 .  {* Discard members of a group.  *}
 .  bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *);
 .
+.  {* Return TRUE if need to try if 2 sections export the same set of
+.     symbols.  *}
+.  bfd_boolean (*_bfd_match_symbols_in_sections)
+.    (struct bfd_section *, struct bfd_section *);
+.
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
 .  NAME##_get_dynamic_symtab_upper_bound, \
--- binutils/bfd/tekhex.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/tekhex.c	2004-05-17 09:45:02.000000000 -0700
@@ -1003,6 +1003,8 @@ tekhex_print_symbol (abfd, filep, symbol
 #define tekhex_bfd_merge_sections bfd_generic_merge_sections
 #define tekhex_bfd_is_group_section bfd_generic_is_group_section
 #define tekhex_bfd_discard_group bfd_generic_discard_group
+#define tekhex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define tekhex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define tekhex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define tekhex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/versados.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/versados.c	2004-05-17 09:45:10.000000000 -0700
@@ -874,6 +874,8 @@ versados_canonicalize_reloc (abfd, secti
 #define versados_bfd_merge_sections bfd_generic_merge_sections
 #define versados_bfd_is_group_section bfd_generic_is_group_section
 #define versados_bfd_discard_group bfd_generic_discard_group
+#define versados_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define versados_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define versados_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define versados_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/vms.c.linkonce	2004-04-30 08:27:20.000000000 -0700
+++ binutils/bfd/vms.c	2004-05-17 13:35:00.000000000 -0700
@@ -168,6 +168,8 @@ static bfd_boolean vms_bfd_set_private_f
 #define vms_bfd_link_just_syms _bfd_generic_link_just_syms
 #define vms_bfd_is_group_section bfd_generic_is_group_section
 #define vms_bfd_discard_group bfd_generic_discard_group
+#define vms_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 \f
 /*===========================================================================*/
 
--- binutils/bfd/xcoff-target.h.linkonce	2002-12-04 09:03:10.000000000 -0800
+++ binutils/bfd/xcoff-target.h	2004-05-17 09:45:17.000000000 -0700
@@ -99,6 +99,8 @@ extern int lynx_core_file_failing_signal
 #define _bfd_xcoff_bfd_gc_sections coff_bfd_gc_sections
 #define _bfd_xcoff_bfd_merge_sections coff_bfd_merge_sections
 #define _bfd_xcoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_xcoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define _bfd_xcoff_bfd_link_split_section coff_bfd_link_split_section
 
 /* XCOFF archives do not have anything which corresponds to an
--- binutils/bfd/xsym.c.linkonce	2004-04-30 08:27:22.000000000 -0700
+++ binutils/bfd/xsym.c	2004-05-17 09:45:22.000000000 -0700
@@ -44,6 +44,8 @@
 #define bfd_sym_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_sym_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_sym_bfd_discard_group bfd_generic_discard_group
+#define bfd_sym_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_sym_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define bfd_sym_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define bfd_sym_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/ld/ldlang.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/ld/ldlang.c	2004-05-17 17:47:45.000000000 -0700
@@ -853,6 +853,36 @@ struct already_linked
   asection *sec;
 };
 
+struct already_linked_section
+{
+  asection *sec;
+  asection *linked;
+};
+
+static bfd_boolean
+try_match_symbols_in_sections (struct already_linked_hash_entry *h,
+			       void *info)
+{
+  struct already_linked *l;
+  struct already_linked_section *s
+    = (struct already_linked_section *) info;
+
+  /* No need to check group section.  */
+  if ((s->sec->flags & SEC_GROUP) != 0)
+    return TRUE;
+
+  for (l = h->entry; l != NULL; l = l->next)
+    if ((l->sec->flags & SEC_GROUP) == 0
+	&& bfd_match_symbols_in_sections (l->sec->owner,
+					  l->sec, s->sec))
+      {
+	s->linked = l->sec;
+	return FALSE;
+      }
+
+  return TRUE;
+}
+
 /* The hash table.  */
 
 static struct bfd_hash_table already_linked_table;
@@ -865,6 +895,8 @@ section_already_linked (bfd *abfd, asect
   const char *name;
   struct already_linked *l;
   struct already_linked_hash_entry *already_linked_list;
+  struct already_linked_section result;
+  asection *group;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
@@ -876,7 +908,12 @@ section_already_linked (bfd *abfd, asect
 
   flags = bfd_get_section_flags (abfd, sec);
 
-  if ((flags & SEC_LINK_ONCE) == 0)
+  /* Check if it belongs to a section group.  */
+  group = sec->group;
+
+  /* Return if it isn't a .gnu.linkonce section nor a member of a
+     group.  */
+  if ((flags & SEC_LINK_ONCE) == 0 && group == NULL)
     return;
 
   /* FIXME: When doing a relocatable link, we may have trouble
@@ -902,6 +939,18 @@ section_already_linked (bfd *abfd, asect
     ((struct already_linked_hash_entry *)
      bfd_hash_lookup (&already_linked_table, name, TRUE, FALSE));
 
+  if ((flags & SEC_LINK_ONCE) == 0)
+    {
+      /* If this is a member of a COMDAT group, go straight to comdat
+	 and .gnu.linkonce check. Otherwise, just return.   */
+      if ((group->flags
+	   & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	  == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	goto comdat;
+      else
+	return;
+    }
+
   for (l = already_linked_list->entry; l != NULL; l = l->next)
     {
       if (sec->comdat == NULL
@@ -957,6 +1006,26 @@ section_already_linked (bfd *abfd, asect
 	}
     }
 
+comdat:
+  /* When we get here, we must be either a group member or a
+     .gnu.linkonce section. Check if a member of a COMDAT group
+     matches a .gnu.linkonce section and vice versa.  */
+  result.sec = sec;
+  result.linked = NULL;
+  bfd_hash_traverse (&already_linked_table,
+		     (bfd_boolean (*) (struct bfd_hash_entry *, void *))
+		     try_match_symbols_in_sections,
+		     &result);
+  if (result.linked)
+    {
+      sec->output_section = bfd_abs_section_ptr;
+      sec->kept_section = result.linked;
+
+      /* Also discard the group section.  */
+      if (group != NULL)
+	group->output_section = bfd_abs_section_ptr;
+    }
+
   /* This is the first section with this name.  Record it.  Allocate
      the memory from the same obstack as the hash table is kept in.  */
 

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-18  1:06     ` H. J. Lu
@ 2004-05-18  2:13       ` H. J. Lu
  2004-05-18  3:41       ` H. J. Lu
  1 sibling, 0 replies; 10+ messages in thread
From: H. J. Lu @ 2004-05-18  2:13 UTC (permalink / raw)
  To: binutils

On Mon, May 17, 2004 at 06:06:05PM -0700, H. J. Lu wrote:
> 
> I think there may be a bug in ldlang.c:section_already_linked. We
> check section names for linkonce sections. But for group sections,
> section names are meaningless. Compilers/assemblers don't have to
> use group signature for section name.  Shouldn't we check for group
> signatures instead?

Never mind. Group signature is used for name of group section.


H.J.

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-18  1:06     ` H. J. Lu
  2004-05-18  2:13       ` H. J. Lu
@ 2004-05-18  3:41       ` H. J. Lu
  2004-05-18 16:38         ` H. J. Lu
  1 sibling, 1 reply; 10+ messages in thread
From: H. J. Lu @ 2004-05-18  3:41 UTC (permalink / raw)
  To: binutils

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

On Mon, May 17, 2004 at 06:06:05PM -0700, H. J. Lu wrote:
> On Mon, May 17, 2004 at 02:24:23PM -0700, H. J. Lu wrote:
> > On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> > > On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > > > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > > > them gracefully:
> > > 
> > > Ewww.  Should we even try?  I understand that such a patch might be
> > > useful while gcc is emitting both comdat and linkonce, but once you've
> > > completed the change to comdat it shouldn't be necessary.  Also, I'm not
> > > really happy with where you have added this code.  At least, it is the
> > > wrong place to be discarding duplicate sections.  That ought to happen
> > > in ldlang.c:section_already_linked.
> > > 
> > 
> > This patch implements it.
> > 
> > 
> 
> Here is an update. I should skip checking members of section groups
> for already linked section.
> 

Another update. Should I skip COMDAT group members on the already
linked list? It may happen if a COMDAT group member has the same
section name as a linkonce section.


H.J.

[-- Attachment #2: bfd-comdat-linkonce-4.patch --]
[-- Type: text/plain, Size: 36807 bytes --]

bfd/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* aout-adobe.c (aout_32_bfd_match_symbols_in_sections): Defined.
	* aout-target.h (MY_bfd_match_symbols_in_sections): Likewise.
	* aout-tic30.c (MY_bfd_match_symbols_in_sections): Likewise.
	* binary.c (binary_bfd_match_symbols_in_sections): Likewise.
	* bout.c (b_out_bfd_match_symbols_in_sections): Likewise.
	* coff-alpha.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coff-mips.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coffcode.h (coff_bfd_match_symbols_in_sections): Likewise.
	* i386msdos.c (msdos_bfd_match_symbols_in_sections): Likewise.
	* i386os9k.c (os9k_bfd_match_symbols_in_sections): Likewise.
	* ieee.c (ieee_bfd_match_symbols_in_sections): Likewise.
	* ihex.c (ihex_bfd_match_symbols_in_sections): Likewise.
	* mach-o.c (bfd_mach_o_bfd_match_symbols_in_sections): Likewise.
	* mmo.c (mmo_bfd_discard_group): Likewise.
	* nlm-target.h (nlm_bfd_match_symbols_in_sections): Likewise.
	* oasys.c (oasys_bfd_match_symbols_in_sections): Likewise.
	* pef.c (bfd_pef_bfd_match_symbols_in_sections): Likewise.
	* ppcboot.c (ppcboot_bfd_match_symbols_in_sections): Likewise.
	* som.c (som_bfd_discard_group): Likewise.
	* srec.c (srec_bfd_match_symbols_in_sections): Likewise.
	* tekhex.c (tekhex_bfd_match_symbols_in_sections): Likewise.
	* versados.c (versados_bfd_match_symbols_in_sections): Likewise.
	* vms.c (vms_bfd_match_symbols_in_sections): Likewise.
	* coff-target.h (_bfd_xcoff_bfd_match_symbols_in_sections): Likewise.
	* xsym.c (bfd_sym_bfd_match_symbols_in_sections): Likewise.

	* bfd.c (bfd_match_symbols_in_sections): New.

	* coff-rs6000.c (rs6000coff_vec): Add
	_bfd_nolink_bfd_match_symbols_in_sections,
	(pmac_xcoff_vec): Likewise.
	* coff64-rs6000.c (rs6000coff64_vec): Likewise.
	(aix5coff64_vec): Likewise.

	* elf-bfd.h (bfd_elf_match_symbols_in_sections): New prototype.
	(_bfd_elf_setup_group_pointers): Likewise.

	* elf.c (_bfd_elf_setup_group_pointers): New function.
	(elf_sort_elf_symbol): Likewise.
	(elf_sym_name_compare): Likewise.
	(bfd_elf_match_symbols_in_sections): Likewise.

	* elfcode.h (elf_object_p): Call _bfd_elf_setup_group_pointers.

	* elfxx-target.h (bfd_elfNN_bfd_match_symbols_in_sections): Defined.

	* libbfd-in.h (_bfd_nolink_bfd_match_symbols_in_sections): Defined/

	* section.c (bfd_section): Add group, a pointer to section group.

	* section (STD_SECTION): Initialize group to NULL.
	* ecoff.c (bfd_debug_section): Likewise.

	* targets.c (bfd_target): Add _bfd_match_symbols_in_sections.
	(BFD_JUMP_TABLE_LINK): Updated.

	* bfd-in2.h: Regenerated.
	* libbfd.h: Regenerated.

ld/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* ldlang.c (already_linked_section): New structure.
	(try_match_symbols_in_sections): New function.
	(section_already_linked): Check if a member of a COMDAT group
	matches a .gnu.linkonce section.

--- binutils/bfd/aout-adobe.c.linkonce	2004-04-30 08:25:52.000000000 -0700
+++ binutils/bfd/aout-adobe.c	2004-05-17 13:27:55.000000000 -0700
@@ -519,6 +519,8 @@ aout_adobe_sizeof_headers (ignore_abfd, 
 #define aout_32_bfd_merge_sections	bfd_generic_merge_sections
 #define aout_32_bfd_is_group_section	bfd_generic_is_group_section
 #define aout_32_bfd_discard_group	bfd_generic_discard_group
+#define aout_32_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define aout_32_bfd_link_hash_table_create \
   _bfd_generic_link_hash_table_create
 #define aout_32_bfd_link_hash_table_free \
--- binutils/bfd/aout-target.h.linkonce	2004-04-30 08:25:53.000000000 -0700
+++ binutils/bfd/aout-target.h	2004-05-17 09:41:08.000000000 -0700
@@ -519,6 +519,10 @@ MY_bfd_final_link (abfd, info)
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup NAME(aout,reloc_type_lookup)
 #endif
--- binutils/bfd/aout-tic30.c.linkonce	2004-04-30 08:25:54.000000000 -0700
+++ binutils/bfd/aout-tic30.c	2004-05-17 09:41:32.000000000 -0700
@@ -976,6 +976,10 @@ tic30_aout_set_arch_mach (abfd, arch, ma
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup tic30_aout_reloc_type_lookup
 #endif
--- binutils/bfd/bfd.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/bfd/bfd.c	2004-05-17 09:40:43.000000000 -0700
@@ -1089,6 +1089,9 @@ DESCRIPTION
 .#define bfd_discard_group(abfd, sec) \
 .	BFD_SEND (abfd, _bfd_discard_group, (abfd, sec))
 .
+.#define bfd_match_symbols_in_sections(abfd, sec1, sec2) \
+.	BFD_SEND (abfd, _bfd_match_symbols_in_sections, (sec1, sec2))
+.
 .#define bfd_link_hash_table_create(abfd) \
 .	BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd))
 .
--- binutils/bfd/binary.c.linkonce	2004-04-30 08:26:01.000000000 -0700
+++ binutils/bfd/binary.c	2004-05-17 09:41:47.000000000 -0700
@@ -341,6 +341,8 @@ binary_sizeof_headers (abfd, exec)
 #define binary_bfd_merge_sections bfd_generic_merge_sections
 #define binary_bfd_is_group_section bfd_generic_is_group_section
 #define binary_bfd_discard_group bfd_generic_discard_group
+#define binary_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define binary_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define binary_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define binary_bfd_link_just_syms _bfd_generic_link_just_syms
--- binutils/bfd/bout.c.linkonce	2004-04-30 08:26:02.000000000 -0700
+++ binutils/bfd/bout.c	2004-05-17 13:28:43.000000000 -0700
@@ -1489,6 +1489,8 @@ b_out_bfd_get_relocated_section_contents
 #define b_out_bfd_merge_sections  bfd_generic_merge_sections
 #define b_out_bfd_is_group_section bfd_generic_is_group_section
 #define b_out_bfd_discard_group bfd_generic_discard_group
+#define b_out_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 #define aout_32_get_section_contents_in_window \
   _bfd_generic_get_section_contents_in_window
--- binutils/bfd/coff-alpha.c.linkonce	2004-04-30 08:26:04.000000000 -0700
+++ binutils/bfd/coff-alpha.c	2004-05-17 09:41:52.000000000 -0700
@@ -2361,6 +2361,8 @@ static const struct ecoff_backend_data a
 #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target ecoffalpha_little_vec =
 {
--- binutils/bfd/coff-mips.c.linkonce	2004-04-30 08:26:06.000000000 -0700
+++ binutils/bfd/coff-mips.c	2004-05-17 09:42:02.000000000 -0700
@@ -1395,6 +1395,8 @@ static const struct ecoff_backend_data m
 
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 extern const bfd_target ecoff_big_vec;
 
--- binutils/bfd/coff-rs6000.c.linkonce	2004-04-30 08:26:10.000000000 -0700
+++ binutils/bfd/coff-rs6000.c	2004-05-17 09:42:06.000000000 -0700
@@ -4198,6 +4198,7 @@ const bfd_target rs6000coff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -4442,6 +4443,7 @@ const bfd_target pmac_xcoff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coff64-rs6000.c.linkonce	2004-04-30 08:26:13.000000000 -0700
+++ binutils/bfd/coff64-rs6000.c	2004-05-17 09:42:18.000000000 -0700
@@ -2739,6 +2739,7 @@ const bfd_target rs6000coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -2984,6 +2985,7 @@ const bfd_target aix5coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coffcode.h.linkonce	2004-05-07 13:21:00.000000000 -0700
+++ binutils/bfd/coffcode.h	2004-05-17 09:42:24.000000000 -0700
@@ -5574,6 +5574,11 @@ static const bfd_coff_backend_data ticof
 #define coff_bfd_discard_group		    bfd_generic_discard_group
 #endif
 
+#ifndef coff_bfd_match_symbols_in_sections
+#define coff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
+
 #define CREATE_BIG_COFF_TARGET_VEC(VAR, NAME, EXTRA_O_FLAGS, EXTRA_S_FLAGS, UNDER, ALTERNATIVE, SWAP_TABLE)	\
 const bfd_target VAR =							\
 {									\
--- binutils/bfd/ecoff.c.linkonce	2004-04-26 21:11:29.000000000 -0700
+++ binutils/bfd/ecoff.c	2004-05-17 13:30:52.000000000 -0700
@@ -95,8 +95,8 @@ static asection bfd_debug_section =
      NULL,       NULL,        0,           0,       0,
   /* line_filepos, userdata, contents, lineno, lineno_count,       */
      0,            NULL,     NULL,     NULL,   0,
-  /* entsize, comdat, kept_section, moving_line_filepos,           */
-     0,       NULL,   NULL,         0,
+  /* entsize, comdat, group, kept_section, moving_line_filepos,    */
+     0,       NULL,   NULL,  NULL,         0,
   /* target_index, used_by_bfd, constructor_chain, owner,          */
      0,            NULL,        NULL,              NULL,
   /* symbol,                                                       */
--- binutils/bfd/elf-bfd.h.linkonce	2004-05-11 13:33:49.000000000 -0700
+++ binutils/bfd/elf-bfd.h	2004-05-17 12:32:04.000000000 -0700
@@ -1375,6 +1375,8 @@ extern bfd_boolean bfd_elf_is_group_sect
   (bfd *, const struct bfd_section *);
 extern bfd_boolean bfd_elf_discard_group
   (bfd *, struct bfd_section *);
+extern bfd_boolean bfd_elf_match_symbols_in_sections
+  (struct bfd_section *, struct bfd_section *);
 extern void bfd_elf_set_group_contents
   (bfd *, asection *, void *);
 extern void _bfd_elf_link_just_syms
@@ -1554,6 +1556,9 @@ extern bfd_boolean _bfd_elf_dynamic_symb
 extern bfd_boolean _bfd_elf_symbol_refs_local_p
   (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
 
+extern void _bfd_elf_setup_group_pointers
+  (bfd *);
+
 extern const bfd_target *bfd_elf32_object_p
   (bfd *);
 extern const bfd_target *bfd_elf32_core_file_p
--- binutils/bfd/elf.c.linkonce	2004-05-11 13:33:56.000000000 -0700
+++ binutils/bfd/elf.c	2004-05-17 14:14:58.000000000 -0700
@@ -612,6 +612,26 @@ setup_group (bfd *abfd, Elf_Internal_Shd
   return TRUE;
 }
 
+void
+_bfd_elf_setup_group_pointers (bfd *abfd)
+{
+  unsigned int i;
+  unsigned int num_group = elf_tdata (abfd)->num_group;
+
+  if (num_group == (unsigned) -1)
+    return;
+
+  for (i = 0; i < num_group; i++)
+    {
+      Elf_Internal_Shdr *shdr = elf_tdata (abfd)->group_sect_ptr[i];
+      Elf_Internal_Group *idx = (Elf_Internal_Group *) shdr->contents;
+      unsigned int n_elt = shdr->sh_size / 4;
+
+      while (--n_elt != 0)
+	(++idx)->shdr->bfd_section->group = shdr->bfd_section;
+    }
+}
+
 bfd_boolean
 bfd_elf_is_group_section (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec)
 {
@@ -7678,3 +7698,240 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd
 
   return n;
 }
+
+/* Sort symbol by binding and section. We want to put global
+   symbols sorted by section at the beginning.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1;
+  const Elf_Internal_Sym *s2;
+
+  /* Make sure local or undefined symbols are at the end.  */
+  s1 = (const Elf_Internal_Sym *) arg1;
+  if (s1->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s1->st_info) == STB_LOCAL)
+    return 1;
+  s2 = (const Elf_Internal_Sym *) arg2;
+  if (s2->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s2->st_info) == STB_LOCAL)
+    return -1;
+
+  /* Grouped by section index.  */
+  return s1->st_shndx - s2->st_shndx;
+}
+
+struct elf_symbol
+{
+  Elf_Internal_Sym *sym;
+  const char *name;
+};
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+  const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+  const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+  return strcmp (s1->name, s2->name);
+}
+
+/* Check if 2 sections export the same set of symbols.  */
+
+bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+{
+  bfd *bfd1, *bfd2;
+  const struct elf_backend_data *bed1, *bed2;
+  Elf_Internal_Shdr *hdr1, *hdr2;
+  bfd_size_type symcount1, symcount2;
+  bfd_size_type extsymcount1, extsymcount2;
+  bfd_size_type extsymoff1, extsymoff2;
+  Elf_Internal_Sym *isymbuf1, *isymbuf2;
+  Elf_Internal_Sym *isymstart1 = NULL, *isymstart2 = NULL, *isym;
+  Elf_Internal_Sym *isymend;
+  struct elf_symbol *symp, *symtable1 = NULL, *symtable2 = NULL;
+  bfd_size_type count1, count2, i;
+  int shndx1, shndx2;
+  bfd_boolean result;
+
+  bfd1 = sec1->owner;
+  bfd2 = sec2->owner;
+
+  /* Both sections have to be in ELF.  */
+  if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+      || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+    return FALSE;
+
+  /* One section has to be in a COMDAT group with only one section and
+     the other one has to be a .gnu.linkonce section.  */
+  if (!(elf_section_type (sec1) == elf_section_type (sec2)
+	&& ((elf_section_flags (sec1) & ~SHF_GROUP)
+	    == (elf_section_flags (sec2) & ~SHF_GROUP))
+	&& ((sec1->group != NULL
+	     && ((sec1->group->flags & (SEC_LINK_ONCE
+					| SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	     && elf_next_in_group (sec1) == sec1
+	     && sec2->group == NULL
+	     && ((sec2->flags & (SEC_LINK_ONCE
+				 | SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD)))
+	    || (sec1->group == NULL
+		&& sec2->group != NULL
+		&& ((sec2->group->flags & (SEC_LINK_ONCE
+					   | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+		&& elf_next_in_group (sec2) == sec2
+		&& ((sec1->flags & (SEC_LINK_ONCE
+				    | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))))))
+    return FALSE;
+
+  shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+  shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+  if (shndx1 == -1 || shndx2 == -1)
+    return FALSE;
+
+  bed1 = get_elf_backend_data (bfd1);
+  bed2 = get_elf_backend_data (bfd2);
+  hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+  symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+  hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+  symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+  if (elf_bad_symtab (bfd1))
+    {
+      extsymcount1 = symcount1;
+      extsymoff1 = 0;
+    }
+  else
+    {
+      extsymcount1 = symcount1 - hdr1->sh_info;
+      extsymoff1 = hdr1->sh_info;
+    }
+
+  if (elf_bad_symtab (bfd2))
+    {
+      extsymcount2 = symcount2;
+      extsymoff2 = 0;
+    }
+  else
+    {
+      extsymcount2 = symcount2 - hdr2->sh_info;
+      extsymoff2 = hdr2->sh_info;
+    }
+
+  if (extsymcount1 == 0 || extsymcount2 == 0)
+    return FALSE;
+
+  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, extsymcount1,
+				     extsymoff1, NULL, NULL, NULL);
+  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, extsymcount2,
+				     extsymoff2, NULL, NULL, NULL);
+  if (isymbuf1 == NULL || isymbuf2 == NULL)
+    return FALSE;
+
+  /* Sort symbols by binding and section. Global definitions are at
+     the beginning.  */
+  qsort (isymbuf1, extsymcount1, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+  qsort (isymbuf2, extsymcount2, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+
+  /* Count global symbols defined in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + extsymcount1;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx1)
+	{
+	  if (count1 == 0)
+	    isymstart1 = isym;
+	  count1++;
+	}
+
+      if (count1 && isym->st_shndx != (unsigned int) shndx1)
+	break;
+    }
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + extsymcount2;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx2)
+	{
+	  if (count2 == 0)
+	    isymstart2 = isym;
+	  count2++;
+	}
+
+      if (count2 && isym->st_shndx != (unsigned int) shndx2)
+	break;
+    }
+
+  if (count1 == 0 && count2 == 0)
+    {
+      result = TRUE;
+      goto done;
+    }
+
+  result = FALSE;
+  if (count1 != count2)
+    goto done;
+
+  symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+
+  if (symtable1 == NULL || symtable2 == NULL)
+    goto done;
+
+  symp = symtable1;
+  for (isym = isymstart1, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd1,
+						    hdr1->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+ 
+  symp = symtable2;
+  for (isym = isymstart2, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd2,
+						    hdr2->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+  
+  /* Sort symbol by name.  */
+  qsort (symtable1, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+  qsort (symtable2, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+
+  for (i = 0; i < count1; i++)
+    /* Two symbols must have the same binding, type and name.  */
+    if (symtable1 [i].sym->st_info != symtable2 [i].sym->st_info
+	|| symtable1 [i].sym->st_other != symtable2 [i].sym->st_other
+	|| strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+      goto done;
+
+  result = TRUE;
+
+done:
+  if (symtable1)
+    free (symtable1);
+  if (symtable2)
+    free (symtable2);
+  if (isymbuf1)
+    free (isymbuf1);
+  if (isymbuf2)
+    free (isymbuf2);
+
+  return result;
+}
--- binutils/bfd/elfcode.h.linkonce	2004-04-22 08:20:00.000000000 -0700
+++ binutils/bfd/elfcode.h	2004-05-17 12:30:41.000000000 -0700
@@ -742,6 +742,9 @@ elf_object_p (bfd *abfd)
 	  if (shindex == SHN_LORESERVE - 1)
 	    shindex += SHN_HIRESERVE + 1 - SHN_LORESERVE;
 	}
+
+      /* Set up group pointers.  */
+      _bfd_elf_setup_group_pointers (abfd);
     }
 
   /* Let the backend double check the format and override global
--- binutils/bfd/elfxx-target.h.linkonce	2004-04-30 08:26:46.000000000 -0700
+++ binutils/bfd/elfxx-target.h	2004-05-17 09:42:45.000000000 -0700
@@ -144,6 +144,11 @@
 #define bfd_elfNN_bfd_discard_group bfd_elf_discard_group
 #endif
 
+#ifndef bfd_elfNN_bfd_match_symbols_in_sections
+#define bfd_elfNN_bfd_match_symbols_in_sections \
+  bfd_elf_match_symbols_in_sections
+#endif
+
 #ifndef bfd_elfNN_bfd_make_debug_symbol
 #define bfd_elfNN_bfd_make_debug_symbol \
   ((asymbol * (*) (bfd *, void *, unsigned long)) bfd_nullvoidptr)
--- binutils/bfd/i386msdos.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386msdos.c	2004-05-17 09:42:50.000000000 -0700
@@ -178,6 +178,8 @@ msdos_set_section_contents (abfd, sectio
 #define msdos_bfd_merge_sections bfd_generic_merge_sections
 #define msdos_bfd_is_group_section bfd_generic_is_group_section
 #define msdos_bfd_discard_group bfd_generic_discard_group
+#define msdos_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define msdos_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define msdos_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define msdos_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/i386os9k.c.linkonce	2004-04-30 08:26:47.000000000 -0700
+++ binutils/bfd/i386os9k.c	2004-05-17 13:33:11.000000000 -0700
@@ -335,6 +335,8 @@ os9k_sizeof_headers (ignore_abfd, ignore
 #define os9k_bfd_merge_sections bfd_generic_merge_sections
 #define os9k_bfd_is_group_section bfd_generic_is_group_section
 #define os9k_bfd_discard_group bfd_generic_discard_group
+#define os9k_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define os9k_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define os9k_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define os9k_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ieee.c.linkonce	2004-04-30 08:26:50.000000000 -0700
+++ binutils/bfd/ieee.c	2004-05-17 09:42:58.000000000 -0700
@@ -4039,6 +4039,8 @@ ieee_bfd_debug_info_accumulate (abfd, se
 #define ieee_bfd_merge_sections bfd_generic_merge_sections
 #define ieee_bfd_is_group_section bfd_generic_is_group_section
 #define ieee_bfd_discard_group bfd_generic_discard_group
+#define ieee_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ieee_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ieee_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ieee_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ihex.c.linkonce	2004-04-30 08:26:51.000000000 -0700
+++ binutils/bfd/ihex.c	2004-05-17 09:43:03.000000000 -0700
@@ -990,6 +990,8 @@ ihex_sizeof_headers (abfd, exec)
 #define ihex_bfd_merge_sections bfd_generic_merge_sections
 #define ihex_bfd_is_group_section bfd_generic_is_group_section
 #define ihex_bfd_discard_group bfd_generic_discard_group
+#define ihex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ihex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ihex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ihex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/libbfd-in.h.linkonce	2004-04-30 08:26:52.000000000 -0700
+++ binutils/bfd/libbfd-in.h	2004-05-17 09:43:08.000000000 -0700
@@ -361,6 +361,9 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_discard_group \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) \
    bfd_false)
+#define _bfd_nolink_bfd_match_symbols_in_sections \
+  ((bfd_boolean (*) (struct bfd_section *, struct bfd_section *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_link_hash_table_create \
   ((struct bfd_link_hash_table *(*) (bfd *)) bfd_nullvoidptr)
 #define _bfd_nolink_bfd_link_hash_table_free \
--- binutils/bfd/mach-o.c.linkonce	2004-04-30 08:26:56.000000000 -0700
+++ binutils/bfd/mach-o.c	2004-05-17 09:43:35.000000000 -0700
@@ -70,6 +70,8 @@
 #define bfd_mach_o_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_mach_o_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_mach_o_bfd_discard_group bfd_generic_discard_group
+#define bfd_mach_o_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 static bfd_boolean bfd_mach_o_bfd_copy_private_symbol_data
   PARAMS ((bfd *, asymbol *, bfd *, asymbol *));
--- binutils/bfd/mmo.c.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/mmo.c	2004-05-17 09:43:40.000000000 -0700
@@ -3288,6 +3288,8 @@ mmo_canonicalize_reloc (abfd, section, r
 #define mmo_bfd_merge_sections bfd_generic_merge_sections
 #define mmo_bfd_is_group_section bfd_generic_is_group_section
 #define mmo_bfd_discard_group bfd_generic_discard_group
+#define mmo_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 /* objcopy will be upset if we return -1 from bfd_get_reloc_upper_bound by
    using BFD_JUMP_TABLE_RELOCS (_bfd_norelocs) rather than 0.  FIXME: Most
--- binutils/bfd/nlm-target.h.linkonce	2004-04-30 08:26:59.000000000 -0700
+++ binutils/bfd/nlm-target.h	2004-05-17 09:43:45.000000000 -0700
@@ -46,6 +46,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define nlm_bfd_merge_sections bfd_generic_merge_sections
 #define nlm_bfd_is_group_section bfd_generic_is_group_section
 #define nlm_bfd_discard_group bfd_generic_discard_group
+#define nlm_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define nlm_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define nlm_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define nlm_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/oasys.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/oasys.c	2004-05-17 09:43:49.000000000 -0700
@@ -1508,6 +1508,8 @@ oasys_sizeof_headers (abfd, exec)
 #define oasys_bfd_merge_sections bfd_generic_merge_sections
 #define oasys_bfd_is_group_section bfd_generic_is_group_section
 #define oasys_bfd_discard_group bfd_generic_discard_group
+#define oasys_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define oasys_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define oasys_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define oasys_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/pef.c.linkonce	2004-04-30 08:27:01.000000000 -0700
+++ binutils/bfd/pef.c	2004-05-17 13:33:50.000000000 -0700
@@ -54,6 +54,7 @@
 #define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
 #define bfd_pef_bfd_discard_group                   bfd_generic_discard_group
+#define bfd_pef_bfd_match_symbols_in_sections	    _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_pef_bfd_link_hash_table_create          _bfd_generic_link_hash_table_create
 #define bfd_pef_bfd_link_hash_table_free            _bfd_generic_link_hash_table_free
 #define bfd_pef_bfd_link_add_symbols                _bfd_generic_link_add_symbols
--- binutils/bfd/ppcboot.c.linkonce	2004-04-30 08:27:02.000000000 -0700
+++ binutils/bfd/ppcboot.c	2004-05-17 09:44:21.000000000 -0700
@@ -471,6 +471,8 @@ ppcboot_bfd_print_private_bfd_data (abfd
 #define ppcboot_bfd_merge_sections bfd_generic_merge_sections
 #define ppcboot_bfd_is_group_section bfd_generic_is_group_section
 #define ppcboot_bfd_discard_group bfd_generic_discard_group
+#define ppcboot_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/section.c.linkonce	2004-05-17 07:37:17.000000000 -0700
+++ binutils/bfd/section.c	2004-05-17 07:38:59.000000000 -0700
@@ -493,6 +493,10 @@ CODE_FRAGMENT
 .  {* Optional information about a COMDAT entry; NULL if not COMDAT.  *}
 .  struct bfd_comdat_info *comdat;
 .
+.  {* Optional information about section group; NULL if it doesn't
+.     belongs to any section group.  *}
+.  struct bfd_section *group;
+.
 .  {* Points to the kept section if this section is a link-once section,
 .     and is discarded.  *}
 .  struct bfd_section *kept_section;
@@ -643,8 +647,8 @@ static const asymbol global_syms[] =
     /* line_filepos, userdata, contents, lineno, lineno_count,       */	\
        0,            NULL,     NULL,     NULL,   0,			\
 									\
-    /* entsize, comdat, kept_section, moving_line_filepos,           */	\
-       0,       NULL,   NULL,	      0,				\
+    /* entsize, comdat, group, kept_section, moving_line_filepos,    */	\
+       0,       NULL,   NULL,  NULL,	      0,			\
 									\
     /* target_index, used_by_bfd, constructor_chain, owner,          */	\
        0,            NULL,        NULL,              NULL,		\
--- binutils/bfd/som.c.linkonce	2004-05-03 09:00:44.000000000 -0700
+++ binutils/bfd/som.c	2004-05-17 09:44:25.000000000 -0700
@@ -6412,6 +6412,8 @@ som_bfd_link_split_section (abfd, sec)
 #define som_bfd_merge_sections		bfd_generic_merge_sections
 #define som_bfd_is_group_section	bfd_generic_is_group_section
 #define som_bfd_discard_group		bfd_generic_discard_group
+#define som_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target som_vec = {
   "som",			/* name */
--- binutils/bfd/srec.c.linkonce	2004-04-30 08:27:15.000000000 -0700
+++ binutils/bfd/srec.c	2004-05-17 09:44:33.000000000 -0700
@@ -1286,6 +1286,8 @@ srec_print_symbol (abfd, afile, symbol, 
 #define srec_bfd_merge_sections bfd_generic_merge_sections
 #define srec_bfd_is_group_section bfd_generic_is_group_section
 #define srec_bfd_discard_group bfd_generic_discard_group
+#define srec_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define srec_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define srec_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define srec_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/targets.c.linkonce	2004-05-14 09:00:50.000000000 -0700
+++ binutils/bfd/targets.c	2004-05-17 09:44:55.000000000 -0700
@@ -407,7 +407,8 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_gc_sections, \
 .  NAME##_bfd_merge_sections, \
 .  NAME##_bfd_is_group_section, \
-.  NAME##_bfd_discard_group
+.  NAME##_bfd_discard_group, \
+.  NAME##_bfd_match_symbols_in_sections \
 .
 .  int         (*_bfd_sizeof_headers) (bfd *, bfd_boolean);
 .  bfd_byte *  (*_bfd_get_relocated_section_contents)
@@ -450,6 +451,11 @@ BFD_JUMP_TABLE macros.
 .  {* Discard members of a group.  *}
 .  bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *);
 .
+.  {* Return TRUE if need to try if 2 sections export the same set of
+.     symbols.  *}
+.  bfd_boolean (*_bfd_match_symbols_in_sections)
+.    (struct bfd_section *, struct bfd_section *);
+.
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
 .  NAME##_get_dynamic_symtab_upper_bound, \
--- binutils/bfd/tekhex.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/tekhex.c	2004-05-17 09:45:02.000000000 -0700
@@ -1003,6 +1003,8 @@ tekhex_print_symbol (abfd, filep, symbol
 #define tekhex_bfd_merge_sections bfd_generic_merge_sections
 #define tekhex_bfd_is_group_section bfd_generic_is_group_section
 #define tekhex_bfd_discard_group bfd_generic_discard_group
+#define tekhex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define tekhex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define tekhex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define tekhex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/versados.c.linkonce	2004-04-30 08:27:18.000000000 -0700
+++ binutils/bfd/versados.c	2004-05-17 09:45:10.000000000 -0700
@@ -874,6 +874,8 @@ versados_canonicalize_reloc (abfd, secti
 #define versados_bfd_merge_sections bfd_generic_merge_sections
 #define versados_bfd_is_group_section bfd_generic_is_group_section
 #define versados_bfd_discard_group bfd_generic_discard_group
+#define versados_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define versados_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define versados_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define versados_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/vms.c.linkonce	2004-04-30 08:27:20.000000000 -0700
+++ binutils/bfd/vms.c	2004-05-17 13:35:00.000000000 -0700
@@ -168,6 +168,8 @@ static bfd_boolean vms_bfd_set_private_f
 #define vms_bfd_link_just_syms _bfd_generic_link_just_syms
 #define vms_bfd_is_group_section bfd_generic_is_group_section
 #define vms_bfd_discard_group bfd_generic_discard_group
+#define vms_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 \f
 /*===========================================================================*/
 
--- binutils/bfd/xcoff-target.h.linkonce	2002-12-04 09:03:10.000000000 -0800
+++ binutils/bfd/xcoff-target.h	2004-05-17 09:45:17.000000000 -0700
@@ -99,6 +99,8 @@ extern int lynx_core_file_failing_signal
 #define _bfd_xcoff_bfd_gc_sections coff_bfd_gc_sections
 #define _bfd_xcoff_bfd_merge_sections coff_bfd_merge_sections
 #define _bfd_xcoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_xcoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define _bfd_xcoff_bfd_link_split_section coff_bfd_link_split_section
 
 /* XCOFF archives do not have anything which corresponds to an
--- binutils/bfd/xsym.c.linkonce	2004-04-30 08:27:22.000000000 -0700
+++ binutils/bfd/xsym.c	2004-05-17 09:45:22.000000000 -0700
@@ -44,6 +44,8 @@
 #define bfd_sym_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_sym_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_sym_bfd_discard_group bfd_generic_discard_group
+#define bfd_sym_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_sym_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define bfd_sym_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define bfd_sym_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/ld/ldlang.c.linkonce	2004-05-14 09:00:52.000000000 -0700
+++ binutils/ld/ldlang.c	2004-05-17 20:17:52.000000000 -0700
@@ -853,6 +853,36 @@ struct already_linked
   asection *sec;
 };
 
+struct already_linked_section
+{
+  asection *sec;
+  asection *linked;
+};
+
+static bfd_boolean
+try_match_symbols_in_sections (struct already_linked_hash_entry *h,
+			       void *info)
+{
+  struct already_linked *l;
+  struct already_linked_section *s
+    = (struct already_linked_section *) info;
+
+  /* No need to check group section.  */
+  if ((s->sec->flags & SEC_GROUP) != 0)
+    return TRUE;
+
+  for (l = h->entry; l != NULL; l = l->next)
+    if ((l->sec->flags & SEC_GROUP) == 0
+	&& bfd_match_symbols_in_sections (l->sec->owner,
+					  l->sec, s->sec))
+      {
+	s->linked = l->sec;
+	return FALSE;
+      }
+
+  return TRUE;
+}
+
 /* The hash table.  */
 
 static struct bfd_hash_table already_linked_table;
@@ -865,6 +895,8 @@ section_already_linked (bfd *abfd, asect
   const char *name;
   struct already_linked *l;
   struct already_linked_hash_entry *already_linked_list;
+  struct already_linked_section result;
+  asection *group;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
@@ -876,7 +908,12 @@ section_already_linked (bfd *abfd, asect
 
   flags = bfd_get_section_flags (abfd, sec);
 
-  if ((flags & SEC_LINK_ONCE) == 0)
+  /* Check if it belongs to a section group.  */
+  group = sec->group;
+
+  /* Return if it isn't a .gnu.linkonce section nor a member of a
+     group.  */
+  if ((flags & SEC_LINK_ONCE) == 0 && group == NULL)
     return;
 
   /* FIXME: When doing a relocatable link, we may have trouble
@@ -902,8 +939,24 @@ section_already_linked (bfd *abfd, asect
     ((struct already_linked_hash_entry *)
      bfd_hash_lookup (&already_linked_table, name, TRUE, FALSE));
 
+  if ((flags & SEC_LINK_ONCE) == 0)
+    {
+      /* If this is a member of a COMDAT group, go to comdat and
+	 .gnu.linkonce check.  */
+      if ((group->flags
+	   & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	  == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	goto comdat;
+      else
+	return;
+    }
+
   for (l = already_linked_list->entry; l != NULL; l = l->next)
     {
+      /* Don't check members of COMDAT groups.  */
+      if (l->group != NULL)
+	continue;
+
       if (sec->comdat == NULL
 	  || l->sec->comdat == NULL
 	  || strcmp (sec->comdat->name, l->sec->comdat->name) == 0)
@@ -957,6 +1010,26 @@ section_already_linked (bfd *abfd, asect
 	}
     }
 
+comdat:
+  /* When we get here, it must be either a member of a COMDAT group or a
+     .gnu.linkonce section. Check if a member of a COMDAT group matches
+     a .gnu.linkonce section and vice versa.  */
+  result.sec = sec;
+  result.linked = NULL;
+  bfd_hash_traverse (&already_linked_table,
+		     (bfd_boolean (*) (struct bfd_hash_entry *, void *))
+		     try_match_symbols_in_sections,
+		     &result);
+  if (result.linked)
+    {
+      sec->output_section = bfd_abs_section_ptr;
+      sec->kept_section = result.linked;
+
+      /* Also discard the group section.  */
+      if (group != NULL)
+	group->output_section = bfd_abs_section_ptr;
+    }
+
   /* This is the first section with this name.  Record it.  Allocate
      the memory from the same obstack as the hash table is kept in.  */
 

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-18  3:41       ` H. J. Lu
@ 2004-05-18 16:38         ` H. J. Lu
  2004-05-19  2:36           ` Alan Modra
  0 siblings, 1 reply; 10+ messages in thread
From: H. J. Lu @ 2004-05-18 16:38 UTC (permalink / raw)
  To: binutils

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

On Mon, May 17, 2004 at 08:41:50PM -0700, H. J. Lu wrote:
> On Mon, May 17, 2004 at 06:06:05PM -0700, H. J. Lu wrote:
> > On Mon, May 17, 2004 at 02:24:23PM -0700, H. J. Lu wrote:
> > > On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> > > > On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > > > > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > > > > them gracefully:
> > > > 
> > > > Ewww.  Should we even try?  I understand that such a patch might be
> > > > useful while gcc is emitting both comdat and linkonce, but once you've
> > > > completed the change to comdat it shouldn't be necessary.  Also, I'm not
> > > > really happy with where you have added this code.  At least, it is the
> > > > wrong place to be discarding duplicate sections.  That ought to happen
> > > > in ldlang.c:section_already_linked.
> > > > 
> > > 
> > > This patch implements it.
> > > 
> > > 
> > 
> > Here is an update. I should skip checking members of section groups
> > for already linked section.
> > 
> 
> Another update. Should I skip COMDAT group members on the already
> linked list? It may happen if a COMDAT group member has the same
> section name as a linkonce section.


I fixed a small memory leak.


H.J.

[-- Attachment #2: bfd-comdat-linkonce-6.patch --]
[-- Type: text/plain, Size: 36875 bytes --]

bfd/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* aout-adobe.c (aout_32_bfd_match_symbols_in_sections): Defined.
	* aout-target.h (MY_bfd_match_symbols_in_sections): Likewise.
	* aout-tic30.c (MY_bfd_match_symbols_in_sections): Likewise.
	* binary.c (binary_bfd_match_symbols_in_sections): Likewise.
	* bout.c (b_out_bfd_match_symbols_in_sections): Likewise.
	* coff-alpha.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coff-mips.c (_bfd_ecoff_bfd_match_symbols_in_sections): Likewise.
	* coffcode.h (coff_bfd_match_symbols_in_sections): Likewise.
	* i386msdos.c (msdos_bfd_match_symbols_in_sections): Likewise.
	* i386os9k.c (os9k_bfd_match_symbols_in_sections): Likewise.
	* ieee.c (ieee_bfd_match_symbols_in_sections): Likewise.
	* ihex.c (ihex_bfd_match_symbols_in_sections): Likewise.
	* mach-o.c (bfd_mach_o_bfd_match_symbols_in_sections): Likewise.
	* mmo.c (mmo_bfd_discard_group): Likewise.
	* nlm-target.h (nlm_bfd_match_symbols_in_sections): Likewise.
	* oasys.c (oasys_bfd_match_symbols_in_sections): Likewise.
	* pef.c (bfd_pef_bfd_match_symbols_in_sections): Likewise.
	* ppcboot.c (ppcboot_bfd_match_symbols_in_sections): Likewise.
	* som.c (som_bfd_discard_group): Likewise.
	* srec.c (srec_bfd_match_symbols_in_sections): Likewise.
	* tekhex.c (tekhex_bfd_match_symbols_in_sections): Likewise.
	* versados.c (versados_bfd_match_symbols_in_sections): Likewise.
	* vms.c (vms_bfd_match_symbols_in_sections): Likewise.
	* coff-target.h (_bfd_xcoff_bfd_match_symbols_in_sections): Likewise.
	* xsym.c (bfd_sym_bfd_match_symbols_in_sections): Likewise.

	* bfd.c (bfd_match_symbols_in_sections): New.

	* coff-rs6000.c (rs6000coff_vec): Add
	_bfd_nolink_bfd_match_symbols_in_sections,
	(pmac_xcoff_vec): Likewise.
	* coff64-rs6000.c (rs6000coff64_vec): Likewise.
	(aix5coff64_vec): Likewise.

	* elf-bfd.h (bfd_elf_match_symbols_in_sections): New prototype.
	(_bfd_elf_setup_group_pointers): Likewise.

	* elf.c (_bfd_elf_setup_group_pointers): New function.
	(elf_sort_elf_symbol): Likewise.
	(elf_sym_name_compare): Likewise.
	(bfd_elf_match_symbols_in_sections): Likewise.

	* elfcode.h (elf_object_p): Call _bfd_elf_setup_group_pointers.

	* elfxx-target.h (bfd_elfNN_bfd_match_symbols_in_sections): Defined.

	* libbfd-in.h (_bfd_nolink_bfd_match_symbols_in_sections): Defined/

	* section.c (bfd_section): Add group, a pointer to section group.

	* section (STD_SECTION): Initialize group to NULL.
	* ecoff.c (bfd_debug_section): Likewise.

	* targets.c (bfd_target): Add _bfd_match_symbols_in_sections.
	(BFD_JUMP_TABLE_LINK): Updated.

	* bfd-in2.h: Regenerated.
	* libbfd.h: Regenerated.

ld/

2004-05-17  H.J. Lu  <hongjiu.lu@intel.com>

	* ldlang.c (already_linked_section): New structure.
	(try_match_symbols_in_sections): New function.
	(section_already_linked): Check if a member of a COMDAT group
	matches a .gnu.linkonce section.

--- binutils/bfd/aout-adobe.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/aout-adobe.c	2004-05-18 08:19:54.000000000 -0700
@@ -519,6 +519,8 @@ aout_adobe_sizeof_headers (ignore_abfd, 
 #define aout_32_bfd_merge_sections	bfd_generic_merge_sections
 #define aout_32_bfd_is_group_section	bfd_generic_is_group_section
 #define aout_32_bfd_discard_group	bfd_generic_discard_group
+#define aout_32_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define aout_32_bfd_link_hash_table_create \
   _bfd_generic_link_hash_table_create
 #define aout_32_bfd_link_hash_table_free \
--- binutils/bfd/aout-target.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/aout-target.h	2004-05-18 08:19:54.000000000 -0700
@@ -519,6 +519,10 @@ MY_bfd_final_link (abfd, info)
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup NAME(aout,reloc_type_lookup)
 #endif
--- binutils/bfd/aout-tic30.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/aout-tic30.c	2004-05-18 08:19:54.000000000 -0700
@@ -976,6 +976,10 @@ tic30_aout_set_arch_mach (abfd, arch, ma
 #ifndef MY_bfd_discard_group
 #define MY_bfd_discard_group bfd_generic_discard_group
 #endif
+#ifndef MY_bfd_match_symbols_in_sections
+#define MY_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
 #ifndef MY_bfd_reloc_type_lookup
 #define MY_bfd_reloc_type_lookup tic30_aout_reloc_type_lookup
 #endif
--- binutils/bfd/bfd.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/bfd.c	2004-05-18 08:19:54.000000000 -0700
@@ -1112,6 +1112,9 @@ DESCRIPTION
 .#define bfd_discard_group(abfd, sec) \
 .	BFD_SEND (abfd, _bfd_discard_group, (abfd, sec))
 .
+.#define bfd_match_symbols_in_sections(abfd, sec1, sec2) \
+.	BFD_SEND (abfd, _bfd_match_symbols_in_sections, (sec1, sec2))
+.
 .#define bfd_link_hash_table_create(abfd) \
 .	BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd))
 .
--- binutils/bfd/binary.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/binary.c	2004-05-18 08:19:54.000000000 -0700
@@ -341,6 +341,8 @@ binary_sizeof_headers (abfd, exec)
 #define binary_bfd_merge_sections bfd_generic_merge_sections
 #define binary_bfd_is_group_section bfd_generic_is_group_section
 #define binary_bfd_discard_group bfd_generic_discard_group
+#define binary_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define binary_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define binary_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define binary_bfd_link_just_syms _bfd_generic_link_just_syms
--- binutils/bfd/bout.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/bout.c	2004-05-18 08:19:54.000000000 -0700
@@ -1489,6 +1489,8 @@ b_out_bfd_get_relocated_section_contents
 #define b_out_bfd_merge_sections  bfd_generic_merge_sections
 #define b_out_bfd_is_group_section bfd_generic_is_group_section
 #define b_out_bfd_discard_group bfd_generic_discard_group
+#define b_out_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 #define aout_32_get_section_contents_in_window \
   _bfd_generic_get_section_contents_in_window
--- binutils/bfd/coff-alpha.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/coff-alpha.c	2004-05-18 08:19:54.000000000 -0700
@@ -2361,6 +2361,8 @@ static const struct ecoff_backend_data a
 #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target ecoffalpha_little_vec =
 {
--- binutils/bfd/coff-mips.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/coff-mips.c	2004-05-18 08:19:54.000000000 -0700
@@ -1395,6 +1395,8 @@ static const struct ecoff_backend_data m
 
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_ecoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 extern const bfd_target ecoff_big_vec;
 
--- binutils/bfd/coff-rs6000.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/coff-rs6000.c	2004-05-18 08:19:54.000000000 -0700
@@ -4199,6 +4199,7 @@ const bfd_target rs6000coff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -4444,6 +4445,7 @@ const bfd_target pmac_xcoff_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coff64-rs6000.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/coff64-rs6000.c	2004-05-18 08:19:54.000000000 -0700
@@ -2740,6 +2740,7 @@ const bfd_target rs6000coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
@@ -2986,6 +2987,7 @@ const bfd_target aix5coff64_vec =
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
     bfd_generic_discard_group,
+    _bfd_nolink_bfd_match_symbols_in_sections,
 
     /* Dynamic */
     _bfd_xcoff_get_dynamic_symtab_upper_bound,
--- binutils/bfd/coffcode.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/coffcode.h	2004-05-18 08:19:54.000000000 -0700
@@ -5578,6 +5578,11 @@ static const bfd_coff_backend_data ticof
 #define coff_bfd_discard_group		    bfd_generic_discard_group
 #endif
 
+#ifndef coff_bfd_match_symbols_in_sections
+#define coff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
+#endif
+
 #define CREATE_BIG_COFF_TARGET_VEC(VAR, NAME, EXTRA_O_FLAGS, EXTRA_S_FLAGS, UNDER, ALTERNATIVE, SWAP_TABLE)	\
 const bfd_target VAR =							\
 {									\
--- binutils/bfd/ecoff.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/ecoff.c	2004-05-18 08:19:54.000000000 -0700
@@ -95,8 +95,8 @@ static asection bfd_debug_section =
      NULL,       NULL,        0,           0,       0,
   /* line_filepos, userdata, contents, lineno, lineno_count,       */
      0,            NULL,     NULL,     NULL,   0,
-  /* entsize, comdat, kept_section, moving_line_filepos,           */
-     0,       NULL,   NULL,         0,
+  /* entsize, comdat, group, kept_section, moving_line_filepos,    */
+     0,       NULL,   NULL,  NULL,         0,
   /* target_index, used_by_bfd, constructor_chain, owner,          */
      0,            NULL,        NULL,              NULL,
   /* symbol,                                                       */
--- binutils/bfd/elf-bfd.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/elf-bfd.h	2004-05-18 08:19:54.000000000 -0700
@@ -1375,6 +1375,8 @@ extern bfd_boolean bfd_elf_is_group_sect
   (bfd *, const struct bfd_section *);
 extern bfd_boolean bfd_elf_discard_group
   (bfd *, struct bfd_section *);
+extern bfd_boolean bfd_elf_match_symbols_in_sections
+  (struct bfd_section *, struct bfd_section *);
 extern void bfd_elf_set_group_contents
   (bfd *, asection *, void *);
 extern void _bfd_elf_link_just_syms
@@ -1556,6 +1558,9 @@ extern bfd_boolean _bfd_elf_dynamic_symb
 extern bfd_boolean _bfd_elf_symbol_refs_local_p
   (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
 
+extern void _bfd_elf_setup_group_pointers
+  (bfd *);
+
 extern const bfd_target *bfd_elf32_object_p
   (bfd *);
 extern const bfd_target *bfd_elf32_core_file_p
--- binutils/bfd/elf.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/elf.c	2004-05-18 08:56:15.000000000 -0700
@@ -612,6 +612,26 @@ setup_group (bfd *abfd, Elf_Internal_Shd
   return TRUE;
 }
 
+void
+_bfd_elf_setup_group_pointers (bfd *abfd)
+{
+  unsigned int i;
+  unsigned int num_group = elf_tdata (abfd)->num_group;
+
+  if (num_group == (unsigned) -1)
+    return;
+
+  for (i = 0; i < num_group; i++)
+    {
+      Elf_Internal_Shdr *shdr = elf_tdata (abfd)->group_sect_ptr[i];
+      Elf_Internal_Group *idx = (Elf_Internal_Group *) shdr->contents;
+      unsigned int n_elt = shdr->sh_size / 4;
+
+      while (--n_elt != 0)
+	(++idx)->shdr->bfd_section->group = shdr->bfd_section;
+    }
+}
+
 bfd_boolean
 bfd_elf_is_group_section (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec)
 {
@@ -7683,3 +7703,241 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd
 
   return n;
 }
+
+/* Sort symbol by binding and section. We want to put global
+   symbols sorted by section at the beginning.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1;
+  const Elf_Internal_Sym *s2;
+
+  /* Make sure local or undefined symbols are at the end.  */
+  s1 = (const Elf_Internal_Sym *) arg1;
+  if (s1->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s1->st_info) == STB_LOCAL)
+    return 1;
+  s2 = (const Elf_Internal_Sym *) arg2;
+  if (s2->st_shndx == SHN_UNDEF
+      || ELF_ST_BIND (s2->st_info) == STB_LOCAL)
+    return -1;
+
+  /* Grouped by section index.  */
+  return s1->st_shndx - s2->st_shndx;
+}
+
+struct elf_symbol
+{
+  Elf_Internal_Sym *sym;
+  const char *name;
+};
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+  const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+  const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+  return strcmp (s1->name, s2->name);
+}
+
+/* Check if 2 sections export the same set of symbols.  */
+
+bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+{
+  bfd *bfd1, *bfd2;
+  const struct elf_backend_data *bed1, *bed2;
+  Elf_Internal_Shdr *hdr1, *hdr2;
+  bfd_size_type symcount1, symcount2;
+  bfd_size_type extsymcount1, extsymcount2;
+  bfd_size_type extsymoff1, extsymoff2;
+  Elf_Internal_Sym *isymbuf1, *isymbuf2;
+  Elf_Internal_Sym *isymstart1 = NULL, *isymstart2 = NULL, *isym;
+  Elf_Internal_Sym *isymend;
+  struct elf_symbol *symp, *symtable1 = NULL, *symtable2 = NULL;
+  bfd_size_type count1, count2, i;
+  int shndx1, shndx2;
+  bfd_boolean result;
+
+  bfd1 = sec1->owner;
+  bfd2 = sec2->owner;
+
+  /* Both sections have to be in ELF.  */
+  if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+      || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+    return FALSE;
+
+  /* One section has to be in a COMDAT group with only one section and
+     the other one has to be a .gnu.linkonce section.  */
+  if (!(elf_section_type (sec1) == elf_section_type (sec2)
+	&& ((elf_section_flags (sec1) & ~SHF_GROUP)
+	    == (elf_section_flags (sec2) & ~SHF_GROUP))
+	&& ((sec1->group != NULL
+	     && ((sec1->group->flags & (SEC_LINK_ONCE
+					| SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	     && elf_next_in_group (sec1) == sec1
+	     && sec2->group == NULL
+	     && ((sec2->flags & (SEC_LINK_ONCE
+				 | SEC_LINK_DUPLICATES_DISCARD))
+		 == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD)))
+	    || (sec2->group != NULL
+		&& ((sec2->group->flags & (SEC_LINK_ONCE
+					   | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+		&& elf_next_in_group (sec2) == sec2
+		&& sec1->group == NULL
+		&& ((sec1->flags & (SEC_LINK_ONCE
+				    | SEC_LINK_DUPLICATES_DISCARD))
+		    == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))))))
+    return FALSE;
+
+  shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+  shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+  if (shndx1 == -1 || shndx2 == -1)
+    return FALSE;
+
+  bed1 = get_elf_backend_data (bfd1);
+  bed2 = get_elf_backend_data (bfd2);
+  hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+  symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+  hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+  symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+  if (elf_bad_symtab (bfd1))
+    {
+      extsymcount1 = symcount1;
+      extsymoff1 = 0;
+    }
+  else
+    {
+      extsymcount1 = symcount1 - hdr1->sh_info;
+      extsymoff1 = hdr1->sh_info;
+    }
+
+  if (elf_bad_symtab (bfd2))
+    {
+      extsymcount2 = symcount2;
+      extsymoff2 = 0;
+    }
+  else
+    {
+      extsymcount2 = symcount2 - hdr2->sh_info;
+      extsymoff2 = hdr2->sh_info;
+    }
+
+  if (extsymcount1 == 0 || extsymcount2 == 0)
+    return FALSE;
+
+  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, extsymcount1,
+				     extsymoff1, NULL, NULL, NULL);
+  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, extsymcount2,
+				     extsymoff2, NULL, NULL, NULL);
+
+  result = FALSE;
+  if (isymbuf1 == NULL || isymbuf2 == NULL)
+    goto done;
+
+  /* Sort symbols by binding and section. Global definitions are at
+     the beginning.  */
+  qsort (isymbuf1, extsymcount1, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+  qsort (isymbuf2, extsymcount2, sizeof (Elf_Internal_Sym),
+	 elf_sort_elf_symbol);
+
+  /* Count global symbols defined in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + extsymcount1;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx1)
+	{
+	  if (count1 == 0)
+	    isymstart1 = isym;
+	  count1++;
+	}
+
+      if (count1 && isym->st_shndx != (unsigned int) shndx1)
+	break;
+    }
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + extsymcount2;
+       isym < isymend; isym++)
+    {
+      if (isym->st_shndx == (unsigned int) shndx2)
+	{
+	  if (count2 == 0)
+	    isymstart2 = isym;
+	  count2++;
+	}
+
+      if (count2 && isym->st_shndx != (unsigned int) shndx2)
+	break;
+    }
+
+  if (count1 == 0 && count2 == 0)
+    {
+      result = TRUE;
+      goto done;
+    }
+
+  if (count1 != count2)
+    goto done;
+
+  symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+
+  if (symtable1 == NULL || symtable2 == NULL)
+    goto done;
+
+  symp = symtable1;
+  for (isym = isymstart1, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd1,
+						    hdr1->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+ 
+  symp = symtable2;
+  for (isym = isymstart2, isymend = isym + count1;
+       isym < isymend; isym++)
+    {
+      symp->sym = isym;
+      symp->name = bfd_elf_string_from_elf_section (bfd2,
+						    hdr2->sh_link,
+						    isym->st_name);
+      symp++;
+    }
+  
+  /* Sort symbol by name.  */
+  qsort (symtable1, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+  qsort (symtable2, count1, sizeof (struct elf_symbol),
+	 elf_sym_name_compare);
+
+  for (i = 0; i < count1; i++)
+    /* Two symbols must have the same binding, type and name.  */
+    if (symtable1 [i].sym->st_info != symtable2 [i].sym->st_info
+	|| symtable1 [i].sym->st_other != symtable2 [i].sym->st_other
+	|| strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+      goto done;
+
+  result = TRUE;
+
+done:
+  if (symtable1)
+    free (symtable1);
+  if (symtable2)
+    free (symtable2);
+  if (isymbuf1)
+    free (isymbuf1);
+  if (isymbuf2)
+    free (isymbuf2);
+
+  return result;
+}
--- binutils/bfd/elfcode.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/elfcode.h	2004-05-18 08:19:54.000000000 -0700
@@ -742,6 +742,9 @@ elf_object_p (bfd *abfd)
 	  if (shindex == SHN_LORESERVE - 1)
 	    shindex += SHN_HIRESERVE + 1 - SHN_LORESERVE;
 	}
+
+      /* Set up group pointers.  */
+      _bfd_elf_setup_group_pointers (abfd);
     }
 
   /* Let the backend double check the format and override global
--- binutils/bfd/elfxx-target.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/elfxx-target.h	2004-05-18 08:19:54.000000000 -0700
@@ -144,6 +144,11 @@
 #define bfd_elfNN_bfd_discard_group bfd_elf_discard_group
 #endif
 
+#ifndef bfd_elfNN_bfd_match_symbols_in_sections
+#define bfd_elfNN_bfd_match_symbols_in_sections \
+  bfd_elf_match_symbols_in_sections
+#endif
+
 #ifndef bfd_elfNN_bfd_make_debug_symbol
 #define bfd_elfNN_bfd_make_debug_symbol \
   ((asymbol * (*) (bfd *, void *, unsigned long)) bfd_nullvoidptr)
--- binutils/bfd/i386msdos.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/i386msdos.c	2004-05-18 08:19:54.000000000 -0700
@@ -178,6 +178,8 @@ msdos_set_section_contents (abfd, sectio
 #define msdos_bfd_merge_sections bfd_generic_merge_sections
 #define msdos_bfd_is_group_section bfd_generic_is_group_section
 #define msdos_bfd_discard_group bfd_generic_discard_group
+#define msdos_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define msdos_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define msdos_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define msdos_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/i386os9k.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/i386os9k.c	2004-05-18 08:19:54.000000000 -0700
@@ -335,6 +335,8 @@ os9k_sizeof_headers (ignore_abfd, ignore
 #define os9k_bfd_merge_sections bfd_generic_merge_sections
 #define os9k_bfd_is_group_section bfd_generic_is_group_section
 #define os9k_bfd_discard_group bfd_generic_discard_group
+#define os9k_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define os9k_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define os9k_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define os9k_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ieee.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/ieee.c	2004-05-18 08:19:54.000000000 -0700
@@ -4039,6 +4039,8 @@ ieee_bfd_debug_info_accumulate (abfd, se
 #define ieee_bfd_merge_sections bfd_generic_merge_sections
 #define ieee_bfd_is_group_section bfd_generic_is_group_section
 #define ieee_bfd_discard_group bfd_generic_discard_group
+#define ieee_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ieee_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ieee_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ieee_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/ihex.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/ihex.c	2004-05-18 08:19:54.000000000 -0700
@@ -990,6 +990,8 @@ ihex_sizeof_headers (abfd, exec)
 #define ihex_bfd_merge_sections bfd_generic_merge_sections
 #define ihex_bfd_is_group_section bfd_generic_is_group_section
 #define ihex_bfd_discard_group bfd_generic_discard_group
+#define ihex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ihex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ihex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ihex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/libbfd-in.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/libbfd-in.h	2004-05-18 08:19:54.000000000 -0700
@@ -363,6 +363,9 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_discard_group \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) \
    bfd_false)
+#define _bfd_nolink_bfd_match_symbols_in_sections \
+  ((bfd_boolean (*) (struct bfd_section *, struct bfd_section *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_link_hash_table_create \
   ((struct bfd_link_hash_table *(*) (bfd *)) bfd_nullvoidptr)
 #define _bfd_nolink_bfd_link_hash_table_free \
--- binutils/bfd/mach-o.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/mach-o.c	2004-05-18 08:19:54.000000000 -0700
@@ -70,6 +70,8 @@
 #define bfd_mach_o_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_mach_o_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_mach_o_bfd_discard_group bfd_generic_discard_group
+#define bfd_mach_o_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_mach_o_bfd_copy_private_header_data _bfd_generic_bfd_copy_private_header_data
 
 static bfd_boolean bfd_mach_o_bfd_copy_private_symbol_data
--- binutils/bfd/mmo.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/mmo.c	2004-05-18 08:19:54.000000000 -0700
@@ -3288,6 +3288,8 @@ mmo_canonicalize_reloc (abfd, section, r
 #define mmo_bfd_merge_sections bfd_generic_merge_sections
 #define mmo_bfd_is_group_section bfd_generic_is_group_section
 #define mmo_bfd_discard_group bfd_generic_discard_group
+#define mmo_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 /* objcopy will be upset if we return -1 from bfd_get_reloc_upper_bound by
    using BFD_JUMP_TABLE_RELOCS (_bfd_norelocs) rather than 0.  FIXME: Most
--- binutils/bfd/nlm-target.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/nlm-target.h	2004-05-18 08:19:54.000000000 -0700
@@ -46,6 +46,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define nlm_bfd_merge_sections bfd_generic_merge_sections
 #define nlm_bfd_is_group_section bfd_generic_is_group_section
 #define nlm_bfd_discard_group bfd_generic_discard_group
+#define nlm_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define nlm_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define nlm_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define nlm_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/oasys.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/oasys.c	2004-05-18 08:19:54.000000000 -0700
@@ -1508,6 +1508,8 @@ oasys_sizeof_headers (abfd, exec)
 #define oasys_bfd_merge_sections bfd_generic_merge_sections
 #define oasys_bfd_is_group_section bfd_generic_is_group_section
 #define oasys_bfd_discard_group bfd_generic_discard_group
+#define oasys_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define oasys_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define oasys_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define oasys_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/pef.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/pef.c	2004-05-18 08:19:54.000000000 -0700
@@ -54,6 +54,7 @@
 #define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
 #define bfd_pef_bfd_discard_group                   bfd_generic_discard_group
+#define bfd_pef_bfd_match_symbols_in_sections	    _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_pef_bfd_link_hash_table_create          _bfd_generic_link_hash_table_create
 #define bfd_pef_bfd_link_hash_table_free            _bfd_generic_link_hash_table_free
 #define bfd_pef_bfd_link_add_symbols                _bfd_generic_link_add_symbols
--- binutils/bfd/ppcboot.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/ppcboot.c	2004-05-18 08:19:54.000000000 -0700
@@ -471,6 +471,8 @@ ppcboot_bfd_print_private_bfd_data (abfd
 #define ppcboot_bfd_merge_sections bfd_generic_merge_sections
 #define ppcboot_bfd_is_group_section bfd_generic_is_group_section
 #define ppcboot_bfd_discard_group bfd_generic_discard_group
+#define ppcboot_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/section.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/section.c	2004-05-18 08:19:54.000000000 -0700
@@ -493,6 +493,10 @@ CODE_FRAGMENT
 .  {* Optional information about a COMDAT entry; NULL if not COMDAT.  *}
 .  struct bfd_comdat_info *comdat;
 .
+.  {* Optional information about section group; NULL if it doesn't
+.     belongs to any section group.  *}
+.  struct bfd_section *group;
+.
 .  {* Points to the kept section if this section is a link-once section,
 .     and is discarded.  *}
 .  struct bfd_section *kept_section;
@@ -643,8 +647,8 @@ static const asymbol global_syms[] =
     /* line_filepos, userdata, contents, lineno, lineno_count,       */	\
        0,            NULL,     NULL,     NULL,   0,			\
 									\
-    /* entsize, comdat, kept_section, moving_line_filepos,           */	\
-       0,       NULL,   NULL,	      0,				\
+    /* entsize, comdat, group, kept_section, moving_line_filepos,    */	\
+       0,       NULL,   NULL,  NULL,	      0,			\
 									\
     /* target_index, used_by_bfd, constructor_chain, owner,          */	\
        0,            NULL,        NULL,              NULL,		\
--- binutils/bfd/som.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/som.c	2004-05-18 08:19:54.000000000 -0700
@@ -6414,6 +6414,8 @@ som_bfd_link_split_section (abfd, sec)
 #define som_bfd_merge_sections		bfd_generic_merge_sections
 #define som_bfd_is_group_section	bfd_generic_is_group_section
 #define som_bfd_discard_group		bfd_generic_discard_group
+#define som_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 
 const bfd_target som_vec = {
   "som",			/* name */
--- binutils/bfd/srec.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/srec.c	2004-05-18 08:19:54.000000000 -0700
@@ -1286,6 +1286,8 @@ srec_print_symbol (abfd, afile, symbol, 
 #define srec_bfd_merge_sections bfd_generic_merge_sections
 #define srec_bfd_is_group_section bfd_generic_is_group_section
 #define srec_bfd_discard_group bfd_generic_discard_group
+#define srec_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define srec_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define srec_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define srec_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/targets.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/targets.c	2004-05-18 08:19:54.000000000 -0700
@@ -412,7 +412,8 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_gc_sections, \
 .  NAME##_bfd_merge_sections, \
 .  NAME##_bfd_is_group_section, \
-.  NAME##_bfd_discard_group
+.  NAME##_bfd_discard_group, \
+.  NAME##_bfd_match_symbols_in_sections \
 .
 .  int         (*_bfd_sizeof_headers) (bfd *, bfd_boolean);
 .  bfd_byte *  (*_bfd_get_relocated_section_contents)
@@ -455,6 +456,11 @@ BFD_JUMP_TABLE macros.
 .  {* Discard members of a group.  *}
 .  bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *);
 .
+.  {* Return TRUE if need to try if 2 sections export the same set of
+.     symbols.  *}
+.  bfd_boolean (*_bfd_match_symbols_in_sections)
+.    (struct bfd_section *, struct bfd_section *);
+.
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
 .  NAME##_get_dynamic_symtab_upper_bound, \
--- binutils/bfd/tekhex.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/tekhex.c	2004-05-18 08:19:54.000000000 -0700
@@ -1003,6 +1003,8 @@ tekhex_print_symbol (abfd, filep, symbol
 #define tekhex_bfd_merge_sections bfd_generic_merge_sections
 #define tekhex_bfd_is_group_section bfd_generic_is_group_section
 #define tekhex_bfd_discard_group bfd_generic_discard_group
+#define tekhex_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define tekhex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define tekhex_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define tekhex_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/versados.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/versados.c	2004-05-18 08:19:54.000000000 -0700
@@ -874,6 +874,8 @@ versados_canonicalize_reloc (abfd, secti
 #define versados_bfd_merge_sections bfd_generic_merge_sections
 #define versados_bfd_is_group_section bfd_generic_is_group_section
 #define versados_bfd_discard_group bfd_generic_discard_group
+#define versados_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define versados_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define versados_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define versados_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/bfd/vms.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/vms.c	2004-05-18 08:19:54.000000000 -0700
@@ -168,6 +168,8 @@ static bfd_boolean vms_bfd_set_private_f
 #define vms_bfd_link_just_syms _bfd_generic_link_just_syms
 #define vms_bfd_is_group_section bfd_generic_is_group_section
 #define vms_bfd_discard_group bfd_generic_discard_group
+#define vms_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define vms_bfd_copy_private_header_data \
   _bfd_generic_bfd_copy_private_header_data
 \f
--- binutils/bfd/xcoff-target.h.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/xcoff-target.h	2004-05-18 08:19:54.000000000 -0700
@@ -99,6 +99,8 @@ extern int lynx_core_file_failing_signal
 #define _bfd_xcoff_bfd_gc_sections coff_bfd_gc_sections
 #define _bfd_xcoff_bfd_merge_sections coff_bfd_merge_sections
 #define _bfd_xcoff_bfd_discard_group bfd_generic_discard_group
+#define _bfd_xcoff_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define _bfd_xcoff_bfd_link_split_section coff_bfd_link_split_section
 
 /* XCOFF archives do not have anything which corresponds to an
--- binutils/bfd/xsym.c.linkonce	2004-05-18 08:19:37.000000000 -0700
+++ binutils/bfd/xsym.c	2004-05-18 08:19:54.000000000 -0700
@@ -44,6 +44,8 @@
 #define bfd_sym_bfd_merge_sections bfd_generic_merge_sections
 #define bfd_sym_bfd_is_group_section bfd_generic_is_group_section
 #define bfd_sym_bfd_discard_group bfd_generic_discard_group
+#define bfd_sym_bfd_match_symbols_in_sections \
+  _bfd_nolink_bfd_match_symbols_in_sections
 #define bfd_sym_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define bfd_sym_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
 #define bfd_sym_bfd_link_add_symbols _bfd_generic_link_add_symbols
--- binutils/ld/ldlang.c.linkonce	2004-05-18 08:19:43.000000000 -0700
+++ binutils/ld/ldlang.c	2004-05-18 08:45:01.000000000 -0700
@@ -853,6 +853,36 @@ struct already_linked
   asection *sec;
 };
 
+struct already_linked_section
+{
+  asection *sec;
+  asection *linked;
+};
+
+static bfd_boolean
+try_match_symbols_in_sections (struct already_linked_hash_entry *h,
+			       void *info)
+{
+  struct already_linked *l;
+  struct already_linked_section *s
+    = (struct already_linked_section *) info;
+
+  /* No need to check group section.  */
+  if ((s->sec->flags & SEC_GROUP) != 0)
+    return TRUE;
+
+  for (l = h->entry; l != NULL; l = l->next)
+    if ((l->sec->flags & SEC_GROUP) == 0
+	&& bfd_match_symbols_in_sections (l->sec->owner,
+					  l->sec, s->sec))
+      {
+	s->linked = l->sec;
+	return FALSE;
+      }
+
+  return TRUE;
+}
+
 /* The hash table.  */
 
 static struct bfd_hash_table already_linked_table;
@@ -865,6 +895,8 @@ section_already_linked (bfd *abfd, asect
   const char *name;
   struct already_linked *l;
   struct already_linked_hash_entry *already_linked_list;
+  struct already_linked_section result;
+  asection *group;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
@@ -876,7 +908,12 @@ section_already_linked (bfd *abfd, asect
 
   flags = bfd_get_section_flags (abfd, sec);
 
-  if ((flags & SEC_LINK_ONCE) == 0)
+  /* Check if it belongs to a section group.  */
+  group = sec->group;
+
+  /* Return if it isn't a .gnu.linkonce section nor a member of a
+     group.  */
+  if ((flags & SEC_LINK_ONCE) == 0 && group == NULL)
     return;
 
   /* FIXME: When doing a relocatable link, we may have trouble
@@ -902,8 +939,24 @@ section_already_linked (bfd *abfd, asect
     ((struct already_linked_hash_entry *)
      bfd_hash_lookup (&already_linked_table, name, TRUE, FALSE));
 
+  if ((flags & SEC_LINK_ONCE) == 0)
+    {
+      /* If this is a member of a COMDAT group, go to comdat and
+	 .gnu.linkonce check.  */
+      if ((group->flags
+	   & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	  == (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD))
+	goto comdat;
+      else
+	return;
+    }
+
   for (l = already_linked_list->entry; l != NULL; l = l->next)
     {
+      /* Don't check members of COMDAT groups.  */
+      if (l->sec->group != NULL)
+	continue;
+
       if (sec->comdat == NULL
 	  || l->sec->comdat == NULL
 	  || strcmp (sec->comdat->name, l->sec->comdat->name) == 0)
@@ -957,6 +1010,28 @@ section_already_linked (bfd *abfd, asect
 	}
     }
 
+comdat:
+  /* When we get here, it must be either a member of a COMDAT group or a
+     .gnu.linkonce section. Check if a member of a COMDAT group matches
+     a .gnu.linkonce section and vice versa.  */
+  result.sec = sec;
+  result.linked = NULL;
+  bfd_hash_traverse (&already_linked_table,
+		     (bfd_boolean (*) (struct bfd_hash_entry *, void *))
+		     try_match_symbols_in_sections,
+		     &result);
+  if (result.linked)
+    {
+      sec->output_section = bfd_abs_section_ptr;
+      sec->kept_section = result.linked;
+
+      /* Also discard the group section.  */
+      if (group != NULL)
+	group->output_section = bfd_abs_section_ptr;
+
+      return;
+    }
+
   /* This is the first section with this name.  Record it.  Allocate
      the memory from the same obstack as the hash table is kept in.  */
 

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-18 16:38         ` H. J. Lu
@ 2004-05-19  2:36           ` Alan Modra
  2004-05-19 15:43             ` H. J. Lu
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Modra @ 2004-05-19  2:36 UTC (permalink / raw)
  To: H. J. Lu; +Cc: binutils

On Tue, May 18, 2004 at 09:38:28AM -0700, H. J. Lu wrote:
> On Mon, May 17, 2004 at 08:41:50PM -0700, H. J. Lu wrote:
> > On Mon, May 17, 2004 at 06:06:05PM -0700, H. J. Lu wrote:
> > > On Mon, May 17, 2004 at 02:24:23PM -0700, H. J. Lu wrote:
> > > > On Sat, May 15, 2004 at 11:09:47AM +0930, Alan Modra wrote:
> > > > > On Fri, May 14, 2004 at 02:07:37PM -0700, H. J. Lu wrote:
> > > > > > When there are mixed COMDAT and linkonce inputs, linker doesn't handle
> > > > > > them gracefully:
> > > > > 
> > > > > Ewww.  Should we even try?  I understand that such a patch might be
> > > > > useful while gcc is emitting both comdat and linkonce, but once you've
> > > > > completed the change to comdat it shouldn't be necessary.  Also, I'm not
> > > > > really happy with where you have added this code.  At least, it is the
> > > > > wrong place to be discarding duplicate sections.  That ought to happen
> > > > > in ldlang.c:section_already_linked.
> > > > 
> > > > This patch implements it.
> > > 
> > > Here is an update. I should skip checking members of section groups
> > > for already linked section.
> > 
> > Another update. Should I skip COMDAT group members on the already
> > linked list? It may happen if a COMDAT group member has the same
> > section name as a linkonce section.
> 
> I fixed a small memory leak.

Whoa!  HJ, you haven't even answered the basic question of whether the
linker should be doing anything special when confronted with a mix of
COMDAT (new style link-once) and .gnu.linkonce.* (old style link-once)
sections.  It's not clear to me that we should even try to handle this
situation, and what should be done with real groups containing multiple
sections when just one of the sections matches an old style link-once
section.

Secondly, you haven't justified your design.  I think that comparing
symbols exported from a section in order to determine whether a
section is a duplicate is a clever idea, but it doesn't exactly fit
in with the current linker handling of link-once sections.  We currently
just look at section flags and names, discarding sections that have
the SEC_LINK_ONCE flag set and the same name as another SEC_LINK_ONCE
section.  There are some more flags that determine whether the linker
should warn always on discarding, or warn when the section size doesn't
match, or warn when section contents don't match (currently
unimplemented).  Your checking of symbols really fits in with these
warning options.  (And it might be a neat way to implement
SEC_LINK_DUPLICATES_SAME_CONTENTS in the face of needing to support
link-once sections compiled with different gcc optimizations.)

So please spend some time describing why this solution of yours is
necessary, and also why this solution is the best one.  After these
design issues have been discussed, then perhaps we can move on to
looking at implementation details.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

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

* Re: PATCH: Support mixing COMDAT and linkonce
  2004-05-19  2:36           ` Alan Modra
@ 2004-05-19 15:43             ` H. J. Lu
  0 siblings, 0 replies; 10+ messages in thread
From: H. J. Lu @ 2004-05-19 15:43 UTC (permalink / raw)
  To: binutils

On Wed, May 19, 2004 at 12:06:22PM +0930, Alan Modra wrote:
> 
> Whoa!  HJ, you haven't even answered the basic question of whether the
> linker should be doing anything special when confronted with a mix of
> COMDAT (new style link-once) and .gnu.linkonce.* (old style link-once)
> sections.  It's not clear to me that we should even try to handle this
> situation, and what should be done with real groups containing multiple
> sections when just one of the sections matches an old style link-once
> section.
> 
> Secondly, you haven't justified your design.  I think that comparing
> symbols exported from a section in order to determine whether a
> section is a duplicate is a clever idea, but it doesn't exactly fit
> in with the current linker handling of link-once sections.  We currently
> just look at section flags and names, discarding sections that have
> the SEC_LINK_ONCE flag set and the same name as another SEC_LINK_ONCE
> section.  There are some more flags that determine whether the linker
> should warn always on discarding, or warn when the section size doesn't
> match, or warn when section contents don't match (currently
> unimplemented).  Your checking of symbols really fits in with these
> warning options.  (And it might be a neat way to implement
> SEC_LINK_DUPLICATES_SAME_CONTENTS in the face of needing to support
> link-once sections compiled with different gcc optimizations.)
> 
> So please spend some time describing why this solution of yours is
> necessary, and also why this solution is the best one.  After these
> design issues have been discussed, then perhaps we can move on to
> looking at implementation details.


Sometimes compiler has to use the same symbol in both linkonce and
comdat implementation where one linkonce section is replaced by a
section group containing a single section. One case is people put
a variable in a linkonce data section. I agree we can't cover cases
where a section group with multiple sections replaces multiple
linkonce sections.

My goal is to allow mixing comdat section groups containing a single
section with linkonce sections, compiled with different optimizations.
Comparing section name works for linkonce sections. But section name
is not very useful for comdat groups. Is this requirement reasonable?


H.J.

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

end of thread, other threads:[~2004-05-19 15:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-05-14 21:07 PATCH: Support mixing COMDAT and linkonce H. J. Lu
2004-05-15  2:13 ` Alan Modra
2004-05-15 15:44   ` H. J. Lu
2004-05-17 21:24   ` H. J. Lu
2004-05-18  1:06     ` H. J. Lu
2004-05-18  2:13       ` H. J. Lu
2004-05-18  3:41       ` H. J. Lu
2004-05-18 16:38         ` H. J. Lu
2004-05-19  2:36           ` Alan Modra
2004-05-19 15:43             ` H. J. Lu

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).