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


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

PATCH: new linker option for shared library COMDAT optimization


The attached patch implements a new linker option (--shared-comdat) for ELF targets that causes it to discard COMDAT groups that duplicate ones provided by shared libraries, in addition to its current behavior of detecting and discarding duplicates provided by multiple relocatable objects in the link.

As motivation for a patch, consider a situation where you have some low-level C++ libraries, some higher-level UI toolkit libraries, and a set of applications that use them. Chances are that any consumer of the library APIs ends up using many of the same inline constructors, etc that are already provided by the library implementation. By default, GCC emits a copy of these things in COMDAT groups in each object that references them, and the linker has license to discard all but one copy of the group (all copies are supposed to be the same). Presently, it can do this only with relocatable objects in the link; for shared libraries it follows the normal rule that explicitly-provided definitions override anything from a library. However, in a closed system like a root file system for an embedded platform, you end up with needless duplicate copies of these COMDAT groups in every application that uses the libraries. By forcing the linker to use the definitions from the shared libraries instead, we can get smaller executables and also less runtime memory usage (if multiple applications using the same library are in use concurrently).

The slight wrinkle in this scheme is that the ELF specification defines COMDAT groups as an attribute of relocatable files only, not shared objects. So we cannot say definitely that a shared object provides a named COMDAT group. The expedient hack I've adopted is to consider that a shared library provides a COMDAT group if it provides weak definitions for all symbols defined in sections that make up the group.

The effect of this patch is quite similar to what you get if you use the -frepo GCC option, BTW; that tells GCC not to emit local copies of the C++ comdat goo in the first place if all the references are already satisfied by a library included in the link.

