2004-12-01 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index fca84ac87c50b7684a1eb9055d77ea1593f53faf..d230c1bdb343538b69451926cb39eca7a290382a 100644 (file)
@@ -35,6 +35,8 @@
 
 static bfd_reloc_status_type ppc64_elf_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type ppc64_elf_branch_reloc
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_brtaken_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_sectoff_reloc
@@ -49,7 +51,8 @@ static bfd_reloc_status_type ppc64_elf_toc64_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_unhandled_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
-
+static bfd_vma opd_entry_value
+  (asection *, bfd_vma, asection **, bfd_vma *);
 
 #define TARGET_LITTLE_SYM      bfd_elf64_powerpcle_vec
 #define TARGET_LITTLE_NAME     "elf64-powerpcle"
@@ -76,6 +79,7 @@ static bfd_reloc_status_type ppc64_elf_unhandled_reloc
 #define bfd_elf64_new_section_hook           ppc64_elf_new_section_hook
 #define bfd_elf64_bfd_link_hash_table_create  ppc64_elf_link_hash_table_create
 #define bfd_elf64_bfd_link_hash_table_free    ppc64_elf_link_hash_table_free
+#define bfd_elf64_get_synthetic_symtab       ppc64_elf_get_synthetic_symtab
 
 #define elf_backend_object_p                 ppc64_elf_object_p
 #define elf_backend_grok_prstatus            ppc64_elf_grok_prstatus
@@ -170,9 +174,17 @@ static bfd_reloc_status_type ppc64_elf_unhandled_reloc
 #define LIS_R0_0       0x3c000000      /* lis   %r0,0          */
 #define ORI_R0_R0_0    0x60000000      /* ori   %r0,%r0,0      */
 
-/* Instructions to save and restore floating point regs.  */
+/* Instructions used by the save and restore reg functions.  */
+#define STD_R0_0R1     0xf8010000      /* std   %r0,0(%r1)     */
+#define STD_R0_0R12    0xf80c0000      /* std   %r0,0(%r12)    */
+#define LD_R0_0R1      0xe8010000      /* ld    %r0,0(%r1)     */
+#define LD_R0_0R12     0xe80c0000      /* ld    %r0,0(%r12)    */
 #define STFD_FR0_0R1   0xd8010000      /* stfd  %fr0,0(%r1)    */
 #define LFD_FR0_0R1    0xc8010000      /* lfd   %fr0,0(%r1)    */
+#define LI_R12_0       0x39800000      /* li    %r12,0         */
+#define STVX_VR0_R12_R0        0x7c0c01ce      /* stvx  %v0,%r12,%r0   */
+#define LVX_VR0_R12_R0 0x7c0c00ce      /* lvx   %v0,%r12,%r0   */
+#define MTLR_R0                0x7c0803a6      /* mtlr  %r0            */
 #define BLR            0x4e800020      /* blr                  */
 
 /* Since .opd is an array of descriptors and each entry will end up
@@ -305,7 +317,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc64_elf_branch_reloc, /* special_function */
         "R_PPC64_ADDR14",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -354,7 +366,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc64_elf_branch_reloc, /* special_function */
         "R_PPC64_REL24",       /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -369,7 +381,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc64_elf_branch_reloc, /* special_function */
         "R_PPC64_REL14",       /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -2132,6 +2144,29 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
   return bfd_reloc_continue;
 }
 
+static bfd_reloc_status_type
+ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+                       void *data, asection *input_section,
+                       bfd *output_bfd, char **error_message)
+{
+  if (output_bfd != NULL)
+    return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+                                 input_section, output_bfd, error_message);
+
+  if (strcmp (symbol->section->name, ".opd") == 0
+      && (symbol->section->owner->flags & DYNAMIC) == 0)
+    {
+      bfd_vma dest = opd_entry_value (symbol->section,
+                                     symbol->value + reloc_entry->addend,
+                                     NULL, NULL);
+      if (dest != (bfd_vma) -1)
+       reloc_entry->addend = dest - (symbol->value
+                                     + symbol->section->output_section->vma
+                                     + symbol->section->output_offset);
+    }
+  return bfd_reloc_continue;
+}
+
 static bfd_reloc_status_type
 ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
                         void *data, asection *input_section,
@@ -2168,7 +2203,7 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
       else if ((insn & (0x14 << 21)) == (0x10 << 21))
        insn |= 0x08 << 21;
       else
-       return bfd_reloc_continue;
+       goto out;
     }
   else
     {
@@ -2190,7 +2225,9 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
        insn ^= 0x01 << 21;
     }
   bfd_put_32 (abfd, insn, (bfd_byte *) data + octets);
-  return bfd_reloc_continue;
+ out:
+  return ppc64_elf_branch_reloc (abfd, reloc_entry, symbol, data,
+                                input_section, output_bfd, error_message);
 }
 
 static bfd_reloc_status_type
@@ -2333,6 +2370,10 @@ struct ppc64_elf_obj_tdata
   asection *got;
   asection *relgot;
 
+  /* Used during garbage collection.  We attach global symbols defined
+     on removed .opd entries to this section so that the sym is removed.  */
+  asection *deleted_section;
+
   /* TLS local dynamic got entry handling.  Suppose for multiple GOT
      sections means we potentially need one of these for each input bfd.  */
   union {
@@ -2359,6 +2400,17 @@ ppc64_elf_mkobject (bfd *abfd)
   return TRUE;
 }
 
+/* Return 1 if target is one of ours.  */
+
+static bfd_boolean
+is_ppc64_elf_target (const struct bfd_target *targ)
+{
+  extern const bfd_target bfd_elf64_powerpc_vec;
+  extern const bfd_target bfd_elf64_powerpcle_vec;
+
+  return targ == &bfd_elf64_powerpc_vec || targ == &bfd_elf64_powerpcle_vec;
+}
+
 /* Fix bad default arch selected for a 64 bit input bfd when the
    default is 32 bit.  */
 
@@ -2505,6 +2557,377 @@ get_opd_info (asection * sec)
   return NULL;
 }
 \f
