include/elf/
authorAlan Modra <amodra@gmail.com>
Fri, 25 Jun 2010 05:20:57 +0000 (05:20 +0000)
committerAlan Modra <amodra@gmail.com>
Fri, 25 Jun 2010 05:20:57 +0000 (05:20 +0000)
* ppc64.h (R_PPC64_LO_DS_OPT): Define.
bfd/
* elf64-ppc.c (toc_skip_enum): Define.
(ppc64_elf_edit_toc): Use two low bits of skip array as markers.
Optimize largetoc sequences.
(adjust_toc_syms): Update for skip array change.
(ppc64_elf_relocate_section): Handle R_PPC64_LO_DS_OPT.
ld/
* emultempl/ppc64elf.em (prelim_size_sections): New function.
(ppc_before_allocation): Use it.  Size sections before toc edit too.

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

index f1365e5..0153119 100644 (file)
@@ -1,5 +1,13 @@
 2010-06-25  Alan Modra  <amodra@gmail.com>
 
+       * elf64-ppc.c (toc_skip_enum): Define.
+       (ppc64_elf_edit_toc): Use two low bits of skip array as markers.
+       Optimize largetoc sequences.
+       (adjust_toc_syms): Update for skip array change.
+       (ppc64_elf_relocate_section): Handle R_PPC64_LO_DS_OPT.
+
+2010-06-25  Alan Modra  <amodra@gmail.com>
+
        * elf64-ppc.c (is_static_defined): New function.
        (get_tls_mask, ppc_type_of_stub): Use it here.
        (ppc64_elf_edit_opd): Ensure we only attempt to edit ppc64 input.
index fbc7600..116d419 100644 (file)
@@ -7846,6 +7846,8 @@ struct adjust_toc_info
   bfd_boolean global_toc_syms;
 };
 
+enum toc_skip_enum { ref_from_discarded = 1, can_optimize = 2 };
+
 static bfd_boolean
 adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 {
@@ -7874,13 +7876,13 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
       else
        i = eh->elf.root.u.def.value >> 3;
 
-      if (toc_inf->skip[i] == (unsigned long) -1)
+      if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0)
        {
          (*_bfd_error_handler)
            (_("%s defined on removed toc entry"), eh->elf.root.root.string);
          do
            ++i;
-         while (toc_inf->skip[i] == (unsigned long) -1);
+         while ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0);
          eh->elf.root.u.def.value = (bfd_vma) i << 3;
        }
 
@@ -8001,13 +8003,87 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                    goto error_ret;
                }
 
-             skip[val >> 3] = 1;
+             skip[val >> 3] = ref_from_discarded;
            }
 
          if (elf_section_data (sec)->relocs != relstart)
            free (relstart);
        }
 
+      /* For largetoc loads of address constants, we can convert
+        .  addis rx,2,addr@got@ha
+        .  ld ry,addr@got@l(rx)
+        to
+        .  addis rx,2,addr@toc@ha
+        .  addi ry,rx,addr@toc@l
+        when addr is within 2G of the toc pointer.  This then means
+        that the word storing "addr" in the toc is no longer needed.  */
+        
+      if (!ppc64_elf_tdata (ibfd)->has_small_toc_reloc
+         && toc->output_section->rawsize < (bfd_vma) 1 << 31
+         && toc->reloc_count != 0)
+       {
+         /* Read toc relocs.  */
+         relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+                                               info->keep_memory);
+         if (relstart == NULL)
+           goto error_ret;
+
+         for (rel = relstart; rel < relstart + toc->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, addr;
+
+             r_type = ELF64_R_TYPE (rel->r_info);
+             if (r_type != R_PPC64_ADDR64)
+               continue;
+
+             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 (!SYMBOL_REFERENCES_LOCAL (info, h))
+               continue;
+
+             if (h != NULL)
+               val = h->root.u.def.value;
+             else
+               val = sym->st_value;
+             val += rel->r_addend;
+             val += sym_sec->output_section->vma + sym_sec->output_offset;
+
+             /* We don't yet know the exact toc pointer value, but we
+                know it will be somewhere in the toc section.  Don't
+                optimize if the difference from any possible toc
+                pointer is outside [ff..f80008000, 7fff7fff].  */
+             addr = toc->output_section->vma + TOC_BASE_OFF;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             addr = toc->output_section->vma + toc->output_section->rawsize;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             if (skip == NULL)
+               {
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8);
+                 if (skip == NULL)
+                   goto error_ret;
+               }
+
+             skip[rel->r_offset >> 3]
+               |= can_optimize | ((rel - relstart) << 2);
+           }
+
+         if (elf_section_data (toc)->relocs != relstart)
+           free (relstart);
+       }
+
       if (skip == NULL)
        continue;
 
