* elf.c (setup_group): Set SEC_LINK_ONCE on GRP_COMDAT groups.
authorAlan Modra <amodra@gmail.com>
Tue, 4 Jun 2002 01:05:21 +0000 (01:05 +0000)
committerAlan Modra <amodra@gmail.com>
Tue, 4 Jun 2002 01:05:21 +0000 (01:05 +0000)
(bfd_section_from_shdr): Likewise.  Set section name of group
sections from signature.
(group_signature): Split out from setup_group.  Ensure symbol table
is available.
(bfd_elf_discard_group): New function.
(_bfd_elf_make_section_from_shdr): Don't set SEC_LINK_ONCE on
.gnu.linkonce* sections if they are members of a group.
(set_group_contents): Set GRP_COMDAT flag.
* section.c (bfd_discard_group): New function.
* bfd-in.h (bfd_elf_discard_group): Declare.
* bfd-in2.h: Regenerate.
* elf-bfd.h (struct bfd_elf_section_data): Add linkonce_p field.
(elf_linkonce_p): Define.

bfd/ChangeLog
bfd/bfd-in.h
bfd/bfd-in2.h
bfd/elf-bfd.h
bfd/elf.c
bfd/section.c

index bd2d5ee..6e10fad 100644 (file)
@@ -1,5 +1,22 @@
 2002-06-04  Alan Modra  <amodra@bigpond.net.au>
 
+       * elf.c (setup_group): Set SEC_LINK_ONCE on GRP_COMDAT groups.
+       (bfd_section_from_shdr): Likewise.  Set section name of group
+       sections from signature.
+       (group_signature): Split out from setup_group.  Ensure symbol table
+       is available.
+       (bfd_elf_discard_group): New function.
+       (_bfd_elf_make_section_from_shdr): Don't set SEC_LINK_ONCE on
+       .gnu.linkonce* sections if they are members of a group.
+       (set_group_contents): Set GRP_COMDAT flag.
+       * section.c (bfd_discard_group): New function.
+       * bfd-in.h (bfd_elf_discard_group): Declare.
+       * bfd-in2.h: Regenerate.
+       * elf-bfd.h (struct bfd_elf_section_data): Add linkonce_p field.
+       (elf_linkonce_p): Define.
+
+2002-06-04  Alan Modra  <amodra@bigpond.net.au>
+
        * elf.c (bfd_section_from_shdr): Make "name" const.
        * elf-bfd.h (elf_backend_section_from_shdr): Likewise.
        * elf32-i370.c (i370_elf_section_from_shdr): Likewise.
index eecd798..0ba8898 100644 (file)
@@ -657,6 +657,8 @@ extern boolean bfd_elf32_discard_info
   PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf64_discard_info
   PARAMS ((bfd *, struct bfd_link_info *));
+extern void bfd_elf_discard_group
+  PARAMS ((bfd *, struct sec *));
 
 /* Return an upper bound on the number of bytes required to store a
    copy of ABFD's program header table entries.  Return -1 if an error
index f11ba2a..afaefef 100644 (file)
@@ -663,6 +663,8 @@ extern boolean bfd_elf32_discard_info
   PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf64_discard_info
   PARAMS ((bfd *, struct bfd_link_info *));
+extern void bfd_elf_discard_group
+  PARAMS ((bfd *, struct sec *));
 
 /* Return an upper bound on the number of bytes required to store a
    copy of ABFD's program header table entries.  Return -1 if an error
@@ -1450,6 +1452,9 @@ bfd_copy_private_section_data PARAMS ((bfd *ibfd, asection *isec,
 void
 _bfd_strip_section_from_output PARAMS ((struct bfd_link_info *info, asection *section));
 
+void
+bfd_discard_group PARAMS ((bfd *abfd, asection *group));
+
 enum bfd_architecture
 {
   bfd_arch_unknown,   /* File arch not known.  */