+/* Parameters for the qsort hook.  */
+static asection *synthetic_opd;
+static bfd_boolean synthetic_relocatable;
+
+/* qsort comparison function for ppc64_elf_get_synthetic_symtab.  */
+
+static int
+compare_symbols (const void *ap, const void *bp)
+{
+  const asymbol *a = * (const asymbol **) ap;
+  const asymbol *b = * (const asymbol **) bp;
+
+  /* Section symbols first.  */
+  if ((a->flags & BSF_SECTION_SYM) && !(b->flags & BSF_SECTION_SYM))
+    return -1;
+  if (!(a->flags & BSF_SECTION_SYM) && (b->flags & BSF_SECTION_SYM))
+    return 1;
+
+  /* then .opd symbols.  */
+  if (a->section == synthetic_opd && b->section != synthetic_opd)
+    return -1;
+  if (a->section != synthetic_opd && b->section == synthetic_opd)
+    return 1;
+
+  /* then other code symbols.  */
+  if ((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+      == (SEC_CODE | SEC_ALLOC)
+      && (b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+        != (SEC_CODE | SEC_ALLOC))
+    return -1;
+
+  if ((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+      != (SEC_CODE | SEC_ALLOC)
+      && (b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+        == (SEC_CODE | SEC_ALLOC))
+    return 1;
+
+  if (synthetic_relocatable)
+    {
+      if (a->section->id < b->section->id)
+       return -1;
+
+      if (a->section->id > b->section->id)
+       return 1;
+    }
+
+  if (a->value + a->section->vma < b->value + b->section->vma)
+    return -1;
+
+  if (a->value + a->section->vma > b->value + b->section->vma)
+    return 1;
+
+  return 0;
+}
+
+/* Search SYMS for a symbol of the given VALUE.  */
+
+static asymbol *
+sym_exists_at (asymbol **syms, long lo, long hi, int id, bfd_vma value)
+{
+  long mid;
+
+  if (id == -1)
+    {
+      while (lo < hi)
+       {
+         mid = (lo + hi) >> 1;
+         if (syms[mid]->value + syms[mid]->section->vma < value)
+           lo = mid + 1;
+         else if (syms[mid]->value + syms[mid]->section->vma > value)
+           hi = mid;
+         else
+           return syms[mid];
+       }
+    }
+  else
+    {
+      while (lo < hi)
+       {
+         mid = (lo + hi) >> 1;
+         if (syms[mid]->section->id < id)
+           lo = mid + 1;
+         else if (syms[mid]->section->id > id)
+           hi = mid;
+         else if (syms[mid]->value < value)
+           lo = mid + 1;
+         else if (syms[mid]->value > value)
+           hi = mid;
+         else
+           return syms[mid];
+       }
+    }
+  return NULL;
+}
+
+/* Create synthetic symbols, effectively restoring "dot-symbol" function
+   entry syms.  */
+
+static long
+ppc64_elf_get_synthetic_symtab (bfd *abfd,
+                               long static_count, asymbol **static_syms,
+                               long dyn_count, asymbol **dyn_syms,
+                               asymbol **ret)
+{
+  asymbol *s;
+  long i;
+  long count;
+  char *names;
+  long symcount, codesecsym, codesecsymend, secsymend, opdsymend;
+  asection *opd;
+  bfd_boolean relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
+  asymbol **syms;
+
+  *ret = NULL;
+
+  opd = bfd_get_section_by_name (abfd, ".opd");
+  if (opd == NULL)
+    return 0;
+
+  symcount = static_count;
+  if (!relocatable)
+    symcount += dyn_count;
+  if (symcount == 0)
+    return 0;
+
+  syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
+  if (syms == NULL)
+    return 0;
+
+  if (!relocatable && static_count != 0 && dyn_count != 0)
+    {
+      /* Use both symbol tables.  */
+      memcpy (syms, static_syms, static_count * sizeof (*syms));
+      memcpy (syms + static_count, dyn_syms, (dyn_count + 1) * sizeof (*syms));
+    }
+  else if (!relocatable && static_count == 0)
+    memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms));
+  else
+    memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
+
+  synthetic_opd = opd;
+  synthetic_relocatable = relocatable;
+  qsort (syms, symcount, sizeof (*syms), compare_symbols);
+
+  if (!relocatable && symcount > 1)
+    {
+      long j;
+      /* Trim duplicate syms, since we may have merged the normal and
+        dynamic symbols.  Actually, we only care about syms that have
+        different values, so trim any with the same value.  */ 
+      for (i = 1, j = 1; i < symcount; ++i)
+       if (syms[i - 1]->value + syms[i - 1]->section->vma
+           != syms[i]->value + syms[i]->section->vma)
+         syms[j++] = syms[i];
+      symcount = j;
+    }
+
+  i = 0;
+  if (syms[i]->section == opd)
+    ++i;
+  codesecsym = i;
+
+  for (; i < symcount; ++i)
+    if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+        != (SEC_CODE | SEC_ALLOC))
+       || (syms[i]->flags & BSF_SECTION_SYM) == 0)
+      break;
+  codesecsymend = i;
+
+  for (; i < symcount; ++i)
+    if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
+      break;
+  secsymend = i;
+
+  for (; i < symcount; ++i)
+    if (syms[i]->section != opd)
+      break;
+  opdsymend = i;
+
+  for (; i < symcount; ++i)
+    if ((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+       != (SEC_CODE | SEC_ALLOC))
+      break;
+  symcount = i;
+
+  count = 0;
+  if (opdsymend == secsymend)
+    goto done;
+
+  if (relocatable)
+    {
+      bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+      arelent *r;
+      size_t size;
+      long relcount;
+
+      slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+      relcount = (opd->flags & SEC_RELOC) ? opd->reloc_count : 0;
+
+      if (! relcount
+         || ! (*slurp_relocs) (abfd, opd, static_syms, FALSE))
+       goto done;
+
+      size = 0;
+      for (i = secsymend, r = opd->relocation; i < opdsymend; ++i)
+       {
+         asymbol *sym;
+
+         while (r < opd->relocation + relcount
+                && r->address < syms[i]->value + opd->vma)
+           ++r;
+
+         if (r == opd->relocation + relcount)
+           break;
+
+         if (r->address != syms[i]->value + opd->vma)
+           continue;
+
+         if (r->howto->type != R_PPC64_ADDR64)
+           continue;
+
+         sym = *r->sym_ptr_ptr;
+         if (!sym_exists_at (syms, opdsymend, symcount,
+                             sym->section->id, sym->value + r->addend))
+           {
+             ++count;
+             size += sizeof (asymbol);
+             size += strlen (syms[i]->name) + 2;
+           }
+       }
+
+      s = *ret = bfd_malloc (size);
+      if (s == NULL)
+       {
+         count = 0;
+         goto done;
+       }
+
+      names = (char *) (s + count);
+
+      for (i = secsymend, r = opd->relocation; i < opdsymend; ++i)
+       {
+         asymbol *sym;
+
+         while (r < opd->relocation + relcount
+                && r->address < syms[i]->value + opd->vma)
+           ++r;
+
+         if (r == opd->relocation + relcount)
+           break;
+
+         if (r->address != syms[i]->value + opd->vma)
+           continue;
+
+         if (r->howto->type != R_PPC64_ADDR64)
+           continue;
+
+         sym = *r->sym_ptr_ptr;
+         if (!sym_exists_at (syms, opdsymend, symcount,
+                             sym->section->id, sym->value + r->addend))
+           {
+             size_t len;
+
+             *s = *syms[i];
+             s->section = sym->section;
+             s->value = sym->value + r->addend;
+             s->name = names;
+             *names++ = '.';
+             len = strlen (syms[i]->name);
+             memcpy (names, syms[i]->name, len + 1);
+             names += len + 1;
+             s++;
+           }
+       }
+    }
+  else
+    {
+      bfd_byte *contents;
+      size_t size;
+
+      if (!bfd_malloc_and_get_section (abfd, opd, &contents))
+       {
+         if (contents)
+           {
+           free_contents_and_exit:
+             free (contents);
+           }
+         goto done;
+       }
+
+      size = 0;
+      for (i = secsymend; i < opdsymend; ++i)
+       {
+         bfd_vma ent;
+
+         ent = bfd_get_64 (abfd, contents + syms[i]->value);
+         if (!sym_exists_at (syms, opdsymend, symcount, -1, ent))
+           {
+             ++count;
+             size += sizeof (asymbol);
+             size += strlen (syms[i]->name) + 2;
+           }
+       }
+
+      s = *ret = bfd_malloc (size);
+      if (s == NULL)
+       {
+         count = 0;
+         goto free_contents_and_exit;
+       }
+
+      names = (char *) (s + count);
+
+      for (i = secsymend; i < opdsymend; ++i)
+       {
+         bfd_vma ent;
+
+         ent = bfd_get_64 (abfd, contents + syms[i]->value);
+         if (!sym_exists_at (syms, opdsymend, symcount, -1, ent))
+           {
+             long lo, hi;
+             size_t len;
+             asection *sec = abfd->sections;
+
+             *s = *syms[i];
+             lo = codesecsym;
+             hi = codesecsymend;
+             while (lo < hi)
+               {
+                 long mid = (lo + hi) >> 1;
+                 if (syms[mid]->section->vma < ent)
+                   lo = mid + 1;
+                 else if (syms[mid]->section->vma > ent)
+                   hi = mid;
+                 else
+                   {
+                     sec = syms[mid]->section;
+                     break;
+                   }
+               }
+
+             if (lo >= hi && lo > codesecsym)
+               sec = syms[lo - 1]->section;
+
+             for (; sec != NULL; sec = sec->next)
+               {
+                 if (sec->vma > ent)
+                   break;
+                 if ((sec->flags & SEC_ALLOC) == 0
+                     || (sec->flags & SEC_LOAD) == 0)
+                   break;
+                 if ((sec->flags & SEC_CODE) != 0)
+                   s->section = sec;
+               }
+             s->value = ent - s->section->vma;
+             s->name = names;
+             *names++ = '.';
+             len = strlen (syms[i]->name);
+             memcpy (names, syms[i]->name, len + 1);
+             names += len + 1;
+             s++;
+           }
+       }
+      free (contents);
+    }
+
+ done:
+  free (syms);
+  return count;
+}
+\f
 /* The following functions are specific to the ELF linker, while
    functions above are used generally.  Those named ppc64_elf_* are
    called by the main ELF linker code.  They appear in this file more
@@ -2783,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.  */
@@ -2869,6 +3292,9 @@ struct ppc_link_hash_table
   /* Statistics.  */
   unsigned long stub_count[ppc_stub_plt_call];
 
+  /* Number of stubs against global syms.  */
+  unsigned long stub_globals;
+
   /* Set if we should emit symbols for stubs.  */
   unsigned int emit_stub_syms:1;
 
@@ -3112,6 +3538,8 @@ ppc_stub_name (const asection *input_section,
                   (int) rel->r_addend & 0xffffffff);
        }
     }
+  if (stub_name[len - 2] == '+' && stub_name[len - 1] == '0')
+    stub_name[len - 2] = 0;
   return stub_name;
 }
 