@@ -8100,12 +8176,37 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                if (val >= toc->size)
                  continue;
 
+               if ((skip[val >> 3] & can_optimize) != 0)
+                 {
+                   bfd_vma off;
+                   unsigned char opc;
+
+                   switch (r_type)
+                     {
+                     case R_PPC64_TOC16_HA:
+                       break;
+
+                     case R_PPC64_TOC16_LO_DS:
+                       off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
+                       if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
+                         return FALSE;
+                       if ((opc & (0x3f << 2)) == (58u << 2))
+                         break;
+                       /* Fall thru */
+
+                     default:
+                       /* Wrong sort of reloc, or not a ld.  We may
+                          as well clear ref_from_discarded too.  */
+                       skip[val >> 3] = 0;
+                     }
+                 }
+
                /* 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]))
+                       || !(skip[rel->r_offset >> 3] & ref_from_discarded)))
                  /* Do all the relocs again, to catch reference
                     chains.  */
                  repeat = 1;
@@ -8127,13 +8228,15 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
        {
          if (*keep)
            {
-             *drop = 0;
+             *drop &= ~ref_from_discarded;
+             if ((*drop & can_optimize) != 0)
+               some_unused = 1;
              last = 0;
            }
          else if (*drop)
            {
              some_unused = 1;
-             last = 1;
+             last = ref_from_discarded;
            }
          else
            *drop = last;
@@ -8145,6 +8248,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
        {
          bfd_byte *contents, *src;
          unsigned long off;
+         bfd_boolean local_toc_syms = FALSE;
 
          /* Shuffle the toc contents, and at the same time convert the
             skip array from booleans into offsets.  */
@@ -8157,11 +8261,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
               src < contents + toc->size;
               src += 8, ++drop)
            {
-             if (*drop)
-               {
-                 *drop = (unsigned long) -1;
-                 off += 8;
-               }
+             if ((*drop & (can_optimize | ref_from_discarded)) != 0)
+               off += 8;
              else if (off != 0)
                {
                  *drop = off;
@@ -8172,7 +8273,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
          toc->rawsize = toc->size;
          toc->size = src - contents - off;
 
-         /* Adjust addends for relocs against the toc section sym.  */
+         /* Adjust addends for relocs against the toc section sym,
+            and optimize any accesses we can.  */
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
            {
              if (sec->reloc_count == 0
@@ -8214,13 +8316,50 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                                  r_symndx, ibfd))
                    goto error_ret;
 
-                 if (sym_sec != toc || h != NULL || sym->st_value != 0)
+                 if (sym_sec != toc)
                    continue;
 
-                 val = rel->r_addend;
+                 if (h != NULL)
+                   val = h->root.u.def.value;
+                 else
+                   {
+                     val = sym->st_value;
+                     if (val != 0)
+                       local_toc_syms = TRUE;
+                   }
+
+                 val += rel->r_addend;
 
                  if (val > toc->rawsize)
                    val = toc->rawsize;
+                 else if ((skip[val >> 3] & ref_from_discarded) != 0)
+                   continue;
+                 else if ((skip[val >> 3] & can_optimize) != 0)
+                   {
+                     Elf_Internal_Rela *tocrel
+                       = elf_section_data (toc)->relocs + (skip[val >> 3] >> 2);
+                     unsigned long tsym = ELF64_R_SYM (tocrel->r_info);
+
+                     switch (r_type)
+                       {
+                       case R_PPC64_TOC16_HA:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_TOC16_HA);
+                         break;
+
+                       case R_PPC64_TOC16_LO_DS:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT);
+                         break;
+
+                       default:
+                         abort ();
+                       }
+                     rel->r_addend = tocrel->r_addend;
+                     elf_section_data (sec)->relocs = relstart;
+                     continue;
+                   }
+
+                 if (h != NULL || sym->st_value != 0)
+                   continue;
 
                  rel->r_addend -= skip[val >> 3];
                  elf_section_data (sec)->relocs = relstart;
@@ -8232,7 +8371,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
          /* We shouldn't have local or global symbols defined in the TOC,
             but handle them anyway.  */
-         if (local_syms != NULL)
+         if (local_toc_syms)
            {
              Elf_Internal_Sym *sym;
 
@@ -8249,14 +8388,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                    else
                      i = sym->st_value >> 3;
 
-                   if (skip[sym->st_value >> 3] == (unsigned long) -1)
+                   if ((skip[i] & (ref_from_discarded | can_optimize)) != 0)
                      {
                        (*_bfd_error_handler)
                          (_("%s defined on removed toc entry"),
                           bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
                        do
                          ++i;
-                       while (skip[i] == (unsigned long) -1);
+                       while ((skip[i] & (ref_from_discarded | can_optimize)));
                        sym->st_value = (bfd_vma) i << 3;
                      }
 
@@ -8289,7 +8428,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              /* 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)
+               if ((skip[rel->r_offset >> 3]
+                    & (ref_from_discarded | can_optimize)) == 0)
                  {
                    wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
                    wrel->r_info = rel->r_info;
@@ -11256,7 +11396,7 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (sec);
 }
 
-/* REL points to a low-part reloc on a bigtoc instruction sequence.
+/* REL points to a low-part reloc on a largetoc instruction sequence.
    Find the matching high-part reloc instruction and verify that it
    is addis REG,r2,x.  If so, return a pointer to the high-part reloc.  */
 
@@ -11565,6 +11705,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        default:
          break;
 
+       case R_PPC64_LO_DS_OPT:
+         insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+         if ((insn & (0x3f << 26)) != 58u << 26)
+           abort ();
+         insn += (14u << 26) - (58u << 26);
+         bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+         r_type = R_PPC64_TOC16_LO;
+         rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+         break;
+
        case R_PPC64_TOC16:
        case R_PPC64_TOC16_LO:
        case R_PPC64_TOC16_DS:
index 2442c50..8a8e118 100644 (file)
@@ -1,3 +1,7 @@
+2010-06-25  Alan Modra  <amodra@gmail.com>
+
+       * ppc64.h (R_PPC64_LO_DS_OPT): Define.
+
 2010-06-15  Joseph Myers  <joseph@codesourcery.com>
 
        * tic6x-attrs.h: New.
index dfd775d..a18edd6 100644 (file)
@@ -140,6 +140,10 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_type)
   RELOC_NUMBER (R_PPC64_TLSGD,            107)
   RELOC_NUMBER (R_PPC64_TLSLD,            108)
 
+#ifndef RELOC_MACROS_GEN_FUNC
+/* Fake relocation only used internally by ld.  */
+  RELOC_NUMBER (R_PPC64_LO_DS_OPT,        128)
+#endif
 /* Support STT_GNU_IFUNC plt calls.  */
   RELOC_NUMBER (R_PPC64_JMP_IREL,         247)
   RELOC_NUMBER (R_PPC64_IRELATIVE,        248)
index aadc774..975edc0 100644 (file)
@@ -1,5 +1,10 @@
 2010-06-25  Alan Modra  <amodra@gmail.com>
 
+       * emultempl/ppc64elf.em (prelim_size_sections): New function.
+       (ppc_before_allocation): Use it.  Size sections before toc edit too.
+
+2010-06-25  Alan Modra  <amodra@gmail.com>
+
        * emultempl/elf32.em (find_exp_assignment): Handle etree_provided.
 
 2010-06-20  Danny Smith  <dannysmith@users.sourceforge.net>
index f8604a6..46d0332 100644 (file)
@@ -227,6 +227,19 @@ sort_toc_sections (lang_statement_list_type *list,
 }
 
 static void
+prelim_size_sections (void)
+{
+  if (expld.phase != lang_mark_phase_enum)
+    {
+      expld.phase = lang_mark_phase_enum;
+      expld.dataseg.phase = exp_dataseg_none;
+      one_lang_size_sections_pass (NULL, FALSE);
+      /* We must not cache anything from the preliminary sizing.  */
+      lang_reset_memory_regions ();
+    }
+}
+
+static void
 ppc_before_allocation (void)
 {
   if (stub_file != NULL)
@@ -240,21 +253,20 @@ ppc_before_allocation (void)
        {
          /* Size the sections.  This is premature, but we want to know the
             TLS segment layout so that certain optimizations can be done.  */
-         expld.phase = lang_mark_phase_enum;
-         expld.dataseg.phase = exp_dataseg_none;
-         one_lang_size_sections_pass (NULL, TRUE);
+         prelim_size_sections ();
 
          if (!ppc64_elf_tls_optimize (&link_info))
            einfo ("%X%P: TLS problem %E\n");
-
-         /* We must not cache anything from the preliminary sizing.  */
-         lang_reset_memory_regions ();
        }
 
       if (!no_toc_opt
-         && !link_info.relocatable
-         && !ppc64_elf_edit_toc (&link_info))
-       einfo ("%X%P: can not edit %s %E\n", "toc");
+         && !link_info.relocatable)
+       {
+         prelim_size_sections ();
+
+         if (!ppc64_elf_edit_toc (&link_info))
+           einfo ("%X%P: can not edit %s %E\n", "toc");
+       }
 
       if (!no_toc_sort)
        {