bfd/
authorAlan Modra <amodra@gmail.com>
Thu, 4 Nov 2004 11:55:13 +0000 (11:55 +0000)
committerAlan Modra <amodra@gmail.com>
Thu, 4 Nov 2004 11:55:13 +0000 (11:55 +0000)
* elf64-ppc.h (ppc64_elf_edit_toc): Declare.
* elf64-ppc.c (struct ppc_link_hash_entry <adjust_done>): Update
comment.
(struct adjust_toc_info): New.
(adjust_toc_syms, ppc64_elf_edit_toc): New functions.
ld/
* emultempl/ppc64elf.em (no_tls_opt): Rename from notlsopt.
(no_opd_opt, no_toc_opt): New static vars.
(ppc_before_allocation): Don't edit opt if no_opd_opt.  Call
ppc64_elf_edit_toc.
(OPTION_NO_OPD_OPT, OPTION_NO_TOC_OPT): Define.
(PARSE_AND_LIST_LONGOPTS): Add no-opd-optimize and no-toc-optimize.
(PARSE_AND_LIST_OPTIONS): Describe new options.
(PARSE_AND_LIST_ARGS_CASES): Handle them.

bfd/ChangeLog
bfd/elf64-ppc.c
bfd/elf64-ppc.h
ld/ChangeLog
ld/emultempl/ppc64elf.em

index 93d96e4..e2e79d0 100644 (file)
@@ -1,3 +1,11 @@
+2004-11-04  Alan Modra  <amodra@bigpond.net.au>
+
+       * elf64-ppc.h (ppc64_elf_edit_toc): Declare.
+       * elf64-ppc.c (struct ppc_link_hash_entry <adjust_done>): Update
+       comment.
+       (struct adjust_toc_info): New.
+       (adjust_toc_syms, ppc64_elf_edit_toc): New functions.
+
 2004-11-03  Bob Wilson  <bob.wilson@acm.org>
 
        * elf.c (assign_file_positions_for_segments): Set next_file_pos even
index c21d027..d230c1b 100644 (file)
@@ -3206,9 +3206,9 @@ struct ppc_link_hash_entry
   unsigned int is_func:1;
   unsigned int is_func_descriptor:1;
 
-  /* Whether global opd sym has been adjusted or not.
-     After ppc64_elf_edit_opd has run, this flag should be set for all
-     globals defined in any opd section.  */
+  /* Whether global opd/toc sym has been adjusted or not.
+     After ppc64_elf_edit_opd/ppc64_elf_edit_toc has run, this flag
+     should be set for all globals defined in any opd/toc section.  */
   unsigned int adjust_done:1;
 
   /* Set if we twiddled this symbol to weak at some stage.  */