This patch has been tested on i686, MIPS, ARM and PowerPC Linux targets. I also tested on local ColdFire and SH branches a few months ago (I'm having problems getting those targets to build from the mainline repositories right now). Besides the included test case, I ran the g++ and libstdc++ testsuites with the --shared-comdat option turned on, and compared the results to the normal behavior.

OK to check in?

-Sandra


2012-02-17 Sandra Loosemore <sandra@codesourcery.com>


	include/
	* bfdlink.h (struct bfd_link_info): Add shared_comdat flag.

	bfd/
	* elflink.c (section_signature): Add forward declaration.
	(struct elf_shared_comdat_symbol): Declare.
	(struct elf_shared_comdat_group): Declare.
	(struct elf_shared_comdat_library): Declare.
	(elf_shared_comdat_group_list): New.
	(elf_shared_comdat_library_list): New.
	(is_comdat_symbol): New.
	(record_shared_comdat_group_symbol): New.
	(record_shared_comdat_library_symbol): New.
	(replace_shared_comdat_group): New.
	(find_shared_comdat_symbols): New.
	(_bfd_elf_symbol_refs_local_p): Return FALSE for comdat symbols
	that may be optimized away.
	(elf_link_add_object_symbols): Record group and library symbols
	for shared comdat analysis.
	(bfd_elf_reloc_symbol_deleted_p): Also check for symbols with no
	regular definition.
	* elf-bfd.h (find_shared_comdat_symbols): Declare.
	(is_comdat_symbol): Declare.
	* elf32-arm.c (elf32_arm_adjust_dynamic_symbol): Test for comdat
	symbols that didn't turn out to need PLT entries after all.
	* elfxx-mips.c (_bfd_mips_elf_check_relocs): Don't require static
	relocs for .pdr references, since they may be optimized away.

	ld/
	* emultempl/elf32.em (gld${EMULATION_NAME}_after_open):
	Call find_shared_comdat_symbols before returning.
	(OPTION_SHARED_COMDAT): Define.
	(gld${EMULATION_NAME}_add_options): Make it know about new option.
	(gld${EMULATION_NAME}_handle_option): Likewise.
	(gld${EMULATION_NAME}_list_options): Likewise.
	* ld.texinfo (--shared-comdat): Document new option.

	ld/testsuite/
	* ld-elf/comdat.exp: New.
	* ld-elf/comdat.H: New.
	* ld-elf/comdat0.C: New.
	* ld-elf/comdat1a.C: New.
	* ld-elf/comdat1b.C: New.
	* ld-elf/comdat-main.C: New.

Index: include/bfdlink.h
===================================================================
RCS file: /cvs/src/src/include/bfdlink.h,v
retrieving revision 1.91
diff -u -p -r1.91 bfdlink.h
--- include/bfdlink.h	15 Jul 2011 02:36:36 -0000	1.91
+++ include/bfdlink.h	15 Feb 2012 19:30:47 -0000
@@ -436,6 +436,10 @@ struct bfd_link_info
   bfd *input_bfds;
   bfd **input_bfds_tail;
 
+  /* TRUE if the ELF --shared-comdat option was specified to prefer symbols
+     from shared libraries over local comdat sections. */
+  unsigned int shared_comdat: 1;
+
   /* Non-NULL if .note.gnu.build-id section should be created.  */
   char *emit_note_gnu_build_id;
 
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.418
diff -u -p -r1.418 elflink.c
--- bfd/elflink.c	17 Aug 2011 00:39:38 -0000	1.418
+++ bfd/elflink.c	15 Feb 2012 19:30:46 -0000
@@ -56,6 +56,343 @@ struct elf_find_verdep_info
 static bfd_boolean _bfd_elf_fix_symbol_flags
   (struct elf_link_hash_entry *, struct elf_info_failed *);
 
+#ifdef DEBUG_SHARED_COMDAT
+static const char *section_signature (asection *);
+#endif
+
+/* Support for shared comdat detection/removal.  The purpose of this
+   optimization is to detect when a shared library included in the link
+   already provides definitions for symbols in a comdat group provided by
+   some object file in the link.  In that case we can completely delete
+   the group from the output of the link and instead just use the library
+   definitions.  This results in smaller executables at the expense of some
+   additional runtime overhead for the now-dynamic symbols.
+
+   The way this works is that we record each comdat symbol defined in a
+   group section as we process the input files.  Likewise, we record each
+   symbol in a shared library that might be a comdat symbol -- this is tricky
+   because the ELF specification defines comdat groups only in relocatable
+   files, so for a shared library we have to guess by considering that
+   at all weakly defined symbols might be part of a group; a relocatable
+   file with a group that defines the symbol might still be seen after the
+   library is processed.  Only after all input files are processed, in the
+   _after_open hook in emultempl/elf32.em, can we match up the groups and
+   libraries and adjust the symbols.
+
+   The current implementation uses simple linked lists to keep track of
+   symbols, groups, and libraries used in the optimization.  */
+
+struct elf_shared_comdat_symbol {
+  /* Link to next in list. */
+  struct elf_shared_comdat_symbol *next;
+  /* The symbol object. */
+  struct elf_link_hash_entry *h;
+  /* The library section where a definition was found. */
+  asection *libsec;
+};
+
+struct elf_shared_comdat_group {
+  /* Link to next in list. */
+  struct elf_shared_comdat_group *next;
+  /* The ELF group section.  We have an entry per group, not per section
+     in the group.  */
+  asection *group_sec;
+  /* Symbols defined in this group.  */
+  struct elf_shared_comdat_symbol *symlist;
+};
+
+struct elf_shared_comdat_library {
+  /* Link to next in list. */
+  struct elf_shared_comdat_library *next;
+  /* The BFD for this library.  */
+  bfd *abfd;
+  /* List of symbols defined in this library that might be from some
+     comdat.  */
+  struct elf_shared_comdat_symbol *symlist;
+};
+
+/* Global state.  We allocate the group and library data structures
+   lazily as we record the symbols that belong to them.  */
+
+static struct elf_shared_comdat_group *elf_shared_comdat_group_list;
+static struct elf_shared_comdat_library *elf_shared_comdat_library_list;
+
+/* Return true if the hash entry points at a symbol defined in a comdat
+   group.  */
+
+bfd_boolean
+is_comdat_symbol (struct elf_link_hash_entry *h)
+{
+  if (((h->root.type == bfd_link_hash_defined)
+       || (h->root.type == bfd_link_hash_defweak))
+      && h->root.u.def.section
+      && elf_section_data (h->root.u.def.section)
+      && elf_sec_group (h->root.u.def.section))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* Record a symbol defined in a comdat group section.  */
+
+static struct elf_shared_comdat_symbol *
+record_shared_comdat_group_symbol (struct elf_link_hash_entry *h)
+{
+  struct elf_shared_comdat_symbol *symbol;
+  struct elf_shared_comdat_group *group;
+  asection *sec;
+
+  BFD_ASSERT (h && is_comdat_symbol (h));
+  sec = elf_sec_group (h->root.u.def.section);
+
+  for (group = elf_shared_comdat_group_list; group; group = group->next)
+    {
+      if (group->group_sec == sec)
+	break;
+      /* We process symbols by the file they're in so stop looking for an entry
+	 for this group if we've reached some other file.  */
+      if (group->group_sec->owner != sec->owner)
+	{
+	  group = NULL;
+	  break;
+	}
+    }
+  if (!group)
+    {
+      group = ((struct elf_shared_comdat_group *)
+	       bfd_malloc (sizeof (struct elf_shared_comdat_group)));
+      if (!group)
+	return NULL;
+      group->group_sec = sec;
+      group->symlist = NULL;
+      group->next = elf_shared_comdat_group_list;
+      elf_shared_comdat_group_list = group;
+    }
+  symbol = ((struct elf_shared_comdat_symbol *)
+	    bfd_malloc (sizeof (struct elf_shared_comdat_symbol)));
+  if (!symbol)
+    return NULL;
+  symbol->h = h;
+  symbol->libsec = NULL;
+  symbol->next = group->symlist;
+  group->symlist = symbol;
+  return symbol;
+}
+
+/* Record a symbol from a shared library that might be a comdat symbol.  */
+
+static struct elf_shared_comdat_symbol *
+record_shared_comdat_library_symbol (bfd *abfd,
+				     struct elf_link_hash_entry *h,
+				     asection *sec)
+{
+  struct elf_shared_comdat_symbol *symbol;
+  struct elf_shared_comdat_library *library;
+
+  BFD_ASSERT (abfd && (abfd->flags & DYNAMIC));
+  BFD_ASSERT (h);
+  BFD_ASSERT (h->root.type == bfd_link_hash_defined
+	      || h->root.type == bfd_link_hash_defweak);
+  BFD_ASSERT (sec);
+
+  /* We process symbols by file, so the BFD should either be the first one
+     on the list, or not present at all.  */
+  if (elf_shared_comdat_library_list
+      && elf_shared_comdat_library_list->abfd == abfd)
+    library = elf_shared_comdat_library_list;
+  else
+    {
+      library = ((struct elf_shared_comdat_library *)
+		 bfd_malloc (sizeof (struct elf_shared_comdat_library)));
+      if (!library)
+	return NULL;
+      library->abfd = abfd;
+      library->symlist = NULL;
+      library->next = elf_shared_comdat_library_list;
+      elf_shared_comdat_library_list = library;
+    }
+  symbol = ((struct elf_shared_comdat_symbol *)
+	    bfd_malloc (sizeof (struct elf_shared_comdat_symbol)));
+  if (!symbol)
+    return NULL;
+  symbol->h = h;
+  symbol->libsec = sec;
+  symbol->next = library->symlist;
+  library->symlist = symbol;
+  return symbol;
+}
+
+/* Re-home symbols defined in the given group to a shared library.  The
+   new section for each symbol was cached in its elf_shared_comdat_symbol
+   object when we matched the group against a library.  */
+
+static void
+replace_shared_comdat_group (struct elf_shared_comdat_group *group)
+{
+  struct elf_shared_comdat_symbol *symbol;
+  asection *s, *first;
+
+  for (symbol = group->symlist; symbol; symbol = symbol->next)
+    {
+#ifdef DEBUG_SHARED_COMDAT
+      printf ("Re-homing symbol %s to section %s from %s\n",
+	      symbol->h->root.root.string,
+	      symbol->libsec->name,
+	      symbol->libsec->owner->filename);
+#endif
+      symbol->h->root.u.def.section = symbol->libsec;
+      symbol->h->def_regular = 0;
+      symbol->h->def_dynamic = 1;
+      symbol->h->dynamic_def = 1;
+      symbol->h->ref_regular = 1;
+      symbol->h->u.weakdef = NULL;
+      /* The other field is sometimes used to store backend-specific
+	 flags.  The ones that are there refer to properties of the old
+	 locally-defined symbol and are certainly incorrect for the
+	 externally-defined one we're replacing it with, so just clear
+	 the field to reset to default.  If the backend needs to do more
+	 than that, it can use the elf_backend_adjust_dynamic_symbol
+	 hook to do it.  */
+      symbol->h->other = 0;
+    }
+
+  /* Delete the comdat group from the link.  */
+  group->group_sec->output_section = bfd_abs_section_ptr;
+  group->group_sec->kept_section = group->symlist->libsec;
+  first = elf_next_in_group (group->group_sec);
+  s = first;
+  while (s != NULL)
+    {
+      s->output_section = bfd_abs_section_ptr;
+      s->kept_section = group->symlist->libsec;
+      s = elf_next_in_group (s);
+      if (s == first)
+	break;
+    }
+}
+
+/* This function is called from the _after_open hook to analyze the
+   collected symbols from groups and libraries, and find groups to optimize
+   away.  */
+
+void
+find_shared_comdat_symbols (void)
+{
+  struct elf_shared_comdat_library *library;
+  struct elf_shared_comdat_group *group;
+
+  /* First preprocess the shared library list.  We built the list in reverse
+     order, and the library list may contain symbol entries that didn't turn
+     out to belong to comdat groups that we can eliminate now.  Note that
+     we can skip this step if there are no groups to optimize anyway.  */
+  if (elf_shared_comdat_group_list)
+    {
+      library = elf_shared_comdat_library_list;
+      elf_shared_comdat_library_list = NULL;
+      while (library)
+	{
+	  struct elf_shared_comdat_library *nextlib = library->next;
+	  struct elf_shared_comdat_symbol *symbol = library->symlist;
+	  library->symlist = NULL;
+	  while (symbol)
+	    {
+	      struct elf_shared_comdat_symbol *nextsym = symbol->next;
+	      if (is_comdat_symbol (symbol->h))
+		{
+		  symbol->next = library->symlist;
+		  library->symlist = symbol;
+		}
+	      else
+		free (symbol);
+	      symbol = nextsym;
+	    }
+	  if (library->symlist)
+	    {
+	      library->next = elf_shared_comdat_library_list;
+	      elf_shared_comdat_library_list = library;
+	    }
+	  else
+	    free (library);
+	  library = nextlib;
+	}
+    }
+
+  /* Now iterate over groups, checking to see whether there is a library
+     that provides definitions for all symbols in the group.  */
+  for (group = elf_shared_comdat_group_list; group; group = group->next)
+    {
+      for (library = elf_shared_comdat_library_list;
+	   library;
+	   library = library->next)
+	{
+	  struct elf_shared_comdat_symbol *gsym, *lsym;
+	  for (gsym = group->symlist; gsym; gsym = gsym->next)
+	    {
+	      for (lsym = library->symlist; lsym; lsym = lsym->next)
+		if (gsym->h == lsym->h)
+		  {
+		    gsym->libsec = lsym->libsec;
+		    break;
+		  }
+	      if (!lsym)
+		break;
+	    }
+	  /* Now gsym is NULL if we matched all symbols in the group.  */
+	  if (!gsym)
+	    {
+#ifdef DEBUG_SHARED_COMDAT
+	      printf ("Matched comdat group %s in library %s.\n",
+		      section_signature (group->group_sec),
+		      library->abfd->filename);
+#endif
+	      replace_shared_comdat_group (group);
+	      break;
+	    }
+#ifdef DEBUG_SHARED_COMDAT
+	  else if (gsym != group->symlist)
+	    /* This might be a sign something bad/unexpected is going on.  */
+	    printf ("Partial match for comdat group %s in library %s, but %s is missing.\n",
+		    section_signature (group->group_sec),
+		    library->abfd->filename,
+		    gsym->h->root.root.string);
+#endif 
+	}
+    }
+
+  /* Free remaining data structures.  */
+  group = elf_shared_comdat_group_list;
+  elf_shared_comdat_group_list = NULL;
+  while (group)
+    {
+      struct elf_shared_comdat_group *nextgroup = group->next;
+      struct elf_shared_comdat_symbol *symbol = group->symlist;
+      while (symbol)
+	{
+	  struct elf_shared_comdat_symbol *nextsym = symbol->next;
+	  free (symbol);
+	  symbol = nextsym;
+	}
+      free (group);
+      group = nextgroup;
+    }
+
+  library = elf_shared_comdat_library_list;
+  elf_shared_comdat_library_list = NULL;
+  while (library)
+    {
+      struct elf_shared_comdat_library *nextlib = library->next;
+      struct elf_shared_comdat_symbol *symbol = library->symlist;
+      while (symbol)
+	{
+	  struct elf_shared_comdat_symbol *nextsym = symbol->next;
+	  free (symbol);
+	  symbol = nextsym;
+	}
+      free (library);
+      library = nextlib;
+    }
+}
+
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
@@ -2828,6 +3165,12 @@ _bfd_elf_symbol_refs_local_p (struct elf
   else if (!h->def_regular)
     return FALSE;
 
+  /* If we are doing shared comdat optimization and this symbol belongs
+     to a comdat group, we may still optimize it away and replace it
+     with a symbol from a dynamic library.  */
+  if (info->shared_comdat && is_comdat_symbol (h))
+    return FALSE;
+
   /* Forced local symbols resolve locally.  */
   if (h->forced_local)
     return TRUE;
@@ -3834,7 +4177,7 @@ error_free_dyn:
     {
       int bind;
       bfd_vma value;
-      asection *sec, *new_sec;
+      asection *sec, *new_sec, *orig_sec;
       flagword flags;
       const char *name;
       struct elf_link_hash_entry *h;
@@ -3847,6 +4190,8 @@ error_free_dyn:
       unsigned int old_alignment;
       bfd *old_bfd;
       bfd * undef_bfd = NULL;
+      bfd_boolean comdat_group_symbol = FALSE;
+      bfd_boolean comdat_library_symbol = FALSE;
 
       override = FALSE;
 
@@ -3972,6 +4317,27 @@ error_free_dyn:
       else
 	definition = TRUE;
 
+      /* Check if the incoming symbol is a definition we need to keep track
+	 of for shared comdat processing.  We can't do this check easily
+	 at the point where the information is recorded because e.g. the
+	 definition flag may be overwritten in the meantime.
+	 Note that any weak definition in a shared library may potentially
+	 turn out to be marked as a belonging to a comdat group in a .o
+	 file we process later in the link.  We don't have enough information
+	 to know that during sequential input processing of shared library
+	 inputs so we mark all such symbols now and filter out the non-comdat
+	 ones later. */
+      if (is_elf_hash_table (htab) && info->shared_comdat && definition)
+	{
+	  if (dynamic && (flags & BSF_WEAK) != 0)
+	    {
+	      comdat_library_symbol = TRUE;
+	      orig_sec = sec;
+	    }
+	  if (!dynamic && elf_sec_group (sec) && !sec->kept_section)
+	    comdat_group_symbol = TRUE;
+	}
+
       size_change_ok = FALSE;
       type_change_ok = bed->type_change_ok;
       old_alignment = 0;
@@ -4178,6 +4544,11 @@ error_free_dyn:
       if (is_elf_hash_table (htab))
 	h->unique_global = (flags & BSF_GNU_UNIQUE) != 0;
 
+      if (comdat_group_symbol)
+	record_shared_comdat_group_symbol (h);
+      if (comdat_library_symbol)
+	record_shared_comdat_library_symbol (abfd, h, orig_sec);
+
       new_weakdef = FALSE;
       if (dynamic
 	  && definition
Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.329
diff -u -p -r1.329 elf-bfd.h
--- bfd/elf-bfd.h	17 Aug 2011 00:39:38 -0000	1.329
+++ bfd/elf-bfd.h	15 Feb 2012 19:30:45 -0000
@@ -2315,6 +2315,10 @@ extern bfd_vma elf32_r_sym (bfd_vma);
 /* Large common section.  */
 extern asection _bfd_elf_large_com_section;
 
+/* Hooks for removing duplicate COMDAT sections.  */
+extern void find_shared_comdat_symbols (void);
+extern bfd_boolean is_comdat_symbol (struct elf_link_hash_entry *);
+
 /* Hash for local symbol with the first section id, ID, in the input
    file and the local symbol index, SYM.  */
 #define ELF_LOCAL_SYMBOL_HASH(ID, SYM) \
Index: bfd/elf32-arm.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-arm.c,v
retrieving revision 1.275
diff -u -p -r1.275 elf32-arm.c
--- bfd/elf32-arm.c	1 Sep 2011 14:10:39 -0000	1.275
+++ bfd/elf32-arm.c	15 Feb 2012 19:30:45 -0000
@@ -12699,6 +12699,7 @@ elf32_arm_adjust_dynamic_symbol (struct 
       if (h->plt.refcount <= 0
 	  || (h->type != STT_GNU_IFUNC
 	      && (SYMBOL_CALLS_LOCAL (info, h)
+		  || (info->shared_comdat && is_comdat_symbol (h))
 		  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
 		      && h->root.type == bfd_link_hash_undefweak))))
 	{
@@ -12706,7 +12707,11 @@ elf32_arm_adjust_dynamic_symbol (struct 
 	     file, but the symbol was never referred to by a dynamic
 	     object, or if all references were garbage collected.  In
 	     such a case, we don't actually need to build a procedure
-	     linkage table, and we can just do a PC24 reloc instead.  */
+	     linkage table, and we can just do a PC24 reloc instead.
+	     Likewise this can happen when we tentatively marked a comdat
+	     symbol for shared comdat optimization but did not actually
+	     find a shared library defining the symbol, so we have retained
+	     the local definition.  */
 	  h->plt.offset = (bfd_vma) -1;
 	  eh->plt.thumb_refcount = 0;
 	  eh->plt.maybe_thumb_refcount = 0;
Index: bfd/elfxx-mips.c
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.c,v
retrieving revision 1.292
diff -u -p -r1.292 elfxx-mips.c
--- bfd/elfxx-mips.c	2 Aug 2011 14:28:26 -0000	1.292
+++ bfd/elfxx-mips.c	15 Feb 2012 19:30:46 -0000
@@ -7858,6 +7858,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	     nil.  */
 	  if ((sec->flags & SEC_DEBUGGING) != 0)
 	    break;
+
+	  /* Likewise ignore relocs from the .pdr section.  This is needed
+	     specifically for shared comdat optimization when the symbol
+	     referred to in the .pdr relocation is being optimized away.  */
+	  if (strcmp (sec->name, ".pdr") == 0)
+	    break;
+
 	  /* Fall through.  */
 
 	default:
Index: ld/emultempl/elf32.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/elf32.em,v
retrieving revision 1.222
diff -u -p -r1.222 elf32.em
--- ld/emultempl/elf32.em	14 Jul 2011 12:43:35 -0000	1.222
+++ ld/emultempl/elf32.em	15 Feb 2012 19:30:47 -0000
@@ -1180,7 +1180,11 @@ gld${EMULATION_NAME}_after_open (void)
      needed list can actually grow while we are stepping through this
      loop.  */
   if (!link_info.executable)
-    return;
+    {
+      if (link_info.shared_comdat)
+	find_shared_comdat_symbols ();
+      return;
+    }
   needed = bfd_elf_get_needed_list (link_info.output_bfd, &link_info);
   for (l = needed; l != NULL; l = l->next)
     {
@@ -1347,6 +1351,9 @@ fragment <<EOF
       einfo ("%P: warning: %s, needed by %B, not found (try using -rpath or -rpath-link)\n",
 	     l->name, l->by);
     }
+
+  if (link_info.shared_comdat)
+    find_shared_comdat_symbols ();
 }
 
 EOF
@@ -2122,6 +2129,7 @@ fragment <<EOF
 #define OPTION_HASH_STYLE		(OPTION_EXCLUDE_LIBS + 1)
 #define OPTION_BUILD_ID			(OPTION_HASH_STYLE + 1)
 #define OPTION_AUDIT			(OPTION_BUILD_ID + 1)
+#define OPTION_SHARED_COMDAT		(OPTION_AUDIT + 1)
 
 static void
 gld${EMULATION_NAME}_add_options
@@ -2145,6 +2153,7 @@ if test x"$GENERATE_SHLIB_SCRIPT" = xyes
 fragment <<EOF
     {"audit", required_argument, NULL, OPTION_AUDIT},
     {"Bgroup", no_argument, NULL, OPTION_GROUP},
+    {"shared-comdat", no_argument, NULL, OPTION_SHARED_COMDAT},
 EOF
 fi
 fragment <<EOF
@@ -2229,6 +2238,10 @@ fragment <<EOF
       link_info.unresolved_syms_in_shared_libs = RM_GENERATE_ERROR;
       break;
 
+    case OPTION_SHARED_COMDAT:
+      link_info.shared_comdat = TRUE;
+      break;
+
     case OPTION_EXCLUDE_LIBS:
       add_excluded_libs (optarg);
       break;
@@ -2375,6 +2388,8 @@ fragment <<EOF
   -P AUDITLIB, --depaudit=AUDITLIB\n" "\
                               Specify a library to use for auditing dependencies\n"));
   fprintf (file, _("\
+  --shared-comdat             Use comdat groups from shared objects\n"));
+  fprintf (file, _("\
   --disable-new-dtags         Disable new dynamic tags\n"));
   fprintf (file, _("\
   --enable-new-dtags          Enable new dynamic tags\n"));
Index: ld/ld.texinfo
===================================================================
RCS file: /cvs/src/src/ld/ld.texinfo,v
retrieving revision 1.280
diff -u -p -r1.280 ld.texinfo
--- ld/ld.texinfo	22 Aug 2011 12:35:10 -0000	1.280
+++ ld/ld.texinfo	15 Feb 2012 19:30:47 -0000
@@ -1212,6 +1212,14 @@ symbols to the definition within the sha
 This option is only meaningful on ELF platforms which support shared
 libraries.
 
+@kindex --shared-comdat
+@item --shared-comdat
+Discard local COMDAT sections and use definitions inherited from
+shared libraries when possible.  This can result in smaller output
+files at the expense of slightly slower execution.  
+This option is only meaningful on ELF platforms which support shared
+libraries.
+
 @kindex --dynamic-list=@var{dynamic-list-file}
 @item --dynamic-list=@var{dynamic-list-file}
 Specify the name of a dynamic list file to the linker.  This is
Index: ld/testsuite/ld-elf/comdat.exp
===================================================================
RCS file: ld/testsuite/ld-elf/comdat.exp
diff -N ld/testsuite/ld-elf/comdat.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat.exp	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,112 @@
+# Expect script for various ELF tests.
+#   Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+# Exclude non-ELF targets.
+if ![is_elf_format] {
+    return
+}
+
+# This feature probably doesn't make sense on non-Linux targets.
+if { ![istarget *-*-linux*] } {
+    return
+}
+
+# Check if compiler works.
+if { [which $CXX] == 0 } {
+    return
+}
+
+global nm
+global nm_output
+
+# Build unoptimized libraries and program.  There should be a symbol
+# named "_ZN4testC2Ei" defined in all four pieces.  If not, the compiler
+# is doing something unexpected and this test isn't valid.
+
+if { ![ld_simple_link $CXX libcomdat0-base.so "$CXXFLAGS -fPIC -shared $srcdir/$subdir/comdat0.C"]
+     || ![ld_nm $nm "" "libcomdat0-base.so"]
+     || ![info exists nm_output(ZN4testC2Ei)] } {
+   unresolved "ELF comdat baseline"
+   return
+}
+
+if { ![ld_simple_link $CXX libcomdat1a-base.so "$CXXFLAGS -fPIC -shared $srcdir/$subdir/comdat1a.C -L . -lcomdat0-base"]
+     || ![ld_nm $nm "" "libcomdat1a-base.so"]
+     || ![info exists nm_output(ZN4testC2Ei)] } {
+   unresolved "ELF comdat baseline"
+   return
+}
+
+if { ![ld_simple_link $CXX libcomdat1b-base.so "$CXXFLAGS -fPIC -shared $srcdir/$subdir/comdat1b.C -L. -lcomdat0-base"]
+     || ![ld_nm $nm "" "libcomdat1b-base.so"]
+     || ![info exists nm_output(ZN4testC2Ei)] } {
+   unresolved "ELF comdat baseline"
+   return
+}
+
+if { ![ld_simple_link $CXX comdat-main-base "$CXXFLAGS $srcdir/$subdir/comdat-main.C -L. -Wl,-rpath-link=. -lcomdat1a-base -lcomdat1b-base -lcomdat0-base"]
+     || ![ld_nm $nm "" "comdat-main-base"]
+     || ![info exists nm_output(ZN4testC2Ei)] } {
+   unresolved "ELF comdat baseline"
+   return
+}
+
+# Now rebuild everything with --shared-comdat.  Only libcomdat0 should
+# include a definition of "_ZN4testC2Ei".  The other three pieces should
+# simply include a dynamic reference to that definition.
+
+if { ![ld_simple_link $CXX libcomdat0-opt.so "$CXXFLAGS -fPIC -shared -Wl,--shared-comdat $srcdir/$subdir/comdat0.C"]
+     || ![ld_nm $nm "" "libcomdat0-opt.so"]
+     || ![info exists nm_output(ZN4testC2Ei)] } {
+   fail "ELF comdat libcomdat0"
+   return
+} else {
+   pass "ELF comdat libcomdat0"
+}
+
+if { ![ld_simple_link $CXX libcomdat1a-opt.so "$CXXFLAGS -fPIC -shared -Wl,--shared-comdat $srcdir/$subdir/comdat1a.C -L . -lcomdat0-opt"]
+     || ![ld_nm $nm "" "libcomdat1a-opt.so"]
+     || [info exists nm_output(ZN4testC2Ei)] } {
+   fail "ELF comdat libcomdat1a"
+   return
+} else {
+   pass "ELF comdat libcomdat1a"
+}
+
+if { ![ld_simple_link $CXX libcomdat1b-opt.so "$CXXFLAGS -fPIC -shared -Wl,--shared-comdat $srcdir/$subdir/comdat1b.C -L. -lcomdat0-opt"]
+     || ![ld_nm $nm "" "libcomdat1b-opt.so"]
+     || [info exists nm_output(ZN4testC2Ei)] } {
+   fail "ELF comdat libcomdat1b"
+   return
+} else {
+   pass "ELF comdat libcomdat1b"
+}
+
+if { ![ld_simple_link $CXX comdat-main-opt "$CXXFLAGS -Wl,--shared-comdat $srcdir/$subdir/comdat-main.C -L. -Wl,-rpath-link=. -lcomdat1a-opt -lcomdat1b-opt -lcomdat0-opt"]
+     || ![ld_nm $nm "" "comdat-main-opt"]
+     || [info exists nm_output(ZN4testC2Ei)] } {
+   fail "ELF comdat comdat-main"
+   return
+} else {
+   pass "ELF comdat comdat-main"
+}
+
+
Index: ld/testsuite/ld-elf/comdat.H
===================================================================
RCS file: ld/testsuite/ld-elf/comdat.H
diff -N ld/testsuite/ld-elf/comdat.H
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat.H	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,14 @@
+class test {
+ public:
+  int x;
+  test () { x = 0; }
+  test (int xx) { x = xx; }
+  int increment () { return ++x; }
+  void print ();
+};
+
+extern test *maketest (int);
+
+extern void a (int);
+extern void b (int);
+
Index: ld/testsuite/ld-elf/comdat0.C
===================================================================
RCS file: ld/testsuite/ld-elf/comdat0.C
diff -N ld/testsuite/ld-elf/comdat0.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat0.C	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,12 @@
+#include "comdat.H"
+#include <stdio.h>
+
+void test::print ()
+{
+  printf ("(%p) x = %d\n", (void *)this, x);
+}
+
+test *maketest (int xx)
+{
+  return new test (xx);
+}
Index: ld/testsuite/ld-elf/comdat1a.C
===================================================================
RCS file: ld/testsuite/ld-elf/comdat1a.C
diff -N ld/testsuite/ld-elf/comdat1a.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat1a.C	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,10 @@
+#include "comdat.H"
+
+void a (int xx)
+{
+  test obj (xx);
+  obj.print ();
+  obj.increment ();
+  obj.print ();
+}
+
Index: ld/testsuite/ld-elf/comdat1b.C
===================================================================
RCS file: ld/testsuite/ld-elf/comdat1b.C
diff -N ld/testsuite/ld-elf/comdat1b.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat1b.C	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,9 @@
+#include "comdat.H"
+
+void b (int xx)
+{
+  test obj (xx);
+  obj.print ();
+  obj.increment ();
+  obj.print ();
+}
Index: ld/testsuite/ld-elf/comdat-main.C
===================================================================
RCS file: ld/testsuite/ld-elf/comdat-main.C
diff -N ld/testsuite/ld-elf/comdat-main.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-elf/comdat-main.C	15 Feb 2012 19:30:47 -0000
@@ -0,0 +1,9 @@
+#include "comdat.H"
+
+int main ()
+{
+  test widget (0);
+  widget.print ();
+  a (1);
+  b (42);
+}

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