@@ -3121,12 +3549,11 @@ ppc_stub_name (const asection *input_section,
 static struct ppc_stub_hash_entry *
 ppc_get_stub_entry (const asection *input_section,
                    const asection *sym_sec,
-                   struct elf_link_hash_entry *hash,
+                   struct ppc_link_hash_entry *h,
                    const Elf_Internal_Rela *rel,
                    struct ppc_link_hash_table *htab)
 {
   struct ppc_stub_hash_entry *stub_entry;
-  struct ppc_link_hash_entry *h = (struct ppc_link_hash_entry *) hash;
   const asection *id_sec;
 
   /* If this input section is part of a group of sections sharing one
@@ -3251,7 +3678,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
       || ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
     return FALSE;
 
-  if (info->shared)
+  if (info->shared || info->emitrelocations)
     {
       flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
               | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
@@ -3339,7 +3766,6 @@ ppc64_elf_copy_indirect_symbol
    struct elf_link_hash_entry *ind)
 {
   struct ppc_link_hash_entry *edir, *eind;
-  flagword mask;
 
   edir = (struct ppc_link_hash_entry *) dir;
   eind = (struct ppc_link_hash_entry *) ind;
@@ -3383,18 +3809,18 @@ ppc64_elf_copy_indirect_symbol
   edir->is_func_descriptor |= eind->is_func_descriptor;
   edir->tls_mask |= eind->tls_mask;
 
-  mask = (ELF_LINK_HASH_REF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR
-         | ELF_LINK_HASH_REF_REGULAR_NONWEAK | ELF_LINK_NON_GOT_REF
-         | ELF_LINK_HASH_NEEDS_PLT);
   /* If called to transfer flags for a weakdef during processing
-     of elf_adjust_dynamic_symbol, don't copy ELF_LINK_NON_GOT_REF.
+     of elf_adjust_dynamic_symbol, don't copy NON_GOT_REF.
      We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
-  if (ELIMINATE_COPY_RELOCS
-      && eind->elf.root.type != bfd_link_hash_indirect
-      && (edir->elf.elf_link_hash_flags & ELF_LINK_HASH_DYNAMIC_ADJUSTED) != 0)
-    mask &= ~ELF_LINK_NON_GOT_REF;
+  if (!(ELIMINATE_COPY_RELOCS
+       && eind->elf.root.type != bfd_link_hash_indirect
+       && edir->elf.dynamic_adjusted))
+    edir->elf.non_got_ref |= eind->elf.non_got_ref;
 
-  edir->elf.elf_link_hash_flags |= eind->elf.elf_link_hash_flags & mask;
+  edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
+  edir->elf.ref_regular |= eind->elf.ref_regular;
+  edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
+  edir->elf.needs_plt |= eind->elf.needs_plt;
 
   /* If we were called to copy over info for a weak sym, that's all.  */
   if (eind->elf.root.type != bfd_link_hash_indirect)
@@ -3565,7 +3991,10 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
    new ABI object defines "bar".  Well, at least, undefined dot symbols
    are made weak.  This stops later archive searches from including an
    object if we already have a function descriptor definition.  It also
-   prevents the linker complaining about undefined symbols.  */
+   prevents the linker complaining about undefined symbols.
+   We also check and correct mismatched symbol visibility here.  The
+   most restrictive visibility of the function descriptor and the
+   function entry symbol is used.  */
 
 static bfd_boolean
 add_symbol_adjust (struct elf_link_hash_entry *h, void *inf)
@@ -3581,8 +4010,7 @@ add_symbol_adjust (struct elf_link_hash_entry *h, void *inf)
   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_undefined
-      || h->root.root.string[0] != '.')
+  if (h->root.root.string[0] != '.')
     return TRUE;
 
   info = inf;
@@ -3591,9 +4019,19 @@ add_symbol_adjust (struct elf_link_hash_entry *h, void *inf)
   fdh = get_fdh (eh, htab);
   if (fdh != NULL)
     {
-      eh->elf.root.type = bfd_link_hash_undefweak;
-      eh->was_undefined = 1;
-      htab->twiddled_syms = 1;
+      unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1;
+      unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1;
+      if (entry_vis < descr_vis)
+       fdh->elf.other += entry_vis - descr_vis;
+      else if (entry_vis > descr_vis)
+       eh->elf.other += descr_vis - entry_vis;
+
+      if (eh->elf.root.type == bfd_link_hash_undefined)
+       {
+         eh->elf.root.type = bfd_link_hash_undefweak;
+         eh->was_undefined = 1;
+         htab->twiddled_syms = 1;
+       }
     }
 
   return TRUE;
@@ -3606,6 +4044,9 @@ ppc64_elf_check_directives (bfd *abfd ATTRIBUTE_UNUSED,
   struct ppc_link_hash_table *htab;
 
   htab = ppc_hash_table (info);
+  if (!is_ppc64_elf_target (htab->elf.root.creator))
+    return TRUE;
+
   elf_link_hash_traverse (&htab->elf, add_symbol_adjust, info);
 
   /* We need to fix the undefs list for any syms we have twiddled to
@@ -3622,22 +4063,22 @@ ppc64_elf_check_directives (bfd *abfd ATTRIBUTE_UNUSED,
          if (h->type != bfd_link_hash_undefined
              && h->type != bfd_link_hash_common)
            {
-             *pun = h->und_next;
-             h->und_next = NULL;
+             *pun = h->u.undef.next;
+             h->u.undef.next = NULL;
              if (h == htab->elf.root.undefs_tail)
                {
                  if (pun == &htab->elf.root.undefs)
                    htab->elf.root.undefs_tail = NULL;
                  else
-                   /* pun points at an und_next field.  Go back to
+                   /* pun points at an u.undef.next field.  Go back to
                       the start of the link_hash_entry.  */
                    htab->elf.root.undefs_tail = (struct bfd_link_hash_entry *)
-                     ((char *) pun - ((char *) &h->und_next - (char *) h));
+                     ((char *) pun - ((char *) &h->u.undef.next - (char *) h));
                  break;
                }
            }
          else
-           pun = &h->und_next;
+           pun = &h->u.undef.next;
        }
 
       htab->twiddled_syms = 0;
@@ -3713,7 +4154,7 @@ update_plt_info (bfd *abfd, struct ppc_link_hash_entry *eh, bfd_vma addend)
       eh->elf.plt.plist = ent;
     }
   ent->plt.refcount += 1;
-  eh->elf.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+  eh->elf.needs_plt = 1;
   eh->is_func = 1;
   return TRUE;
 }
@@ -3773,7 +4214,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       };
       bfd_size_type amt;
 
-      amt = sec->size * sizeof (union opd_info) / 24;
+      amt = sec->size * sizeof (union opd_info) / 8;
       opd_sym_map = bfd_zalloc (abfd, amt);
       if (opd_sym_map == NULL)
        return FALSE;
@@ -4080,7 +4521,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  if (s == NULL)
                    return FALSE;
                  else if (s != sec)
-                   opd_sym_map[rel->r_offset / 24] = s;
+                   opd_sym_map[rel->r_offset / 8] = s;
                }
            }
          /* Fall through.  */
@@ -4109,7 +4550,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_TOC:
          if (h != NULL && !info->shared)
            /* We may need a copy reloc.  */
-           h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF;
+           h->non_got_ref = 1;
 
          /* Don't propagate .opd relocs.  */
          if (NO_OPD_RELOCS && opd_sym_map != NULL)
@@ -4142,14 +4583,12 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                   || (h != NULL
                       && (! info->symbolic
                           || h->root.type == bfd_link_hash_defweak
-                          || (h->elf_link_hash_flags
-                              & ELF_LINK_HASH_DEF_REGULAR) == 0))))
+                          || !h->def_regular))))
              || (ELIMINATE_COPY_RELOCS
                  && !info->shared
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
-                     || (h->elf_link_hash_flags
-                         & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+                     || !h->def_regular)))
            {
              struct ppc_dyn_relocs *p;
              struct ppc_dyn_relocs **head;
@@ -4259,6 +4698,32 @@ opd_entry_value (asection *opd_sec,
   bfd *opd_bfd = opd_sec->owner;
   Elf_Internal_Rela *lo, *hi, *look;
 
+  /* No relocs implies we are linking a --just-symbols object.  */
+  if (opd_sec->reloc_count == 0)
+    {
+      bfd_vma val;
+
+      if (!bfd_get_section_contents (opd_bfd, opd_sec, &val, offset, 8))
+       return (bfd_vma) -1;
+      
+      if (code_sec != NULL)
+       {
+         asection *sec, *likely = NULL;
+         for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
+           if (sec->vma <= val
+               && (sec->flags & SEC_LOAD) != 0
+               && (sec->flags & SEC_ALLOC) != 0)
+             likely = sec;
+         if (likely != NULL)
+           {
+             *code_sec = likely;
+             if (code_off != NULL)
+               *code_off = val - likely->vma;
+           }
+       }
+      return val;
+    }
+
   /* Go find the opd reloc at the sym address.  */
   lo = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
   BFD_ASSERT (lo != NULL);
@@ -4459,7 +4924,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
          if (!rsec->gc_mark)
            _bfd_elf_gc_mark (info, rsec, ppc64_elf_gc_mark_hook);
 
-         rsec = opd_sym_section[sym->st_value / 24];
+         rsec = opd_sym_section[sym->st_value / 8];
        }
     }
 
@@ -4605,61 +5070,305 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
   return TRUE;
 }
 
-/* Called via elf_link_hash_traverse to transfer dynamic linking
-   information on function code symbol entries to their corresponding
-   function descriptor symbol entries.  */
-static bfd_boolean
-func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
+/* The maximum size of .sfpr.  */
+#define SFPR_MAX (218*4)
+
+struct sfpr_def_parms
 {
-  struct bfd_link_info *info;
-  struct ppc_link_hash_table *htab;
-  struct plt_entry *ent;
-  struct ppc_link_hash_entry *fh;
-  struct ppc_link_hash_entry *fdh;
-  bfd_boolean force_local;
+  const char name[12];
+  unsigned char lo, hi;
+  bfd_byte * (*write_ent) (bfd *, bfd_byte *, int);
+  bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
+};
 
-  fh = (struct ppc_link_hash_entry *) h;
-  if (fh->elf.root.type == bfd_link_hash_indirect)
-    return TRUE;
+/* Auto-generate _save*, _rest* functions in .sfpr.  */
 
-  if (fh->elf.root.type == bfd_link_hash_warning)
-    fh = (struct ppc_link_hash_entry *) fh->elf.root.u.i.link;
+static unsigned int
+sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  unsigned int i;
+  size_t len = strlen (parm->name);
+  bfd_boolean writing = FALSE;
+  char sym[16];
 
-  info = inf;
-  htab = ppc_hash_table (info);
+  memcpy (sym, parm->name, len);
+  sym[len + 2] = 0;
 