@@ -6645,6 +6645,445 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Called via elf_link_hash_traverse from ppc64_elf_edit_toc to adjust
+   the values of any global symbols in a toc section that has been
+   edited.  Globals in toc sections should be a rarity, so this function
+   sets a flag if any are found in toc sections other than the one just
+   edited, so that futher hash table traversals can be avoided.  */
+
+struct adjust_toc_info
+{
+  asection *toc;
+  unsigned long *skip;
+  bfd_boolean global_toc_syms;
+};
+
+static bfd_boolean
+adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
+{
+  struct ppc_link_hash_entry *eh;
+  struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf;
+
+  if (h->root.type == bfd_link_hash_indirect)
+    return TRUE;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  if (h->root.type != bfd_link_hash_defined
+      && h->root.type != bfd_link_hash_defweak)
+    return TRUE;
+
+  eh = (struct ppc_link_hash_entry *) h;
+  if (eh->adjust_done)
+    return TRUE;
+
+  if (eh->elf.root.u.def.section == toc_inf->toc)
+    {
+      unsigned long skip = toc_inf->skip[eh->elf.root.u.def.value >> 3];
+      if (skip != (unsigned long) -1)
+       eh->elf.root.u.def.value -= skip;
+      else
+       {
+         (*_bfd_error_handler)
+           (_("%s defined in removed toc entry"), eh->elf.root.root.string);
+         eh->elf.root.u.def.section = &bfd_abs_section;
+         eh->elf.root.u.def.value = 0;
+       }
+      eh->adjust_done = 1;
+    }
+  else if (strcmp (eh->elf.root.u.def.section->name, ".toc") == 0)
+    toc_inf->global_toc_syms = TRUE;
+
+  return TRUE;
+}
+
+/* Examine all relocs referencing .toc sections in order to remove
+   unused .toc entries.  */
+
+bfd_boolean
+ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
+{
+  bfd *ibfd;
+  struct adjust_toc_info toc_inf;
+
+  toc_inf.global_toc_syms = TRUE;
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    {
+      asection *toc, *sec;
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Sym *local_syms;
+      struct elf_link_hash_entry **sym_hashes;
+      Elf_Internal_Rela *relstart, *rel, *wrel;
+      unsigned long *skip, *drop;
+      unsigned char *used;
+      unsigned char *keep, last, some_unused;
+
+      toc = bfd_get_section_by_name (ibfd, ".toc");
+      if (toc == NULL
+         || toc->sec_info_type == ELF_INFO_TYPE_JUST_SYMS
+         || elf_discarded_section (toc))
+       continue;
+
+      local_syms = NULL;
+      symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
+      sym_hashes = elf_sym_hashes (ibfd);
+
+      /* Look at sections dropped from the final link.  */
+      skip = NULL;
+      relstart = NULL;
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       {
+         if (sec->reloc_count == 0
+             || !elf_discarded_section (sec)
+             || get_opd_info (sec)
+             || (sec->flags & SEC_ALLOC) == 0
+             || (sec->flags & SEC_DEBUGGING) != 0)
+           continue;
+
+         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, FALSE);
+         if (relstart == NULL)
+           goto error_ret;
+
+         /* Run through the relocs to see which toc entries might be
+            unused.  */
+         for (rel = relstart; rel < relstart + sec->reloc_count; ++rel)
+           {
+             enum elf_ppc64_reloc_type r_type;
+             unsigned long r_symndx;
+             asection *sym_sec;
+             struct elf_link_hash_entry *h;
+             Elf_Internal_Sym *sym;
+             bfd_vma val;
+
+             r_type = ELF64_R_TYPE (rel->r_info);
+             switch (r_type)
+               {
+               default:
+                 continue;
+
+               case R_PPC64_TOC16:
+               case R_PPC64_TOC16_LO:
+               case R_PPC64_TOC16_HI:
+               case R_PPC64_TOC16_HA:
+               case R_PPC64_TOC16_DS:
+               case R_PPC64_TOC16_LO_DS:
+                 break;
+               }
+
+             r_symndx = ELF64_R_SYM (rel->r_info);
+             if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                             r_symndx, ibfd))
+               goto error_ret;
+
+             if (sym_sec != toc)
+               continue;
+
+             if (h != NULL)
+               val = h->root.u.def.value;
+             else
+               val = sym->st_value;
+             val += rel->r_addend;
+
+             if (val >= toc->size)
+               continue;
+
+             /* Anything in the toc ought to be aligned to 8 bytes.
+                If not, don't mark as unused.  */
+             if (val & 7)
+               continue;
+
+             if (skip == NULL)
+               {
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 7) / 8);
+                 if (skip == NULL)
+                   goto error_ret;
+               }
+
+             skip[val >> 3] = 1;
+           }
+
+         if (elf_section_data (sec)->relocs != relstart)
+           free (relstart);
+       }
+
+      if (skip == NULL)
+       continue;
+
+      used = bfd_zmalloc (sizeof (*used) * (toc->size + 7) / 8);
+      if (used == NULL)
+       {
+       error_ret:
+         if (local_syms != NULL
+             && symtab_hdr->contents != (unsigned char *) local_syms)
+           free (local_syms);
+         if (sec != NULL
+             && relstart != NULL
+             && elf_section_data (sec)->relocs != relstart)
+           free (relstart);
+         if (skip != NULL)
+           free (skip);
+         return FALSE;
+       }
+
+      /* Now check all kept sections that might reference the toc.  */
+      for (sec = ibfd->sections;
+          sec != NULL;
+          /* Check the toc itself last.  */
+          sec = (sec == toc ? NULL
+                 : sec->next == toc && sec->next->next ? sec->next->next
+                 : sec->next == NULL ? toc
+                 : sec->next))
+       {
+         int repeat;
+
+         if (sec->reloc_count == 0
+             || elf_discarded_section (sec)
+             || get_opd_info (sec)
+             || (sec->flags & SEC_ALLOC) == 0
+             || (sec->flags & SEC_DEBUGGING) != 0)
+           continue;
+
+         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, TRUE);
+         if (relstart == NULL)
+           goto error_ret;
+
+         /* Mark toc entries referenced as used.  */
+         repeat = 0;
+         do
+           for (rel = relstart; rel < relstart + sec->reloc_count; ++rel)
+             {
+               enum elf_ppc64_reloc_type r_type;
+               unsigned long r_symndx;
+               asection *sym_sec;
+               struct elf_link_hash_entry *h;
+               Elf_Internal_Sym *sym;
+               bfd_vma val;
+
+               r_type = ELF64_R_TYPE (rel->r_info);
+               switch (r_type)
+                 {
+                 case R_PPC64_TOC16:
+                 case R_PPC64_TOC16_LO:
+                 case R_PPC64_TOC16_HI:
+                 case R_PPC64_TOC16_HA:
+                 case R_PPC64_TOC16_DS:
+                 case R_PPC64_TOC16_LO_DS:
+                   /* In case we're taking addresses of toc entries.  */
+                 case R_PPC64_ADDR64:
+                   break;
+
+                 default:
+                   continue;
+                 }
+
+               r_symndx = ELF64_R_SYM (rel->r_info);
+               if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                               r_symndx, ibfd))
+                 {
+                   free (used);
+                   goto error_ret;
+                 }
+
+               if (sym_sec != toc)
+                 continue;
+
+               if (h != NULL)
+                 val = h->root.u.def.value;
+               else
+                 val = sym->st_value;
+               val += rel->r_addend;
+
+               if (val >= toc->size)
+                 continue;
+
+               /* For the toc section, we only mark as used if
+                  this entry itself isn't unused.  */
+               if (sec == toc
+                   && !used[val >> 3]
+                   && (used[rel->r_offset >> 3]
+                       || !skip[rel->r_offset >> 3]))
+                 /* Do all the relocs again, to catch reference
+                    chains.  */
+                 repeat = 1;
+
+               used[val >> 3] = 1;
+             }
+         while (repeat);
+       }
+
+      /* Merge the used and skip arrays.  Assume that TOC
+        doublewords not appearing as either used or unused belong
+        to to an entry more than one doubleword in size.  */
+      for (drop = skip, keep = used, last = 0, some_unused = 0;
+          drop < skip + (toc->size + 7) / 8;
+          ++drop, ++keep)
+       {
+         if (*keep)
+           {
+             *drop = 0;
+             last = 0;
+           }
+         else if (*drop)
+           {
+             some_unused = 1;
+             last = 1;
+           }
+         else
+           *drop = last;
+       }
+
+      free (used);
+
+      if (some_unused)
+       {
+         bfd_byte *contents, *src;
+         unsigned long off;
+
+         /* Shuffle the toc contents, and at the same time convert the
+            skip array from booleans into offsets.  */
+         if (!bfd_malloc_and_get_section (ibfd, toc, &contents))
+           goto error_ret;
+
+         elf_section_data (toc)->this_hdr.contents = contents;
+
+         for (src = contents, off = 0, drop = skip;
+              src < contents + toc->size;
+              src += 8, ++drop)
+           {
+             if (*drop)
+               {
+                 *drop = (unsigned long) -1;
+                 off += 8;
+               }
+             else if (off != 0)
+               {
+                 *drop = off;
+                 memcpy (src - off, src, 8);
+               }
+           }
+         toc->rawsize = toc->size;
+         toc->size = src - contents - off;
+
+         /* Read toc relocs.  */
+         relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, TRUE);
+         if (relstart == NULL)
+           goto error_ret;
+
+         /* Remove unused toc relocs, and adjust those we keep.  */
+         wrel = relstart;
+         for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
+           if (skip[rel->r_offset >> 3] != (unsigned long) -1)
+             {
+               wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
+               wrel->r_info = rel->r_info;
+               wrel->r_addend = rel->r_addend;
+               ++wrel;
+             }
+         toc->reloc_count = wrel - relstart;
+
+         /* Adjust addends for relocs against the toc section sym.  */
+         for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+           {
+             if (sec->reloc_count == 0
+                 || elf_discarded_section (sec))
+               continue;
+
+             relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                                   TRUE);
+             if (relstart == NULL)
+               goto error_ret;
+
+             for (rel = relstart; rel < relstart + sec->reloc_count; ++rel)
+               {
+                 enum elf_ppc64_reloc_type r_type;
+                 unsigned long r_symndx;
+                 asection *sym_sec;
+                 struct elf_link_hash_entry *h;
+                 Elf_Internal_Sym *sym;
+
+                 r_type = ELF64_R_TYPE (rel->r_info);
+                 switch (r_type)
+                   {
+                   default:
+                     continue;
+
+                   case R_PPC64_TOC16:
+                   case R_PPC64_TOC16_LO:
+                   case R_PPC64_TOC16_HI:
+                   case R_PPC64_TOC16_HA:
+                   case R_PPC64_TOC16_DS:
+                   case R_PPC64_TOC16_LO_DS:
+                   case R_PPC64_ADDR64:
+                     break;
+                   }
+
+                 r_symndx = ELF64_R_SYM (rel->r_info);
+                 if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                                 r_symndx, ibfd))
+                   goto error_ret;
+
+                 if (sym_sec != toc || h != NULL || sym->st_value != 0)
+                   continue;
+
+                 rel->r_addend -= skip[rel->r_addend >> 3];
+               }
+           }
+
+         /* We shouldn't have local or global symbols defined in the TOC,
+            but handle them anyway.  */
+         if (local_syms != NULL)
+           {
+             Elf_Internal_Sym *sym;
+
+             for (sym = local_syms;
+                  sym < local_syms + symtab_hdr->sh_info;
+                  ++sym)
+               if (sym->st_shndx != SHN_UNDEF
+                   && (sym->st_shndx < SHN_LORESERVE
+                       || sym->st_shndx > SHN_HIRESERVE)
+                   && sym->st_value != 0
+                   && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc)
+                 {
+                   if (skip[sym->st_value >> 3] != (unsigned long) -1)
+                     sym->st_value -= skip[sym->st_value >> 3];
+                   else
+                     {
+                       (*_bfd_error_handler)
+                         (_("%s defined in removed toc entry"),
+                          bfd_elf_local_sym_name (ibfd, sym));
+                       sym->st_value = 0;
+                       sym->st_shndx = SHN_ABS;
+                     }
+                   symtab_hdr->contents = (unsigned char *) local_syms;
+                 }
+           }
+
+         /* Finally, adjust any global syms defined in the toc.  */
+         if (toc_inf.global_toc_syms)
+           {
+             toc_inf.toc = toc;
+             toc_inf.skip = skip;
+             toc_inf.global_toc_syms = FALSE;
+             elf_link_hash_traverse (elf_hash_table (info), adjust_toc_syms,
+                                     &toc_inf);
+           }
+       }
+
+      if (local_syms != NULL
+         && symtab_hdr->contents != (unsigned char *) local_syms)
+       {
+         if (!info->keep_memory)
+           free (local_syms);
+         else
+           symtab_hdr->contents = (unsigned char *) local_syms;
+       }
+      free (skip);
+    }
+
+  return TRUE;
+}
+
 /* Allocate space in .plt, .got and associated reloc sections for
    dynamic relocs.  */
 