index d771840..47bb689 100644 (file)
@@ -926,11 +926,15 @@ struct bfd_elf_section_data
 
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
+
+  /* Nonzero when a group is COMDAT.  */
+  unsigned int linkonce_p:1;
 };
 
 #define elf_section_data(sec)  ((struct bfd_elf_section_data*)sec->used_by_bfd)
 #define elf_group_name(sec)    (elf_section_data(sec)->group_name)
 #define elf_next_in_group(sec) (elf_section_data(sec)->next_in_group)
+#define elf_linkonce_p(sec)    (elf_section_data(sec)->linkonce_p)
 
 /* Return true if section has been discarded.  */
 #define elf_discarded_section(sec)                                     \
index a92827f..e1e929f 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -50,6 +50,7 @@ static boolean prep_headers PARAMS ((bfd *));
 static boolean swap_out_syms PARAMS ((bfd *, struct bfd_strtab_hash **, int));
 static boolean copy_private_bfd_data PARAMS ((bfd *, bfd *));
 static char *elf_read PARAMS ((bfd *, file_ptr, bfd_size_type));
+static const char *group_signature PARAMS ((bfd *, Elf_Internal_Shdr *));
 static boolean setup_group PARAMS ((bfd *, Elf_Internal_Shdr *, asection *));
 static void merge_sections_remove_hook PARAMS ((bfd *, asection *));
 static void elf_fake_sections PARAMS ((bfd *, asection *, PTR));
@@ -361,6 +362,35 @@ typedef union elf_internal_group {
   unsigned int flags;
 } Elf_Internal_Group;
 
+/* Return the name of the group signature symbol.  Why isn't the
+   signature just a string?  */
+
+static const char *
+group_signature (abfd, ghdr)
+     bfd *abfd;
+     Elf_Internal_Shdr *ghdr;
+{
+  struct elf_backend_data *bed;
+  file_ptr pos;
+  unsigned char ename[4];
+  unsigned long iname;
+
+  /* First we need to ensure the symbol table is available.  */
+  if (! bfd_section_from_shdr (abfd, ghdr->sh_link))
+    return NULL;
+
+  /* Fortunately, the name index is at the same place in the external
+     symbol for both 32 and 64 bit ELF.  */
+  bed = get_elf_backend_data (abfd);
+  pos = elf_tdata (abfd)->symtab_hdr.sh_offset;
+  pos += ghdr->sh_info * bed->s->sizeof_sym;
+  if (bfd_seek (abfd, pos, SEEK_SET) != 0
+      || bfd_bread (ename, (bfd_size_type) 4, abfd) != 4)
+    return NULL;
+  iname = H_GET_32 (abfd, ename);
+  return elf_string_from_elf_strtab (abfd, iname);
+}
+
 /* Set next_in_group list pointer, and group name for NEWSECT.  */
 
 static boolean
@@ -440,6 +470,9 @@ setup_group (abfd, hdr, newsect)
                      if (src == shdr->contents)
                        {
                          dest->flags = idx;
+                         if (shdr->bfd_section != NULL && (idx & GRP_COMDAT))
+                           shdr->bfd_section->flags
+                             |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
                          break;
                        }
                      if (idx >= shnum)
@@ -492,32 +525,20 @@ setup_group (abfd, hdr, newsect)
                  }
                else
                  {
-                   struct elf_backend_data *bed;
-                   file_ptr pos;
-                   unsigned char ename[4];
-                   unsigned long iname;
                    const char *gname;
 
-                   /* Humbug.  Get the name from the group signature
-                      symbol.  Why isn't the signature just a string?
-                      Fortunately, the name index is at the same
-                      place in the external symbol for both 32 and 64
-                      bit ELF.  */
-                   bed = get_elf_backend_data (abfd);
-                   pos = elf_tdata (abfd)->symtab_hdr.sh_offset;
-                   pos += shdr->sh_info * bed->s->sizeof_sym;
-                   if (bfd_seek (abfd, pos, SEEK_SET) != 0
-                       || bfd_bread (ename, (bfd_size_type) 4, abfd) != 4)
+                   gname = group_signature (abfd, shdr);
+                   if (gname == NULL)
                      return false;
-                   iname = H_GET_32 (abfd, ename);
-                   gname = elf_string_from_elf_strtab (abfd, iname);
                    elf_group_name (newsect) = gname;
 
                    /* Start a circular list with one element.  */
                    elf_next_in_group (newsect) = newsect;
                  }