-  /* If this is a function code symbol, transfer dynamic linking
-     information to the function descriptor symbol.  */
-  if (!fh->is_func)
-    return TRUE;
+  for (i = parm->lo; i <= parm->hi; i++)
+    {
+      struct elf_link_hash_entry *h;
 
-  for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
-    if (ent->plt.refcount > 0)
-      break;
-  if (ent == NULL
-      || fh->elf.root.root.string[0] != '.'
-      || fh->elf.root.root.string[1] == '\0')
-    return TRUE;
+      sym[len + 0] = i / 10 + '0';
+      sym[len + 1] = i % 10 + '0';
+      h = elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
+      if (h != NULL
+         && !h->def_regular)
+       {
+         h->root.type = bfd_link_hash_defined;
+         h->root.u.def.section = htab->sfpr;
+         h->root.u.def.value = htab->sfpr->size;
+         h->type = STT_FUNC;
+         h->def_regular = 1;
+         _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
+         writing = TRUE;
+         if (htab->sfpr->contents == NULL)
+           {
+             htab->sfpr->contents = bfd_alloc (htab->elf.dynobj, SFPR_MAX);
+             if (htab->sfpr->contents == NULL)
+               return FALSE;
+           }
+       }
+      if (writing)
+       {
+         bfd_byte *p = htab->sfpr->contents + htab->sfpr->size;
+         if (i != parm->hi)
+           p = (*parm->write_ent) (htab->elf.dynobj, p, i);
+         else
+           p = (*parm->write_tail) (htab->elf.dynobj, p, i);
+         htab->sfpr->size = p - htab->sfpr->contents;
+       }
+    }
 
-  /* Find the corresponding function descriptor symbol.  Create it
-     as undefined if necessary.  */
+  return TRUE;
+}
 