index 4a58bbe..0e5a0e2 100644 (file)
@@ -25,6 +25,8 @@ asection *ppc64_elf_tls_setup
   (bfd *, struct bfd_link_info *);
 bfd_boolean ppc64_elf_tls_optimize
   (bfd *, struct bfd_link_info *);
+bfd_boolean ppc64_elf_edit_toc
+  (bfd *, struct bfd_link_info *);
 bfd_vma ppc64_elf_toc
   (bfd *);
 int ppc64_elf_setup_section_lists
index c88752f..cfaeacc 100644 (file)
@@ -1,9 +1,20 @@
+2004-11-04  Alan Modra  <amodra@bigpond.net.au>
+
+       * emultempl/ppc64elf.em (no_tls_opt): Rename from notlsopt.
+       (no_opd_opt, no_toc_opt): New static vars.
+       (ppc_before_allocation): Don't edit opt if no_opd_opt.  Call
+       ppc64_elf_edit_toc.
+       (OPTION_NO_OPD_OPT, OPTION_NO_TOC_OPT): Define.
+       (PARSE_AND_LIST_LONGOPTS): Add no-opd-optimize and no-toc-optimize.
+       (PARSE_AND_LIST_OPTIONS): Describe new options.
+       (PARSE_AND_LIST_ARGS_CASES): Handle them.
+
 2004-10-27  Nick Clifton  <nickc@redhat.com>
 
        * emultempl/alphaelf.em (alpha_after_parse): Add extra, NULL,
        parameter to invocation of lang_section_start.
        * emultempl/aix.em (_handle_option): Likewise.