+
                if (shdr->bfd_section != NULL)
                  elf_next_in_group (shdr->bfd_section) = newsect;
+
                i = num_group - 1;
                break;
              }
@@ -532,6 +553,24 @@ setup_group (abfd, hdr, newsect)
   return true;
 }
 
+void
+bfd_elf_discard_group (abfd, group)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *group;
+{
+  asection *first = elf_next_in_group (group);
+  asection *s = first;
+
+  while (s != NULL)
+    {
+      s->output_section = bfd_abs_section_ptr;
+      s = elf_next_in_group (s);
+      /* These lists are circular.  */
+      if (s == first)
+       break;
+    }
+}
+
 /* Make a BFD section from an ELF section.  We store a pointer to the
    BFD section in the bfd_section field of the header.  */
 
@@ -620,7 +659,8 @@ _bfd_elf_make_section_from_shdr (abfd, hdr, name)
      The symbols will be defined as weak, so that multiple definitions
      are permitted.  The GNU linker extension is to actually discard
      all but one of the sections.  */
-  if (strncmp (name, ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0)
+  if (strncmp (name, ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0
+      && elf_next_in_group (newsect) == NULL)
     flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
 
   bed = get_elf_backend_data (abfd);
@@ -1820,7 +1860,12 @@ bfd_section_from_shdr (abfd, shindex)
       return true;
 
     case SHT_GROUP:
-      /* Make a section for objcopy and relocatable links.  */
+      /* We need a BFD section for objcopy and relocatable linking,
+        and it's handy to have the signature available as the section
+        name.  */
+      name = group_signature (abfd, hdr);
+      if (name == NULL)
+       return false;
       if (!_bfd_elf_make_section_from_shdr (abfd, hdr, name))
        return false;
       if (hdr->contents != NULL)
@@ -1829,6 +1874,10 @@ bfd_section_from_shdr (abfd, shindex)
          unsigned int n_elt = hdr->sh_size / 4;
          asection *s;
 
+         if (idx->flags & GRP_COMDAT)
+           hdr->bfd_section->flags
+             |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
+
          while (--n_elt != 0)
            if ((s = (++idx)->shdr->bfd_section) != NULL
                && elf_next_in_group (s) != NULL)
@@ -2369,7 +2418,7 @@ set_group_contents (abfd, sec, failedptrarg)
       while (elt != elf_next_in_group (l->u.indirect.section));
 
   loc -= 4;
-  H_PUT_32 (abfd, 0, loc);
+  H_PUT_32 (abfd, sec->flags & SEC_LINK_ONCE ? GRP_COMDAT : 0, loc);
 
   BFD_ASSERT (loc == sec->contents);
 }
index 358f5c4..70c7f27 100644 (file)
@@ -1375,3 +1375,24 @@ _bfd_strip_section_from_output (info, s)
 
   s->flags |= SEC_EXCLUDE;
 }
+
+/*
+FUNCTION
+       bfd_discard_group
+
+SYNOPSIS
+       void bfd_discard_group (bfd *abfd, asection *group);
+
+DESCRIPTION
+       Remove all members of @var{group} from the output.
+*/
+
+void
+bfd_discard_group (abfd, group)
+     bfd *abfd;
+     asection *group;
+{
+  if ((group->flags & SEC_GROUP) != 0
+      && abfd->xvec->flavour == bfd_target_elf_flavour)
+    bfd_elf_discard_group (abfd, group);
+}