-  fdh = get_fdh (fh, htab);
-  if (fdh != NULL)
-    while (fdh->elf.root.type == bfd_link_hash_indirect
-          || fdh->elf.root.type == bfd_link_hash_warning)
-      fdh = (struct ppc_link_hash_entry *) fdh->elf.root.u.i.link;
+static bfd_byte *
+savegpr0 (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, STD_R0_0R1 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
 
-  if (fdh == NULL
-      && info->shared
-      && (fh->elf.root.type == bfd_link_hash_undefined
-         || fh->elf.root.type == bfd_link_hash_undefweak))
-    {
-      bfd *abfd;
-      asymbol *newsym;
-      struct bfd_link_hash_entry *bh;
+static bfd_byte *
+savegpr0_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = savegpr0 (abfd, p, r);
+  bfd_put_32 (abfd, STD_R0_0R1 + 16, p);
+  p = p + 4;
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
 
-      abfd = fh->elf.root.u.undef.abfd;
+static bfd_byte *
+restgpr0 (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LD_R0_0R1 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restgpr0_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LD_R0_0R1 + 16, p);
+  p = p + 4;
+  p = restgpr0 (abfd, p, r);
+  bfd_put_32 (abfd, MTLR_R0, p);
+  p = p + 4;
+  if (r == 29)
+    {
+      p = restgpr0 (abfd, p, 30);
+      p = restgpr0 (abfd, p, 31);
+    }
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savegpr1 (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, STD_R0_0R12 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savegpr1_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = savegpr1 (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restgpr1 (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LD_R0_0R12 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restgpr1_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = restgpr1 (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savefpr (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, STFD_FR0_0R1 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savefpr0_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = savefpr (abfd, p, r);
+  bfd_put_32 (abfd, STD_R0_0R1 + 16, p);
+  p = p + 4;
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restfpr (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LFD_FR0_0R1 + (r << 21) + (1 << 16) - (32 - r) * 8, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restfpr0_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LD_R0_0R1 + 16, p);
+  p = p + 4;
+  p = restfpr (abfd, p, r);
+  bfd_put_32 (abfd, MTLR_R0, p);
+  p = p + 4;
+  if (r == 29)
+    {
+      p = restfpr (abfd, p, 30);
+      p = restfpr (abfd, p, 31);
+    }
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savefpr1_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = savefpr (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restfpr1_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = restfpr (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+savevr (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LI_R12_0 + (1 << 16) - (32 - r) * 16, p);
+  p = p + 4;
+  bfd_put_32 (abfd, STVX_VR0_R12_R0 + (r << 21), p);
+  return p + 4;
+}
+
+static bfd_byte *
+savevr_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = savevr (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+static bfd_byte *
+restvr (bfd *abfd, bfd_byte *p, int r)
+{
+  bfd_put_32 (abfd, LI_R12_0 + (1 << 16) - (32 - r) * 16, p);
+  p = p + 4;
+  bfd_put_32 (abfd, LVX_VR0_R12_R0 + (r << 21), p);
+  return p + 4;
+}
+
+static bfd_byte *
+restvr_tail (bfd *abfd, bfd_byte *p, int r)
+{
+  p = restvr (abfd, p, r);
+  bfd_put_32 (abfd, BLR, p);
+  return p + 4;
+}
+
+/* Called via elf_link_hash_traverse to transfer dynamic linking
+   information on function code symbol entries to their corresponding
+   function descriptor symbol entries.  */
+
+static bfd_boolean
+func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
+{
+  struct bfd_link_info *info;
+  struct ppc_link_hash_table *htab;
+  struct plt_entry *ent;
+  struct ppc_link_hash_entry *fh;
+  struct ppc_link_hash_entry *fdh;
+  bfd_boolean force_local;
+
+  fh = (struct ppc_link_hash_entry *) h;
+  if (fh->elf.root.type == bfd_link_hash_indirect)
+    return TRUE;
+
+  if (fh->elf.root.type == bfd_link_hash_warning)
+    fh = (struct ppc_link_hash_entry *) fh->elf.root.u.i.link;
+
+  info = inf;
+  htab = ppc_hash_table (info);
+
+  /* Resolve undefined references to dot-symbols as the value
+     in the function descriptor, if we have one in a regular object.
+     This is to satisfy cases like ".quad .foo".  Calls to functions
+     in dynamic objects are handled elsewhere.  */
+  if (fh->elf.root.type == bfd_link_hash_undefweak
+      && fh->was_undefined
+      && (fh->oh->elf.root.type == bfd_link_hash_defined
+         || fh->oh->elf.root.type == bfd_link_hash_defweak)
+      && get_opd_info (fh->oh->elf.root.u.def.section) != NULL
+      && opd_entry_value (fh->oh->elf.root.u.def.section,
+                         fh->oh->elf.root.u.def.value,
+                         &fh->elf.root.u.def.section,
+                         &fh->elf.root.u.def.value) != (bfd_vma) -1)
+    {
+      fh->elf.root.type = fh->oh->elf.root.type;
+      fh->elf.forced_local = 1;
+    }
+
+  /* If this is a function code symbol, transfer dynamic linking
+     information to the function descriptor symbol.  */
+  if (!fh->is_func)
+    return TRUE;
+
+  for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
+    if (ent->plt.refcount > 0)
+      break;
+  if (ent == NULL
+      || fh->elf.root.root.string[0] != '.'
+      || fh->elf.root.root.string[1] == '\0')
+    return TRUE;
+
+  /* Find the corresponding function descriptor symbol.  Create it
+     as undefined if necessary.  */
+
+  fdh = get_fdh (fh, htab);
+  if (fdh != NULL)
+    while (fdh->elf.root.type == bfd_link_hash_indirect
+          || fdh->elf.root.type == bfd_link_hash_warning)
+      fdh = (struct ppc_link_hash_entry *) fdh->elf.root.u.i.link;
+
+  if (fdh == NULL
+      && info->shared
+      && (fh->elf.root.type == bfd_link_hash_undefined
+         || fh->elf.root.type == bfd_link_hash_undefweak))
+    {
+      bfd *abfd;
+      asymbol *newsym;
+      struct bfd_link_hash_entry *bh;
+
+      abfd = fh->elf.root.u.undef.abfd;
       newsym = bfd_make_empty_symbol (abfd);
       newsym->name = fh->elf.root.root.string + 1;
       newsym->section = bfd_und_section_ptr;
@@ -4676,27 +5385,26 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
          return FALSE;
        }
       fdh = (struct ppc_link_hash_entry *) bh;
-      fdh->elf.elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+      fdh->elf.non_elf = 0;
       fdh->elf.size = 24;
       fdh->elf.type = STT_OBJECT;
     }
 
   if (fdh != NULL
-      && (fdh->elf.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0
+      && !fdh->elf.forced_local
       && (info->shared
-         || (fdh->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-         || (fdh->elf.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0
+         || fdh->elf.def_dynamic
+         || fdh->elf.ref_dynamic
          || (fdh->elf.root.type == bfd_link_hash_undefweak
              && ELF_ST_VISIBILITY (fdh->elf.other) == STV_DEFAULT)))
     {
       if (fdh->elf.dynindx == -1)
        if (! bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
          return FALSE;
-      fdh->elf.elf_link_hash_flags
-       |= (fh->elf.elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
-                                     | ELF_LINK_HASH_REF_DYNAMIC
-                                     | ELF_LINK_HASH_REF_REGULAR_NONWEAK
-                                     | ELF_LINK_NON_GOT_REF));
+      fdh->elf.ref_regular |= fh->elf.ref_regular;
+      fdh->elf.ref_dynamic |= fh->elf.ref_dynamic;
+      fdh->elf.ref_regular_nonweak |= fh->elf.ref_regular_nonweak;
+      fdh->elf.non_got_ref |= fh->elf.non_got_ref;
       if (ELF_ST_VISIBILITY (fh->elf.other) == STV_DEFAULT)
        {
          struct plt_entry **ep = &fdh->elf.plt.plist;
@@ -4704,7 +5412,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
            ep = &(*ep)->next;
          *ep = fh->elf.plt.plist;
          fh->elf.plt.plist = NULL;
-         fdh->elf.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+         fdh->elf.needs_plt = 1;
        }
       fdh->is_func_descriptor = 1;
       fdh->oh = fh;
@@ -4720,120 +5428,57 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
      linker dragging in a definition from a static library.  */
   force_local
     = (info->shared
-       && ((fh->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+       && (!fh->elf.def_regular
           || fdh == NULL
-          || (fdh->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
-          || (fdh->elf.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0));
+          || !fdh->elf.def_regular
+          || fdh->elf.forced_local));
   _bfd_elf_link_hash_hide_symbol (info, &fh->elf, force_local);
 
   return TRUE;
 }
 
-#define MIN_SAVE_FPR 14
-#define MAX_SAVE_FPR 31
-
 /* Called near the start of bfd_elf_size_dynamic_sections.  We use
    this hook to a) provide some gcc support functions, and b) transfer
    dynamic linking information gathered so far on function code symbol
    entries, to their corresponding function descriptor symbol entries.  */
+
 static bfd_boolean
 ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
                            struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
-  unsigned int lowest_savef = MAX_SAVE_FPR + 2;
-  unsigned int lowest_restf = MAX_SAVE_FPR + 2;
   unsigned int i;
-  struct elf_link_hash_entry *h;
-  bfd_byte *p;
-  char sym[10];
+  const struct sfpr_def_parms funcs[] =
+    {
+      { "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
+      { "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
+      { "_restgpr0_", 30, 31, restgpr0, restgpr0_tail },
+      { "_savegpr1_", 14, 31, savegpr1, savegpr1_tail },
+      { "_restgpr1_", 14, 31, restgpr1, restgpr1_tail },
+      { "_savefpr_", 14, 31, savefpr, savefpr0_tail },
+      { "_restfpr_", 14, 29, restfpr, restfpr0_tail },
+      { "_restfpr_", 30, 31, restfpr, restfpr0_tail },
+      { "._savef", 14, 31, savefpr, savefpr1_tail },
+      { "._restf", 14, 31, restfpr, restfpr1_tail },
+      { "_savevr_", 20, 31, savevr, savevr_tail },
+      { "_restvr_", 20, 31, restvr, restvr_tail }
+    };
 
   htab = ppc_hash_table (info);
-
   if (htab->sfpr == NULL)
     /* We don't have any relocs.  */
     return TRUE;
 
-  /* First provide any missing ._savef* and ._restf* functions.  */
-  memcpy (sym, "._savef14", 10);
-  for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
-    {
-      sym[7] = i / 10 + '0';
-      sym[8] = i % 10 + '0';
-      h = elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
-      if (h != NULL
-         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
-       {
-         if (lowest_savef > i)
-           lowest_savef = i;
-         h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = htab->sfpr;
-         h->root.u.def.value = (i - lowest_savef) * 4;
-         h->type = STT_FUNC;
-         h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
-         _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
-       }
-    }
-
-  memcpy (sym, "._restf14", 10);
-  for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
-    {
-      sym[7] = i / 10 + '0';
-      sym[8] = i % 10 + '0';
-      h = elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
-      if (h != NULL
-         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
-       {
-         if (lowest_restf > i)
-           lowest_restf = i;
-         h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = htab->sfpr;
-         h->root.u.def.value = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
-                                + (i - lowest_restf) * 4);
-         h->type = STT_FUNC;
-         h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
-         _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
-       }
-    }
+  /* Provide any missing _save* and _rest* functions.  */
+  htab->sfpr->size = 0;
+  for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
+    if (!sfpr_define (info, &funcs[i]))
+      return FALSE;
 
   elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
 
-  htab->sfpr->size = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
-                     + (MAX_SAVE_FPR + 2 - lowest_restf) * 4);
-
   if (htab->sfpr->size == 0)
-    {
-      _bfd_strip_section_from_output (info, htab->sfpr);
-      return TRUE;
-    }
-
-  p = bfd_alloc (htab->elf.dynobj, htab->sfpr->size);
-  if (p == NULL)
-    return FALSE;
-  htab->sfpr->contents = p;
-
-  for (i = lowest_savef; i <= MAX_SAVE_FPR; i++)
-    {
-      unsigned int fpr = i << 21;
-      unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
-      bfd_put_32 (htab->elf.dynobj, STFD_FR0_0R1 + fpr + stackoff, p);
-      p += 4;
-    }
-  if (lowest_savef <= MAX_SAVE_FPR)
-    {
-      bfd_put_32 (htab->elf.dynobj, BLR, p);
-      p += 4;
-    }
-
-  for (i = lowest_restf; i <= MAX_SAVE_FPR; i++)
-    {
-      unsigned int fpr = i << 21;
-      unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
-      bfd_put_32 (htab->elf.dynobj, LFD_FR0_0R1 + fpr + stackoff, p);
-      p += 4;
-    }
-  if (lowest_restf <= MAX_SAVE_FPR)
-    bfd_put_32 (htab->elf.dynobj, BLR, p);
+    _bfd_strip_section_from_output (info, htab->sfpr);
 
   return TRUE;
 }
@@ -4856,7 +5501,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   /* Deal with function syms.  */
   if (h->type == STT_FUNC
-      || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+      || h->needs_plt)
     {
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
@@ -4870,7 +5515,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
              && h->root.type == bfd_link_hash_undefweak))
        {
          h->plt.plist = NULL;
-         h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+         h->needs_plt = 0;
        }
     }
   else
@@ -4879,16 +5524,14 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
-  if (h->weakdef != NULL)
+  if (h->u.weakdef != NULL)
     {
-      BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
-                 || h->weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->weakdef->root.u.def.section;
-      h->root.u.def.value = h->weakdef->root.u.def.value;
+      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+                 || h->u.weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->u.weakdef->root.u.def.section;
+      h->root.u.def.value = h->u.weakdef->root.u.def.value;
       if (ELIMINATE_COPY_RELOCS)
-       h->elf_link_hash_flags
-         = ((h->elf_link_hash_flags & ~ELF_LINK_NON_GOT_REF)
-            | (h->weakdef->elf_link_hash_flags & ELF_LINK_NON_GOT_REF));
+       h->non_got_ref = h->u.weakdef->non_got_ref;
       return TRUE;
     }
 
@@ -4901,7 +5544,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   /* If there are no references to this symbol that do not use the
      GOT, we don't need to generate a copy reloc.  */
-  if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0)
+  if (!h->non_got_ref)
     return TRUE;
 
   if (ELIMINATE_COPY_RELOCS)
@@ -4921,7 +5564,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
         we'll be keeping the dynamic relocs and avoiding the copy reloc.  */
       if (p == NULL)
        {
-         h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
+         h->non_got_ref = 0;
          return TRUE;
        }
     }
@@ -4959,7 +5602,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
     {
       htab->relbss->size += sizeof (Elf64_External_Rela);
-      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
+      h->needs_copy = 1;
     }
 
   /* We need to figure out the alignment required for this symbol.  I
@@ -5189,7 +5832,7 @@ get_tls_mask (char **tls_maskp, unsigned long *toc_symndx,
   if ((h == NULL
        || ((h->root.type == bfd_link_hash_defined
            || h->root.type == bfd_link_hash_defweak)
-          && !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+          && !h->def_dynamic))
       && (next_r == -1 || next_r == -2))
     return 1 - next_r;
   return 1;
@@ -5223,12 +5866,22 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
   opd_adjust = get_opd_info (sym_sec);
   if (opd_adjust != NULL)
     {
-      long adjust = opd_adjust[eh->elf.root.u.def.value / 24];
+      long adjust = opd_adjust[eh->elf.root.u.def.value / 8];
       if (adjust == -1)
        {
          /* This entry has been deleted.  */
+         asection *dsec = ppc64_elf_tdata (sym_sec->owner)->deleted_section;
+         if (dsec == NULL)
+           {
+             for (dsec = sym_sec->owner->sections; dsec; dsec = dsec->next)
+               if (elf_discarded_section (dsec))
+                 {
+                   ppc64_elf_tdata (sym_sec->owner)->deleted_section = dsec;
+                   break;
+                 }
+           }
          eh->elf.root.u.def.value = 0;
-         eh->elf.root.u.def.section = &bfd_abs_section;
+         eh->elf.root.u.def.section = dsec;
        }
       else
        eh->elf.root.u.def.value += adjust;
@@ -5244,10 +5897,12 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
    applications.  */
 
 bfd_boolean
-ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
+ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
+                   bfd_boolean non_overlapping)
 {
   bfd *ibfd;
   bfd_boolean some_edited = FALSE;
+  asection *need_pad = NULL;
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
@@ -5259,23 +5914,27 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
       bfd_vma offset;
       bfd_size_type amt;
       long *opd_adjust;
-      bfd_boolean need_edit;
+      bfd_boolean need_edit, add_aux_fields;
+      bfd_size_type cnt_16b = 0;
 
       sec = bfd_get_section_by_name (ibfd, ".opd");
       if (sec == NULL)
        continue;
 
-      amt = sec->size * sizeof (long) / 24;
+      amt = sec->size * sizeof (long) / 8;
       opd_adjust = get_opd_info (sec);
       if (opd_adjust == NULL)
        {
-         /* Must be a ld -r link.  ie. check_relocs hasn't been
-            called.  */
+         /* check_relocs hasn't been called.  Must be a ld -r link
+            or --just-symbols object.   */
          opd_adjust = bfd_zalloc (obfd, amt);
          ppc64_elf_section_data (sec)->opd.adjust = opd_adjust;
        }
       memset (opd_adjust, 0, amt);
 
+      if (sec->sec_info_type == ELF_INFO_TYPE_JUST_SYMS)
+       continue;
+
       if (sec->output_section == bfd_abs_section_ptr)
        continue;
 
@@ -5296,6 +5955,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
       /* First run through the relocs to check they are sane, and to
         determine whether we need to edit this opd section.  */
       need_edit = FALSE;
+      need_pad = sec;
       offset = 0;
       relend = relstart + sec->reloc_count;
       for (rel = relstart; rel < relend; )
@@ -5306,7 +5966,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
          struct elf_link_hash_entry *h;
          Elf_Internal_Sym *sym;
 
-         /* .opd contains a regular array of 24 byte entries.  We're
+         /* .opd contains a regular array of 16 or 24 byte entries.  We're
             only interested in the reloc pointing to a function entry
             point.  */
          if (rel->r_offset != offset
@@ -5318,6 +5978,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                 Also, there's nothing to prevent someone putting
                 something silly in .opd with the assembler.  No .opd
                 optimization for them!  */
+           broken_opd:
              (*_bfd_error_handler)
                (_("%B: .opd is not a regular array of opd entries"), ibfd);
              need_edit = FALSE;
@@ -5365,19 +6026,54 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
              || sym_sec->output_section == bfd_abs_section_ptr)
            need_edit = TRUE;
 
-         offset += 24;
          rel += 2;
-         /* Allow for the possibility of a reloc on the third word.  */
-         if (rel < relend
-             && rel->r_offset == offset - 8)
-           rel += 1;
+         if (rel == relend
+             || (rel + 1 == relend && rel->r_offset == offset + 16))
+           {
+             if (sec->size == offset + 24)
+               {
+                 need_pad = NULL;
+                 break;
+               }
+             if (rel == relend && sec->size == offset + 16)
+               {
+                 cnt_16b++;
+                 break;
+               }
+             goto broken_opd;
+           }
+
+         if (rel->r_offset == offset + 24)
+           offset += 24;
+         else if (rel->r_offset != offset + 16)
+           goto broken_opd;
+         else if (rel + 1 < relend
+                  && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
+           {
+             offset += 16;
+             cnt_16b++;
+           }
+         else if (rel + 2 < relend
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
+                  && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
+           {
+             offset += 24;
+             rel += 1;
+           }
+         else
+           goto broken_opd;
        }
 
-      if (need_edit)
+      add_aux_fields = non_overlapping && cnt_16b > 0;
+
+      if (need_edit || add_aux_fields)
        {
          Elf_Internal_Rela *write_rel;
          bfd_byte *rptr, *wptr;
+         bfd_byte *new_contents = NULL;
          bfd_boolean skip;
+         long opd_ent_size;
 
          /* This seems a waste of time as input .opd sections are all
             zeros as generated by gcc, but I suppose there's no reason
@@ -5406,9 +6102,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
 
          wptr = sec->contents;
          rptr = sec->contents;
+         new_contents = sec->contents;
+
+         if (add_aux_fields)
+           {
+             new_contents = bfd_malloc (sec->size + cnt_16b * 8);
+             if (new_contents == NULL)
+               return FALSE;
+             need_pad = FALSE;
+             wptr = new_contents;
+           }
+
          write_rel = relstart;
          skip = FALSE;
          offset = 0;
+         opd_ent_size = 0;
          for (rel = relstart; rel < relend; rel++)
            {
              unsigned long r_symndx;
@@ -5424,6 +6132,19 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
              if (rel->r_offset == offset)
                {
                  struct ppc_link_hash_entry *fdh = NULL;
+
+                 /* See if the .opd entry is full 24 byte or
+                    16 byte (with fd_aux entry overlapped with next
+                    fd_func).  */
+                 opd_ent_size = 24;
+                 if ((rel + 2 == relend && sec->size == offset + 16)
+                     || (rel + 3 < relend
+                         && rel[2].r_offset == offset + 16
+                         && rel[3].r_offset == offset + 24
+                         && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
+                         && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+                   opd_ent_size = 16;
+
                  if (h != NULL
                      && h->root.root.string[0] == '.')
                    fdh = get_fdh ((struct ppc_link_hash_entry *) h,
@@ -5440,7 +6161,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                          fdh->elf.root.u.def.value = 0;
                          fdh->elf.root.u.def.section = sym_sec;
                        }
-                     opd_adjust[rel->r_offset / 24] = -1;
+                     opd_adjust[rel->r_offset / 8] = -1;
                    }
                  else
                    {
@@ -5455,7 +6176,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                             for local symbols, because various places
                             in the generic ELF code use the value
                             stored in u.def.value.  */
-                         fdh->elf.root.u.def.value = wptr - sec->contents;
+                         fdh->elf.root.u.def.value = wptr - new_contents;
                          fdh->adjust_done = 1;
                        }
 
@@ -5465,14 +6186,20 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                         for the function descriptor sym which we
                         don't have at the moment.  So keep an
                         array of adjustments.  */
-                     opd_adjust[rel->r_offset / 24] = wptr - rptr;
+                     opd_adjust[rel->r_offset / 8]
+                       = (wptr - new_contents) - (rptr - sec->contents);
 
                      if (wptr != rptr)
-                       memcpy (wptr, rptr, 24);
-                     wptr += 24;
+                       memcpy (wptr, rptr, opd_ent_size);
+                     wptr += opd_ent_size;
+                     if (add_aux_fields && opd_ent_size == 16)
+                       {
+                         memset (wptr, '\0', 8);
+                         wptr += 8;
+                       }
                    }
-                 rptr += 24;
-                 offset += 24;
+                 rptr += opd_ent_size;
+                 offset += opd_ent_size;
                }
 
              if (skip)
@@ -5510,15 +6237,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                  /* We need to adjust any reloc offsets to point to the
                     new opd entries.  While we're at it, we may as well
                     remove redundant relocs.  */
-                 rel->r_offset += wptr - rptr;
+                 rel->r_offset += opd_adjust[(offset - opd_ent_size) / 8];
                  if (write_rel != rel)
                    memcpy (write_rel, rel, sizeof (*rel));
                  ++write_rel;
                }
            }
 
-         sec->size = wptr - sec->contents;
+         sec->size = wptr - new_contents;
          sec->reloc_count = write_rel - relstart;
+         if (add_aux_fields)
+           {
+             free (sec->contents);
+             sec->contents = new_contents;
+           }
+
          /* Fudge the size too, as this is used later in
             elf_bfd_final_link if we are emitting relocs.  */
          elf_section_data (sec)->rel_hdr.sh_size
@@ -5542,6 +6275,40 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
   if (some_edited)
     elf_link_hash_traverse (elf_hash_table (info), adjust_opd_syms, NULL);
 
+  /* If we are doing a final link and the last .opd entry is just 16 byte
+     long, add a 8 byte padding after it.  */
+  if (need_pad != NULL && !info->relocatable)
+    {
+      bfd_byte *p;
+
+      if ((need_pad->flags & SEC_IN_MEMORY) == 0)
+       {
+         BFD_ASSERT (need_pad->size > 0);
+
+         p = bfd_malloc (need_pad->size + 8);
+         if (p == NULL)
+           return FALSE;
+
+         if (! bfd_get_section_contents (need_pad->owner, need_pad,
+                                         p, 0, need_pad->size))
+           return FALSE;
+
+         need_pad->contents = p;
+         need_pad->flags |= (SEC_IN_MEMORY | SEC_HAS_CONTENTS);
+       }
+      else
+       {
+         p = bfd_realloc (need_pad->contents, need_pad->size + 8);
+         if (p == NULL)
+           return FALSE;
+
+         need_pad->contents = p;
+       }
+
+      memset (need_pad->contents + need_pad->size, 0, 8);
+      need_pad->size += 8;
+    }
+
   return TRUE;
 }
 
@@ -5660,7 +6427,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                ok_tprel = FALSE;
                is_local = FALSE;
                if (h == NULL
-                   || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC))
+                   || !h->def_dynamic)
                  {
                    is_local = TRUE;
                    value += sym_sec->output_offset;
@@ -5878,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.  */
 
@@ -5939,13 +7145,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       if (!doneone)
        {
          h->plt.plist = NULL;
-         h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+         h->needs_plt = 0;
        }
     }
   else
     {
       h->plt.plist = NULL;
-      h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+      h->needs_plt = 0;
     }
 
   eh = (struct ppc_link_hash_entry *) h;
@@ -5983,14 +7189,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
           Undefined weak syms won't yet be marked as dynamic,
           nor will all TLS symbols.  */
        if (h->dynindx == -1
-           && (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+           && !h->forced_local)
          {
            if (! bfd_elf_link_record_dynamic_symbol (info, h))
              return FALSE;
          }
 
        if ((gent->tls_type & TLS_LD) != 0
-           && !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC))
+           && !h->def_dynamic)
          {
            gent->got.offset = ppc64_tlsld_got (gent->owner)->offset;
            continue;
@@ -6057,14 +7263,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         symbols which turn out to need copy relocs or are not
         dynamic.  */
 
-      if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
-         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+      if (!h->non_got_ref
+         && h->def_dynamic
+         && !h->def_regular)
        {
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
-             && (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+             && !h->forced_local)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
@@ -6161,7 +7367,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
 
-      if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+      if (!is_ppc64_elf_target (ibfd->xvec))
        continue;
 
       if (ppc64_tlsld_got (ibfd)->refcount > 0)
@@ -6331,6 +7537,9 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
+      if (!is_ppc64_elf_target (ibfd->xvec))
+       continue;
+
       s = ppc64_elf_tdata (ibfd)->got;
       if (s != NULL && s != htab->got)
        {
@@ -6512,7 +7721,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   bfd_byte *p;
   unsigned int indx;
   struct plt_entry *ent;
-  bfd_vma off;
+  bfd_vma dest, off;
   int size;
 
   /* Massage our args to the form they really have.  */
@@ -6531,9 +7740,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
     case ppc_stub_long_branch:
     case ppc_stub_long_branch_r2off:
       /* Branches are relative.  This is where we are going to.  */
-      off = (stub_entry->target_value
-            + stub_entry->target_section->output_offset
-            + stub_entry->target_section->output_section->vma);
+      off = dest = (stub_entry->target_value
+                   + stub_entry->target_section->output_offset
+                   + stub_entry->target_section->output_section->vma);
 
       /* And this is where we are coming from.  */
       off -= (stub_entry->stub_offset
@@ -6560,6 +7769,67 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       bfd_put_32 (htab->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
 
       BFD_ASSERT (off + (1 << 25) < (bfd_vma) (1 << 26));
+
+      if (info->emitrelocations)
+       {
+         Elf_Internal_Rela *relocs, *r;
+         struct bfd_elf_section_data *elfsec_data;
+
+         elfsec_data = elf_section_data (stub_entry->stub_sec);
+         relocs = elfsec_data->relocs;
+         if (relocs == NULL)
+           {
+             bfd_size_type relsize;
+             relsize = stub_entry->stub_sec->reloc_count * sizeof (*relocs);
+             relocs = bfd_alloc (htab->stub_bfd, relsize);
+             if (relocs == NULL)
+               return FALSE;
+             elfsec_data->relocs = relocs;
+             elfsec_data->rel_hdr.sh_size = relsize;
+             elfsec_data->rel_hdr.sh_entsize = 24;
+             stub_entry->stub_sec->reloc_count = 0;
+           }
+         r = relocs + stub_entry->stub_sec->reloc_count;
+         stub_entry->stub_sec->reloc_count += 1;
+         r->r_offset = loc - stub_entry->stub_sec->contents;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
+         r->r_addend = dest;
+         if (stub_entry->h != NULL)
+           {
+             struct elf_link_hash_entry **hashes;
+             unsigned long symndx;
+             struct ppc_link_hash_entry *h;
+
+             hashes = elf_sym_hashes (htab->stub_bfd);
+             if (hashes == NULL)
+               {
+                 bfd_size_type hsize;
+
+                 hsize = (htab->stub_globals + 1) * sizeof (*hashes);
+                 hashes = bfd_zalloc (htab->stub_bfd, hsize);
+                 if (hashes == NULL)
+                   return FALSE;
+                 elf_sym_hashes (htab->stub_bfd) = hashes;
+                 htab->stub_globals = 1;
+               }
+             symndx = htab->stub_globals++;
+             h = stub_entry->h;
+             hashes[symndx] = &h->elf;
+             r->r_info = ELF64_R_INFO (symndx, R_PPC64_REL24);
+             if (h->oh != NULL && h->oh->is_func)
+               h = h->oh;
+             if (h->elf.root.u.def.section != stub_entry->target_section)
+               /* H is an opd symbol.  The addend must be zero.  */
+               r->r_addend = 0;
+             else
+               {
+                 off = (h->elf.root.u.def.value
+                        + h->elf.root.u.def.section->output_offset
+                        + h->elf.root.u.def.section->output_section->vma);
+                 r->r_addend -= off;
+               }
+           }
+       }
       break;
 
     case ppc_stub_plt_branch:
@@ -6582,7 +7852,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       bfd_put_64 (htab->brlt->owner, off,
                  htab->brlt->contents + br_entry->offset);
 
-      if (info->shared)
+      if (htab->relbrlt != NULL)
        {
          /* Create a reloc for the branch lookup table entry.  */
          Elf_Internal_Rela rela;
@@ -6702,16 +7972,26 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
   stub_entry->stub_sec->size += size;
 
-  if (htab->emit_stub_syms
-      && !(stub_entry->stub_type == ppc_stub_plt_call
-          && stub_entry->h->oh != NULL
-          && stub_entry->h->oh->elf.root.type == bfd_link_hash_defined
-          && stub_entry->h->oh->elf.root.u.def.section == stub_entry->stub_sec
-          && stub_entry->h->oh->elf.root.u.def.value == stub_entry->stub_offset))
+  if (htab->emit_stub_syms)
     {
       struct elf_link_hash_entry *h;
-      h = elf_link_hash_lookup (&htab->elf, stub_entry->root.string,
-                               TRUE, FALSE, FALSE);
+      size_t len1, len2;
+      char *name;
+      const char *const stub_str[] = { "long_branch",
+                                      "long_branch_r2off",
+                                      "plt_branch",
+                                      "plt_branch_r2off",
+                                      "plt_call" };
+
+      len1 = strlen (stub_str[stub_entry->stub_type - 1]);
+      len2 = strlen (stub_entry->root.string);
+      name = bfd_malloc (len1 + len2 + 2);
+      if (name == NULL)
+       return FALSE;
+      memcpy (name, stub_entry->root.string, 9);
+      memcpy (name + 9, stub_str[stub_entry->stub_type - 1], len1);
+      memcpy (name + len1 + 9, stub_entry->root.string + 8, len2 - 8 + 1);
+      h = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
       if (h == NULL)
        return FALSE;
       if (h->root.type == bfd_link_hash_new)
@@ -6719,10 +7999,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          h->root.type = bfd_link_hash_defined;
          h->root.u.def.section = stub_entry->stub_sec;
          h->root.u.def.value = stub_entry->stub_offset;
-         h->elf_link_hash_flags = (ELF_LINK_HASH_REF_REGULAR
-                                   | ELF_LINK_HASH_DEF_REGULAR
-                                   | ELF_LINK_HASH_REF_REGULAR_NONWEAK
-                                   | ELF_LINK_FORCED_LOCAL);
+         h->ref_regular = 1;
+         h->def_regular = 1;
+         h->ref_regular_nonweak = 1;
+         h->forced_local = 1;
+         h->non_elf = 0;
        }
     }
 
@@ -6814,7 +8095,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              br_entry->offset = htab->brlt->size;
              htab->brlt->size += 8;
 
-             if (info->shared)
+             if (htab->relbrlt != NULL)
                htab->relbrlt->size += sizeof (Elf64_External_Rela);
            }
 
@@ -6823,6 +8104,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          if (stub_entry->stub_type != ppc_stub_plt_branch)
            size = 28;
        }
+
+      if (info->emitrelocations
+         && (stub_entry->stub_type == ppc_stub_long_branch
+             || stub_entry->stub_type == ppc_stub_long_branch_r2off))
+       stub_entry->stub_sec->reloc_count += 1;
     }
 
   stub_entry->stub_sec->size += size;
@@ -7309,7 +8595,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
 
                      if (hash == NULL)
                        {
-                         long adjust = opd_adjust[sym_value / 24];
+                         long adjust = opd_adjust[sym_value / 8];
                          if (adjust == -1)
                            continue;
                          sym_value += adjust;
@@ -7409,6 +8695,10 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                  stub_entry->target_section = code_sec;
                  stub_entry->h = hash;
                  stub_entry->addend = irela->r_addend;
+
+                 if (stub_entry->h != NULL)
+                   htab->stub_globals += 1;
+
                  stub_changed = TRUE;
                }
 
@@ -7436,10 +8726,13 @@ ppc64_elf_size_stubs (bfd *output_bfd,
           stub_sec != NULL;
           stub_sec = stub_sec->next)
        if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
-         stub_sec->size = 0;
+         {
+           stub_sec->size = 0;
+           stub_sec->reloc_count = 0;
+         }
 
       htab->brlt->size = 0;
-      if (info->shared)
+      if (htab->relbrlt != NULL)
        htab->relbrlt->size = 0;
 
       bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
@@ -7573,10 +8866,11 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
              h->root.type = bfd_link_hash_defined;
              h->root.u.def.section = htab->glink;
              h->root.u.def.value = 0;
-             h->elf_link_hash_flags = (ELF_LINK_HASH_REF_REGULAR
-                                       | ELF_LINK_HASH_DEF_REGULAR
-                                       | ELF_LINK_HASH_REF_REGULAR_NONWEAK
-                                       | ELF_LINK_FORCED_LOCAL);
+             h->ref_regular = 1;
+             h->def_regular = 1;
+             h->ref_regular_nonweak = 1;
+             h->forced_local = 1;
+             h->non_elf = 0;
            }
        }
       p = htab->glink->contents;
@@ -7644,7 +8938,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
       if (htab->brlt->contents == NULL)
        return FALSE;
     }
-  if (info->shared && htab->relbrlt->size != 0)
+  if (htab->relbrlt != NULL && htab->relbrlt->size != 0)
     {
       htab->relbrlt->contents = bfd_zalloc (htab->relbrlt->owner,
                                            htab->relbrlt->size);
@@ -7681,13 +8975,14 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
       if (*stats == NULL)
        return FALSE;
 
-      sprintf (*stats, _("linker stubs in %u groups\n"
+      sprintf (*stats, _("linker stubs in %u group%s\n"
                         "  branch       %lu\n"
                         "  toc adjust   %lu\n"
                         "  long branch  %lu\n"
                         "  long toc adj %lu\n"
                         "  plt call     %lu"),
               stub_sec_count,
+              stub_sec_count == 1 ? "" : "s",
               htab->stub_count[ppc_stub_long_branch - 1],
               htab->stub_count[ppc_stub_long_branch_r2off - 1],
               htab->stub_count[ppc_stub_plt_branch - 1],
@@ -7786,6 +9081,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
     ppc_howto_init ();
 
   htab = ppc_hash_table (info);
+
+  /* Don't relocate stub sections.  */
+  if (input_section->owner == htab->stub_bfd)
+    return TRUE;
+
   local_got_ents = elf_local_got_ents (input_bfd);
   TOCstart = elf_gp (output_bfd);
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
@@ -7801,8 +9101,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       bfd_reloc_status_type r;
       Elf_Internal_Sym *sym;
       asection *sec;
-      struct elf_link_hash_entry *h;
-      struct elf_link_hash_entry *fdh;
+      struct elf_link_hash_entry *h_elf;
+      struct ppc_link_hash_entry *h;
+      struct ppc_link_hash_entry *fdh;
       const char *sym_name;
       unsigned long r_symndx, toc_symndx;
       char tls_mask, tls_gd, tls_type;
@@ -7829,7 +9130,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
       sym = NULL;
       sec = NULL;
-      h = NULL;
+      h_elf = NULL;
       sym_name = NULL;
       unresolved_reloc = FALSE;
       warned = FALSE;
@@ -7847,7 +9148,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          opd_adjust = get_opd_info (sec);
          if (opd_adjust != NULL)
            {
-             long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 24];
+             long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 8];
              if (adjust == -1)
                relocation = 0;
              else
@@ -7858,11 +9159,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        {
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
-                                  h, sec, relocation,
+                                  h_elf, sec, relocation,
                                   unresolved_reloc, warned);
-         sym_name = h->root.root.string;
-         sym_type = h->type;
+         sym_name = h_elf->root.root.string;
+         sym_type = h_elf->type;
        }
+      h = (struct ppc_link_hash_entry *) h_elf;
 
       /* TLS optimizations.  Replace instruction sequences and relocs
         based on information we collected in tls_optimize.  We edit
@@ -7874,7 +9176,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       if (IS_PPC64_TLS_RELOC (r_type))
        {
          if (h != NULL)
-           tls_mask = ((struct ppc_link_hash_entry *) h)->tls_mask;
+           tls_mask = h->tls_mask;
          else if (local_got_ents != NULL)
            {
              char *lgot_masks;
@@ -7900,8 +9202,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       if (r_symndx != 0
          && r_type != R_PPC64_NONE
          && (h == NULL
-             || h->root.type == bfd_link_hash_defined
-             || h->root.type == bfd_link_hash_defweak)
+             || h->elf.root.type == bfd_link_hash_defined
+             || h->elf.root.type == bfd_link_hash_defweak)
          && IS_PPC64_TLS_RELOC (r_type) != (sym_type == STT_TLS))
        {
          if (r_type == R_PPC64_TLS && tls_mask != 0)
@@ -8257,9 +9559,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          stub_entry = NULL;
          fdh = h;
          if (((h != NULL
-               && (((fdh = &((struct ppc_link_hash_entry *) h)->oh->elf) != NULL
-                    && fdh->plt.plist != NULL)
-                   || (fdh = h)->plt.plist != NULL))
+               && (((fdh = h->oh) != NULL
+                    && fdh->elf.plt.plist != NULL)
+                   || (fdh = h)->elf.plt.plist != NULL))
               || (sec != NULL
                   && sec->output_section != NULL
                   && sec->id <= htab->top_id
@@ -8298,7 +9600,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        can_plt_call = TRUE;
                    }
                  else if (h != NULL
-                          && strcmp (h->root.root.string,
+                          && strcmp (h->elf.root.root.string,
                                      ".__libc_start_main") == 0)
                    {
                      /* Allow crt1 branch to go via a toc adjusting stub.  */
@@ -8364,7 +9666,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  >= 2 * max_br_offset)
              && r_type != R_PPC64_ADDR14_BRTAKEN
              && r_type != R_PPC64_ADDR14_BRNTAKEN)
-           stub_entry = ppc_get_stub_entry (input_section, sec, h, rel, htab);
+           stub_entry = ppc_get_stub_entry (input_section, sec, h, rel,
+                                            htab);
 
          if (stub_entry != NULL)
            {
@@ -8404,7 +9707,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             We can thus call a weak function without first
             checking whether the function is defined.  */
          else if (h != NULL
-                  && h->root.type == bfd_link_hash_undefweak
+                  && h->elf.root.type == bfd_link_hash_undefweak
                   && r_type == R_PPC64_REL24
                   && relocation == 0
                   && rel->r_addend == 0)
@@ -8483,7 +9786,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
            if (tls_type == (TLS_TLS | TLS_LD)
                && (h == NULL
-                   || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+                   || !h->elf.def_dynamic))
              offp = &ppc64_tlsld_got (input_bfd)->offset;
            else
              {
@@ -8492,9 +9795,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (h != NULL)
                  {
                    bfd_boolean dyn = htab->elf.dynamic_sections_created;
-                   if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+                   if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared,
+                                                         &h->elf)
                        || (info->shared
-                           && SYMBOL_REFERENCES_LOCAL (info, h)))
+                           && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
                      /* This is actually a static link, or it is a
                         -Bsymbolic link and the symbol is defined
                         locally, or the symbol was forced to be local
@@ -8502,10 +9806,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      ;
                    else
                      {
-                       indx = h->dynindx;
+                       indx = h->elf.dynindx;
                        unresolved_reloc = FALSE;
                      }
-                   ent = h->got.glist;
+                   ent = h->elf.got.glist;
                  }
                else
                  {
@@ -8544,8 +9848,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                *offp = off | 1;
                if ((info->shared || indx != 0)
                    && (h == NULL
-                       || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-                       || h->root.type != bfd_link_hash_undefweak))
+                       || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
+                       || h->elf.root.type != bfd_link_hash_undefweak))
                  {
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
@@ -8653,7 +9957,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (htab->plt != NULL)
            {
              struct plt_entry *ent;
-             for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+             for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next)
                if (ent->addend == rel->r_addend
                    && ent->plt.offset != (bfd_vma) -1)
                  {
@@ -8793,17 +10097,17 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          if ((info->shared
               && (h == NULL
-                  || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-                  || h->root.type != bfd_link_hash_undefweak)
+                  || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
+                  || h->elf.root.type != bfd_link_hash_undefweak)
               && (MUST_BE_DYN_RELOC (r_type)
-                  || !SYMBOL_CALLS_LOCAL (info, h)))
+                  || !SYMBOL_CALLS_LOCAL (info, &h->elf)))
              || (ELIMINATE_COPY_RELOCS
                  && !info->shared
                  && h != NULL
-                 && h->dynindx != -1
-                 && (h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
-                 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-                 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0))
+                 && h->elf.dynindx != -1
+                 && !h->elf.non_got_ref
+                 && h->elf.def_dynamic
+                 && !h->elf.def_regular))
            {
              Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
@@ -8842,10 +10146,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (!SYMBOL_REFERENCES_LOCAL (info, h)
+             else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
                       && !is_opd
                       && r_type != R_PPC64_TOC)
-               outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
+               outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
              else
                {
                  /* This symbol is local, or marked to become local,
@@ -9056,7 +10360,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
         not process them.  */
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0))
+              && h->elf.def_dynamic))
        {
          (*_bfd_error_handler)
            (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
@@ -9064,7 +10368,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             input_section,
             (long) rel->r_offset,
             ppc64_elf_howto_table[(int) r_type]->name,
-            h->root.root.string);
+            h->elf.root.root.string);
          ret = FALSE;
        }
 