-       
+
        * Makefile.am (eelf64alpha.c, eelf64alpha_fbsd.c,
        eelf64alpha_nbsd.c): Add dependency upon emultempl/alphaelf.em.
        * Makefile.in: Regenerate.
index 8699a82..3713fd3 100644 (file)
@@ -44,7 +44,13 @@ static bfd_signed_vma group_size = 1;
 static int dotsyms = 1;
 
 /* Whether to run tls optimization.  */
-static int notlsopt = 0;
+static int no_tls_opt = 0;
+
+/* Whether to run opd optimization.  */
+static int no_opd_opt = 0;
+
+/* Whether to run toc optimization.  */
+static int no_toc_opt = 0;
 
 /* Whether to emit symbols for stubs.  */
 static int emit_stub_syms = 0;
@@ -92,13 +98,14 @@ ppc_before_allocation (void)
 {
   if (stub_file != NULL)
     {
-      if (!ppc64_elf_edit_opd (output_bfd, &link_info, non_overlapping_opd))
+      if (!no_opd_opt
+         && !ppc64_elf_edit_opd (output_bfd, &link_info, non_overlapping_opd))
        {
-         einfo ("%X%P: can not edit opd %E\n");
+         einfo ("%X%P: can not edit %s %E\n", "opd");
          return;
        }
 
-      if (ppc64_elf_tls_setup (output_bfd, &link_info) && !notlsopt)
+      if (ppc64_elf_tls_setup (output_bfd, &link_info) && !no_tls_opt)
        {
          /* Size the sections.  This is premature, but we want to know the
             TLS segment layout so that certain optimizations can be done.  */
@@ -115,6 +122,14 @@ ppc_before_allocation (void)
          elf_tdata (output_bfd)->program_header_size = 0;
          lang_reset_memory_regions ();
        }
+
+      if (!no_toc_opt
+         && !link_info.relocatable
+         && !ppc64_elf_edit_toc (output_bfd, &link_info))
+       {
+         einfo ("%X%P: can not edit %s %E\n", "toc");
+         return;
+       }
     }
 
   gld${EMULATION_NAME}_before_allocation ();
@@ -467,7 +482,9 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_DOTSYMS                 (OPTION_STUBSYMS + 1)
 #define OPTION_NO_DOTSYMS              (OPTION_DOTSYMS + 1)
 #define OPTION_NO_TLS_OPT              (OPTION_NO_DOTSYMS + 1)
-#define OPTION_NON_OVERLAPPING_OPD     (OPTION_NO_TLS_OPT + 1)
+#define OPTION_NO_OPD_OPT              (OPTION_NO_TLS_OPT + 1)
+#define OPTION_NO_TOC_OPT              (OPTION_NO_OPD_OPT + 1)
+#define OPTION_NON_OVERLAPPING_OPD     (OPTION_NO_TOC_OPT + 1)
 '
 
 PARSE_AND_LIST_LONGOPTS='
@@ -476,6 +493,8 @@ PARSE_AND_LIST_LONGOPTS='
   { "dotsyms", no_argument, NULL, OPTION_DOTSYMS },
   { "no-dotsyms", no_argument, NULL, OPTION_NO_DOTSYMS },
   { "no-tls-optimize", no_argument, NULL, OPTION_NO_TLS_OPT },
+  { "no-opd-optimize", no_argument, NULL, OPTION_NO_OPD_OPT },
+  { "no-toc-optimize", no_argument, NULL, OPTION_NO_TOC_OPT },
   { "non-overlapping-opd", no_argument, NULL, OPTION_NON_OVERLAPPING_OPD },
 '
 
@@ -505,6 +524,12 @@ PARSE_AND_LIST_OPTIONS='
   --no-tls-optimize     Don'\''t try to optimize TLS accesses.\n"
                   ));
   fprintf (file, _("\
+  --no-opd-optimize     Don'\''t optimize the OPD section.\n"
+                  ));
+  fprintf (file, _("\
+  --no-toc-optimize     Don'\''t optimize the TOC section.\n"
+                  ));
+  fprintf (file, _("\
   --non-overlapping-opd Canonicalize .opd, so that there are no overlapping\n\
                           .opd entries.\n"
                   ));
@@ -533,7 +558,15 @@ PARSE_AND_LIST_ARGS_CASES='
       break;
 
     case OPTION_NO_TLS_OPT:
-      notlsopt = 1;
+      no_tls_opt = 1;
+      break;
+
+    case OPTION_NO_OPD_OPT:
+      no_opd_opt = 1;
+      break;
+
+    case OPTION_NO_TOC_OPT:
+      no_toc_opt = 1;
       break;
 
     case OPTION_NON_OVERLAPPING_OPD: