PATCH: Support mixing COMDAT and linkonce

H. J. Lu hjl@lucon.org
Fri May 14 21:07:00 GMT 2004


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.
-------------- next part --------------
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


More information about the Binutils mailing list