@@ -9085,7 +10389,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (warned)
                continue;
              if (h != NULL
-                 && h->root.type == bfd_link_hash_undefweak
+                 && h->elf.root.type == bfd_link_hash_undefweak
                  && ppc64_elf_howto_table[r_type]->pc_relative)
                {
                  /* Assume this is a call protected by other code that
@@ -9098,7 +10402,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                }
 
              if (!((*info->callbacks->reloc_overflow)
-                   (info, sym_name, ppc64_elf_howto_table[r_type]->name,
+                   (info, (h ? &h->elf.root : NULL), sym_name,
+                    ppc64_elf_howto_table[r_type]->name,
                     rel->r_addend, input_bfd, input_section, rel->r_offset)))
                return FALSE;
            }
@@ -9143,7 +10448,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
   if (!info->relocatable)
     value -= input_sec->output_section->vma;
 
-  adjust = opd_adjust[value / 24];
+  adjust = opd_adjust[value / 8];
   if (adjust == -1)
     elfsym->st_value = 0;
   else
@@ -9194,7 +10499,7 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
        bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
       }
 
-  if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
+  if (h->needs_copy)
     {
       Elf_Internal_Rela rela;
       bfd_byte *loc;
@@ -9365,10 +10670,15 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
     }
 
   /* We need to handle writing out multiple GOT sections ourselves,
-     since we didn't add them to DYNOBJ.  */
+     since we didn't add them to DYNOBJ.  We know dynobj is the first
+     bfd.  */
   while ((dynobj = dynobj->link_next) != NULL)
     {
       asection *s;
+
+      if (!is_ppc64_elf_target (dynobj->xvec))
+       continue;
+
       s = ppc64_elf_tdata (dynobj)->got;
       if (s != NULL
          && s->size != 0
This page took 0.060707 seconds and 4 git commands to generate.