2005-05-02 H.J. Lu <hongjiu.lu@intel.com>
[external/binutils.git] / bfd / elf64-ppc.c
index 91f4619..040c037 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
@@ -2153,7 +2153,8 @@ ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
     return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
                                  input_section, output_bfd, error_message);
 
-  if (strcmp (symbol->section->name, ".opd") == 0)
+  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,
@@ -2379,6 +2380,9 @@ struct ppc64_elf_obj_tdata
     bfd_signed_vma refcount;
     bfd_vma offset;
   } tlsld_got;
+
+  /* A copy of relocs before they are modified for --emit-relocs.  */
+  Elf_Internal_Rela *opd_relocs;
 };
 
 #define ppc64_elf_tdata(bfd) \
@@ -2399,6 +2403,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.  */
 
@@ -2549,7 +2564,7 @@ get_opd_info (asection * sec)
 static asection *synthetic_opd;
 static bfd_boolean synthetic_relocatable;
 
-/* Helper routine for ppc64_elf_get_synthetic_symtab.  */
+/* qsort comparison function for ppc64_elf_get_synthetic_symtab.  */
 
 static int
 compare_symbols (const void *ap, const void *bp)
@@ -2557,16 +2572,19 @@ compare_symbols (const void *ap, const void *bp)
   const asymbol *a = * (const asymbol **) ap;
   const asymbol *b = * (const asymbol **) bp;
 
-  if ((a->flags & BSF_SECTION_SYM) == 0 && (b->flags & BSF_SECTION_SYM))
+  /* 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) == 0)
+  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))
@@ -2597,38 +2615,63 @@ compare_symbols (const void *ap, const void *bp)
   return 0;
 }
 
-/* Helper routine for ppc64_elf_get_synthetic_symtab.  */
+/* Search SYMS for a symbol of the given VALUE.  */
 
-static int
-compare_relocs (const void *ap, const void *bp)
+static asymbol *
+sym_exists_at (asymbol **syms, long lo, long hi, int id, bfd_vma value)
 {
-  const arelent *a = * (const arelent **) ap;
-  const arelent *b = * (const arelent **) bp;
-
-  if (a->address < b->address)
-    return -1;
+  long mid;
 
-  if (a->address > b->address)
-    return 1;
-
-  return 0;
+  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.  */
+/* Create synthetic symbols, effectively restoring "dot-symbol" function
+   entry syms.  */
 
 static long
-ppc64_elf_get_synthetic_symtab (bfd *abfd, asymbol **relsyms, asymbol **ret)
+ppc64_elf_get_synthetic_symtab (bfd *abfd,
+                               long static_count, asymbol **static_syms,
+                               long dyn_count, asymbol **dyn_syms,
+                               asymbol **ret)
 {
   asymbol *s;
-  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
-  arelent **relocs, **r;
-  long count, i;
-  size_t size;
+  long i;
+  long count;
   char *names;
-  asymbol **syms = NULL;
-  long symcount = 0, opdsymcount, relcount;
-  asection *relopd, *opd;
+  long symcount, codesecsym, codesecsymend, secsymend, opdsymend;
+  asection *opd;
   bfd_boolean relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
+  asymbol **syms;
 
   *ret = NULL;
 
@@ -2636,297 +2679,259 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, asymbol **relsyms, asymbol **ret)
   if (opd == NULL)
     return 0;
 
-  if ((bfd_get_file_flags (abfd) & HAS_SYMS))
-    {
-      long storage;
-      storage = bfd_get_symtab_upper_bound (abfd);
-      if (storage < 0)
-       return 0;
-
-      if (storage)
-       {
-         syms = bfd_malloc (storage);
-         if (syms == NULL)
-           return 0;
-       }
+  symcount = static_count;
+  if (!relocatable)
+    symcount += dyn_count;
+  if (symcount == 0)
+    return 0;
 
-      symcount = bfd_canonicalize_symtab (abfd, syms);
-      if (symcount < 0)
-       {
-         free (syms);
-         return 0;
-       }
+  syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
+  if (syms == NULL)
+    return -1;
 
-      if (symcount == 0)
-       {
-         free (syms);
-         syms = NULL;
-       }
+  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));
 
-  if (symcount == 0)
+  synthetic_opd = opd;
+  synthetic_relocatable = relocatable;
+  qsort (syms, symcount, sizeof (*syms), compare_symbols);
+
+  if (!relocatable && symcount > 1)
     {
-      long storage;
+      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;
+    }
 
-      storage = bfd_get_dynamic_symtab_upper_bound (abfd);
-      if (storage < 0)
-       return 0;
+  i = 0;
+  if (syms[i]->section == opd)
+    ++i;
+  codesecsym = i;
 
-      if (storage)
-       {
-         syms = bfd_malloc (storage);
-         if (syms == NULL)
-           return 0;
-       }
+  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;
 
-      symcount = bfd_canonicalize_dynamic_symtab (abfd, syms);
-      if (symcount < 0)
-       {
-         free (syms);
-         return 0;
-       }
-    }
+  for (; i < symcount; ++i)
+    if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
+      break;
+  secsymend = i;
 
-  synthetic_opd = opd;
-  synthetic_relocatable = relocatable;
-  qsort (syms, symcount, sizeof (asymbol *), compare_symbols);
+  for (; i < symcount; ++i)
+    if (syms[i]->section != opd)
+      break;
+  opdsymend = i;
 
-  opdsymcount = symcount;
-  for (i = 0; i < symcount; ++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)
     {
-      if (syms[i]->flags & BSF_SECTION_SYM)
+      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 == 0)
+       goto done;
+
+      if (!(*slurp_relocs) (abfd, opd, static_syms, FALSE))
        {
-         if (opdsymcount == symcount)
-           opdsymcount = i;
-         symcount = i;
-         break;
+         count = -1;
+         goto done;
        }
 
-      if (syms[i]->section == opd)
-       continue;
+      size = 0;
+      for (i = secsymend, r = opd->relocation; i < opdsymend; ++i)
+       {
+         asymbol *sym;
 
-      if (opdsymcount == symcount)
-       opdsymcount = i;
+         while (r < opd->relocation + relcount
+                && r->address < syms[i]->value + opd->vma)
+           ++r;
 
-      if ((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-         != (SEC_CODE | SEC_ALLOC))
-       {
-         symcount = i;
-         break;
-       }
-    }
+         if (r == opd->relocation + relcount)
+           break;
 
-  if (opdsymcount == 0)
-    {
-      free (syms);
-      return 0;
-    }
+         if (r->address != syms[i]->value + opd->vma)
+           continue;
 
-  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
-  if (! relocatable)
-    {
-      relopd = bfd_get_section_by_name (abfd, ".rela.opd");
-      if (relopd == NULL)
-       {
-         relopd = bfd_get_section_by_name (abfd, ".rela.dyn");
-         if (relopd == NULL)
+         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))
            {
-             free (syms);
-             return 0;
+             ++count;
+             size += sizeof (asymbol);
+             size += strlen (syms[i]->name) + 2;
            }
        }
-      relcount = relopd->size / 24;
 
-      if (! relcount
-         || ! (*slurp_relocs) (abfd, relopd, relsyms, TRUE))
-       {
-         free (syms);
-         return 0;
-       }
-    }
-  else
-    {
-      relopd = opd;
-      relcount = (opd->flags & SEC_RELOC) ? opd->reloc_count : 0;
-
-      if (! relcount
-         || ! (*slurp_relocs) (abfd, relopd, relsyms, FALSE))
+      s = *ret = bfd_malloc (size);
+      if (s == NULL)
        {
-         free (syms);
-         return 0;
+         count = -1;
+         goto done;
        }
-    }
 
-  relocs = bfd_malloc (relcount * sizeof (arelent **));
-  if (relocs == NULL)
-    {
-      free (syms);
-      return 0;
-    }
-
-  for (i = 0; i < relcount; ++i)
-    relocs[i] = &relopd->relocation[i];
+      names = (char *) (s + count);
 
-  qsort (relocs, relcount, sizeof (*relocs), compare_relocs);
-
-  size = 0;
-  count = 0;
-  for (i = 0, r = relocs; i < opdsymcount; ++i)
-    {
-      long lo, hi, mid;
-      asymbol *sym;
+      for (i = secsymend, r = opd->relocation; i < opdsymend; ++i)
+       {
+         asymbol *sym;
 
-      while (r < relocs + relcount
-            && (*r)->address < syms[i]->value + opd->vma)
-       ++r;
+         while (r < opd->relocation + relcount
+                && r->address < syms[i]->value + opd->vma)
+           ++r;
 
-      if (r == relocs + relcount)
-       continue;
+         if (r == opd->relocation + relcount)
+           break;
 
-      if ((*r)->address != syms[i]->value + opd->vma)
-       continue;
+         if (r->address != syms[i]->value + opd->vma)
+           continue;
 
-      if ((*r)->howto->type != (relocatable
-                               ? R_PPC64_ADDR64 : R_PPC64_RELATIVE))
-       continue;
+         if (r->howto->type != R_PPC64_ADDR64)
+           continue;
 
-      lo = opdsymcount;
-      hi = symcount;
-      sym = *((*r)->sym_ptr_ptr);
-      if (relocatable)
-       while (lo < hi)
-         {
-           mid = (lo + hi) >> 1;
-           if (syms[mid]->section->id < sym->section->id)
-             lo = mid + 1;
-           else if (syms[mid]->section->id > sym->section->id)
-             hi = mid;
-           else if (syms[mid]->value < sym->value + (*r)->addend)
-             lo = mid + 1;
-           else if (syms[mid]->value > sym->value + (*r)->addend)
-             hi = mid;
-           else
-             break;
-         }
-      else
-       while (lo < hi)
-         {
-           mid = (lo + hi) >> 1;
-           if (syms[mid]->value + syms[mid]->section->vma < (*r)->addend)
-             lo = mid + 1;
-           else if (syms[mid]->value + syms[mid]->section->vma > (*r)->addend)
-             hi = mid;
-           else
-             break;
-         }
+         sym = *r->sym_ptr_ptr;
+         if (!sym_exists_at (syms, opdsymend, symcount,
+                             sym->section->id, sym->value + r->addend))
+           {
+             size_t len;
 
-      if (lo >= hi)
-       {
-         ++count;
-         size += sizeof (asymbol);
-         size += strlen (syms[i]->name) + 1;
+             *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++;
+           }
        }
     }
-
-  s = *ret = bfd_malloc (size);
-  if (s == NULL)
-    {
-      free (syms);
-      free (relocs);
-      return 0;
-    }
-
-  names = (char *) (s + count);
-
-  for (i = 0, r = relocs; i < opdsymcount; ++i)
+  else
     {
-      long lo, hi, mid;
-      asymbol *sym;
+      bfd_byte *contents;
+      size_t size;
 
-      while (r < relocs + relcount
-            && (*r)->address < syms[i]->value + opd->vma)
-       ++r;
+      if (!bfd_malloc_and_get_section (abfd, opd, &contents))
+       {
+         if (contents)
+           {
+           free_contents_and_exit:
+             free (contents);
+           }
+         count = -1;
+         goto done;
+       }
 
-      if (r == relocs + relcount)
-       continue;
+      size = 0;
+      for (i = secsymend; i < opdsymend; ++i)
+       {
+         bfd_vma ent;
 
-      if ((*r)->address != syms[i]->value + opd->vma)
-       continue;
+         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;
+           }
+       }
 
-      if ((*r)->howto->type != (relocatable
-                               ? R_PPC64_ADDR64 : R_PPC64_RELATIVE))
-       continue;
+      s = *ret = bfd_malloc (size);
+      if (s == NULL)
+       goto free_contents_and_exit;
 
-      lo = opdsymcount;
-      hi = symcount;
-      sym = *((*r)->sym_ptr_ptr);
-      if (relocatable)
-       while (lo < hi)
-         {
-           mid = (lo + hi) >> 1;
-           if (syms[mid]->section->id < sym->section->id)
-             lo = mid + 1;
-           else if (syms[mid]->section->id > sym->section->id)
-             hi = mid;
-           else if (syms[mid]->value < sym->value + (*r)->addend)
-             lo = mid + 1;
-           else if (syms[mid]->value > sym->value + (*r)->addend)
-             hi = mid;
-           else
-             break;
-         }
-      else
-       while (lo < hi)
-         {
-           mid = (lo + hi) >> 1;
-           if (syms[mid]->value + syms[mid]->section->vma < (*r)->addend)
-             lo = mid + 1;
-           else if (syms[mid]->value + syms[mid]->section->vma > (*r)->addend)
-             hi = mid;
-           else
-             break;
-         }
+      names = (char *) (s + count);
 
-      if (lo >= hi)
+      for (i = secsymend; i < opdsymend; ++i)
        {
-         size_t len;
+         bfd_vma ent;
 
-         *s = *syms[i];
-         
-         if (! relocatable)
+         ent = bfd_get_64 (abfd, contents + syms[i]->value);
+         if (!sym_exists_at (syms, opdsymend, symcount, -1, ent))
            {
-             asection *sec;
+             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;
+                   }
+               }
 
-             s->section = &bfd_abs_section;
-             for (sec = abfd->sections; sec; sec = sec->next)
-               if ((sec->flags & (SEC_ALLOC | SEC_CODE))
-                   == (SEC_ALLOC | SEC_CODE)
-                   && (*r)->addend >= sec->vma
-                   && (*r)->addend < sec->vma + sec->size)
-                 {
-                   s->section = sec;
+             if (lo >= hi && lo > codesecsym)
+               sec = syms[lo - 1]->section;
+
+             for (; sec != NULL; sec = sec->next)
+               {
+                 if (sec->vma > ent)
                    break;
-                 }
-             s->value = (*r)->addend - sec->vma;
-           }
-         else
-           {
-             s->section = sym->section;
-             s->value = sym->value + (*r)->addend;
+                 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++;
            }
-         s->name = names;
-         len = strlen (syms[i]->name);
-         memcpy (names, syms[i]->name, len + 1);
-         names += len + 1;
-         s++;
        }
+      free (contents);
     }
 
+ done:
   free (syms);
-  free (relocs);
   return count;
 }
-
 \f
 /* The following functions are specific to the ELF linker, while
    functions above are used generally.  Those named ppc64_elf_* are
@@ -3205,10 +3210,11 @@ struct ppc_link_hash_entry
   /* Flag function code and descriptor symbols.  */
   unsigned int is_func:1;
   unsigned int is_func_descriptor:1;
+  unsigned int fake: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.  */
@@ -3292,9 +3298,16 @@ 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;
 
+  /* Support for multiple toc sections.  */
+  unsigned int no_multi_toc:1;
+  unsigned int multi_toc_needed:1;
+
   /* Set on error.  */
   unsigned int stub_error:1;
 
@@ -3312,6 +3325,12 @@ struct ppc_link_hash_table
   struct sym_sec_cache sym_sec;
 };
 
+/* Rename some of the generic section flags to better document how they
+   are used here.  */
+#define has_toc_reloc has_gp_reloc
+#define makes_toc_func_call need_finalize_relax
+#define call_check_in_progress reloc_done
+
 /* Get the ppc64 ELF linker hash table from a link_info structure.  */
 
 #define ppc_hash_table(p) \
@@ -3414,14 +3433,9 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
     {
       struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) entry;
 
-      eh->stub_cache = NULL;
-      eh->dyn_relocs = NULL;
-      eh->oh = NULL;
-      eh->is_func = 0;
-      eh->is_func_descriptor = 0;
-      eh->adjust_done = 0;
-      eh->was_undefined = 0;
-      eh->tls_mask = 0;
+      memset (&eh->stub_cache, 0,
+             (sizeof (struct ppc_link_hash_entry)
+              - offsetof (struct ppc_link_hash_entry, stub_cache)));
     }
 
   return entry;
@@ -3535,6 +3549,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;
 }
 
@@ -3673,7 +3689,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);
@@ -3752,6 +3768,41 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Merge PLT info on FROM with that on TO.  */
+
+static void
+move_plt_plist (struct ppc_link_hash_entry *from,
+               struct ppc_link_hash_entry *to)
+{
+  if (from->elf.plt.plist != NULL)
+    {
+      if (to->elf.plt.plist != NULL)
+       {
+         struct plt_entry **entp;
+         struct plt_entry *ent;
+
+         for (entp = &from->elf.plt.plist; (ent = *entp) != NULL; )
+           {
+             struct plt_entry *dent;
+
+             for (dent = to->elf.plt.plist; dent != NULL; dent = dent->next)
+               if (dent->addend == ent->addend)
+                 {
+                   dent->plt.refcount += ent->plt.refcount;
+                   *entp = ent->next;
+                   break;
+                 }
+             if (dent == NULL)
+               entp = &ent->next;
+           }
+         *entp = to->elf.plt.plist;
+       }
+
+      to->elf.plt.plist = from->elf.plt.plist;
+      from->elf.plt.plist = NULL;
+    }
+}
+
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
@@ -3761,7 +3812,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;
@@ -3805,18 +3855,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)
@@ -3855,33 +3905,7 @@ ppc64_elf_copy_indirect_symbol
     }
 
   /* And plt entries.  */
-  if (eind->elf.plt.plist != NULL)
-    {
-      if (edir->elf.plt.plist != NULL)
-       {
-         struct plt_entry **entp;
-         struct plt_entry *ent;
-
-         for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; )
-           {
-             struct plt_entry *dent;
-
-             for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next)
-               if (dent->addend == ent->addend)
-                 {
-                   dent->plt.refcount += ent->plt.refcount;
-                   *entp = ent->next;
-                   break;
-                 }
-             if (dent == NULL)
-               entp = &ent->next;
-           }
-         *entp = edir->elf.plt.plist;
-       }
-
-      edir->elf.plt.plist = eind->elf.plt.plist;
-      eind->elf.plt.plist = NULL;
-    }
+  move_plt_plist (eind, edir);
 
   if (edir->elf.dynindx == -1)
     {
@@ -3920,6 +3944,41 @@ get_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
   return fdh;
 }
 
+/* Make a fake function descriptor sym for the code sym FH.  */
+
+static struct ppc_link_hash_entry *
+make_fdh (struct bfd_link_info *info,
+         struct ppc_link_hash_entry *fh)
+{
+  bfd *abfd;
+  asymbol *newsym;
+  struct bfd_link_hash_entry *bh;
+  struct ppc_link_hash_entry *fdh;
+
+  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;
+  newsym->value = 0;
+  newsym->flags = BSF_WEAK;
+
+  bh = NULL;
+  if (!_bfd_generic_link_add_one_symbol (info, abfd, newsym->name,
+                                        newsym->flags, newsym->section,
+                                        newsym->value, NULL, FALSE, FALSE,
+                                        &bh))
+    return NULL;
+
+  fdh = (struct ppc_link_hash_entry *) bh;
+  fdh->elf.non_elf = 0;
+  fdh->fake = 1;
+  fdh->is_func_descriptor = 1;
+  fdh->oh = fh;
+  fh->is_func = 1;
+  fh->oh = fdh;
+  return fdh;
+}
+
 /* Hacks to support old ABI code.
    When making function calls, old ABI code references function entry
    points (dot symbols), while new ABI code references the function
@@ -3954,7 +4013,9 @@ ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED,
 }
 
 /* This function makes an old ABI object reference to ".bar" cause the
-   inclusion of a new ABI object archive that defines "bar".  */
+   inclusion of a new ABI object archive that defines "bar".
+   NAME is a symbol defined in an archive.  Return a symbol in the hash
+   table that might be satisfied by the archive symbols.  */
 
 static struct elf_link_hash_entry *
 ppc64_elf_archive_symbol_lookup (bfd *abfd,
@@ -3966,7 +4027,11 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
   size_t len;
 
   h = _bfd_elf_archive_symbol_lookup (abfd, info, name);
-  if (h != NULL)
+  if (h != NULL
+      /* Don't return this sym if it is a fake function descriptor
+        created by add_symbol_adjust.  */
+      && !(h->root.type == bfd_link_hash_undefweak
+          && ((struct ppc_link_hash_entry *) h)->fake))
     return h;
 
   if (name[0] == '.')
@@ -3992,10 +4057,16 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
    most restrictive visibility of the function descriptor and the
    function entry symbol is used.  */
 
+struct add_symbol_adjust_data
+{
+  struct bfd_link_info *info;
+  bfd_boolean ok;
+};
+
 static bfd_boolean
 add_symbol_adjust (struct elf_link_hash_entry *h, void *inf)
 {
-  struct bfd_link_info *info;
+  struct add_symbol_adjust_data *data;
   struct ppc_link_hash_table *htab;
   struct ppc_link_hash_entry *eh;
   struct ppc_link_hash_entry *fdh;
@@ -4009,11 +4080,28 @@ add_symbol_adjust (struct elf_link_hash_entry *h, void *inf)
   if (h->root.root.string[0] != '.')
     return TRUE;
 
-  info = inf;
-  htab = ppc_hash_table (info);
+  data = inf;
+  htab = ppc_hash_table (data->info);
   eh = (struct ppc_link_hash_entry *) h;
   fdh = get_fdh (eh, htab);
-  if (fdh != NULL)
+  if (fdh == NULL
+      && !data->info->relocatable
+      && (eh->elf.root.type == bfd_link_hash_undefined
+         || eh->elf.root.type == bfd_link_hash_undefweak)
+      && eh->elf.ref_regular)
+    {
+      /* Make an undefweak function descriptor sym, which is enough to
+        pull in an --as-needed shared lib, but won't cause link
+        errors.  Archives are handled elsewhere.  */
+      fdh = make_fdh (data->info, eh);
+      if (fdh == NULL)
+       data->ok = FALSE;
+      else
+       fdh->elf.ref_regular = 1;
+    }
+  else if (fdh != NULL
+          && (fdh->elf.root.type == bfd_link_hash_defined
+              || fdh->elf.root.type == bfd_link_hash_defweak))
     {
       unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1;
       unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1;
@@ -4038,51 +4126,24 @@ ppc64_elf_check_directives (bfd *abfd ATTRIBUTE_UNUSED,
                            struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
-  extern const bfd_target bfd_elf64_powerpc_vec;
-  extern const bfd_target bfd_elf64_powerpcle_vec;
+  struct add_symbol_adjust_data data;
 
   htab = ppc_hash_table (info);
-  if (htab->elf.root.creator != &bfd_elf64_powerpc_vec
-      && htab->elf.root.creator != &bfd_elf64_powerpcle_vec)
+  if (!is_ppc64_elf_target (htab->elf.root.creator))
     return TRUE;
 
-  elf_link_hash_traverse (&htab->elf, add_symbol_adjust, info);
+  data.info = info;
+  data.ok = TRUE;
+  elf_link_hash_traverse (&htab->elf, add_symbol_adjust, &data);
 
   /* We need to fix the undefs list for any syms we have twiddled to
      undef_weak.  */
   if (htab->twiddled_syms)
     {
-      struct bfd_link_hash_entry **pun;
-
-      pun = &htab->elf.root.undefs;
-      while (*pun != NULL)
-       {
-         struct bfd_link_hash_entry *h = *pun;
-
-         if (h->type != bfd_link_hash_undefined
-             && h->type != bfd_link_hash_common)
-           {
-             *pun = h->und_next;
-             h->und_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
-                      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));
-                 break;
-               }
-           }
-         else
-           pun = &h->und_next;
-       }
-
+      bfd_link_repair_undef_list (&htab->elf.root);
       htab->twiddled_syms = 0;
     }
-  return TRUE;
+  return data.ok;
 }
 
 static bfd_boolean
@@ -4153,8 +4214,10 @@ 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->is_func = 1;
+  eh->elf.needs_plt = 1;
+  if (eh->elf.root.root.string[0] == '.'
+      && eh->elf.root.root.string[1] != '\0')
+    eh->is_func = 1;
   return TRUE;
 }
 
@@ -4281,7 +4344,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT16_LO:
        case R_PPC64_GOT16_LO_DS:
          /* This symbol requires a global offset table entry.  */
-         sec->has_gp_reloc = 1;
+         sec->has_toc_reloc = 1;
          if (ppc64_elf_tdata (abfd)->got == NULL
              && !create_got_section (abfd, info))
            return FALSE;
@@ -4371,7 +4434,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_TOC16_HA:
        case R_PPC64_TOC16_DS:
        case R_PPC64_TOC16_LO_DS:
-         sec->has_gp_reloc = 1;
+         sec->has_toc_reloc = 1;
          break;
 
          /* This relocation describes the C++ object vtable hierarchy.
@@ -4549,7 +4612,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)
@@ -4582,14 +4645,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;
@@ -4627,9 +4688,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
                      sreloc = bfd_make_section (dynobj, name);
                      flags = (SEC_HAS_CONTENTS | SEC_READONLY
-                              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-                     if ((sec->flags & SEC_ALLOC) != 0)
-                       flags |= SEC_ALLOC | SEC_LOAD;
+                              | SEC_IN_MEMORY | SEC_LINKER_CREATED
+                              | SEC_ALLOC | SEC_LOAD);
                      if (sreloc == NULL
                          || ! bfd_set_section_flags (dynobj, sreloc, flags)
                          || ! bfd_set_section_alignment (dynobj, sreloc, 3))
@@ -4697,13 +4757,45 @@ opd_entry_value (asection *opd_sec,
                 bfd_vma *code_off)
 {
   bfd *opd_bfd = opd_sec->owner;
+  Elf_Internal_Rela *relocs;
   Elf_Internal_Rela *lo, *hi, *look;
+  bfd_vma val;
+
+  /* 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;
+    }
+
+  relocs = ppc64_elf_tdata (opd_bfd)->opd_relocs;
+  if (relocs == NULL)
+    relocs = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
 
   /* Go find the opd reloc at the sym address.  */
-  lo = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
+  lo = relocs;
   BFD_ASSERT (lo != NULL);
   hi = lo + opd_sec->reloc_count - 1; /* ignore last reloc */
-
+  val = (bfd_vma) -1;
   while (lo < hi)
     {
       look = lo + (hi - lo) / 2;
@@ -4718,7 +4810,6 @@ opd_entry_value (asection *opd_sec,
              && ELF64_R_TYPE ((look + 1)->r_info) == R_PPC64_TOC)
            {
              unsigned long symndx = ELF64_R_SYM (look->r_info);
-             bfd_vma val;
              asection *sec;
 
              if (symndx < symtab_hdr->sh_info)
@@ -4732,7 +4823,7 @@ opd_entry_value (asection *opd_sec,
                                                  symtab_hdr->sh_info,
                                                  0, NULL, NULL, NULL);
                      if (sym == NULL)
-                       return (bfd_vma) -1;
+                       break;
                      symtab_hdr->contents = (bfd_byte *) sym;
                    }
 
@@ -4767,12 +4858,12 @@ opd_entry_value (asection *opd_sec,
                *code_sec = sec;
              if (sec != NULL && sec->output_section != NULL)
                val += sec->output_section->vma + sec->output_offset;
-             return val;
            }
          break;
        }
     }
-  return (bfd_vma) -1;
+
+  return val;
 }
 
 /* Return the section that should be marked against GC for a given
@@ -4806,7 +4897,9 @@ ppc64_elf_gc_mark_hook (asection *sec,
              && eh->elf.root.type != bfd_link_hash_defweak)
            continue;
 
-         if (eh->is_func_descriptor)
+         if (eh->is_func_descriptor
+             && (eh->oh->elf.root.type == bfd_link_hash_defined
+                 || eh->oh->elf.root.type == bfd_link_hash_defweak))
            rsec = eh->oh->elf.root.u.def.section;
          else if (get_opd_info (eh->elf.root.u.def.section) != NULL
                   && opd_entry_value (eh->elf.root.u.def.section,
@@ -4852,12 +4945,17 @@ ppc64_elf_gc_mark_hook (asection *sec,
            case bfd_link_hash_defined:
            case bfd_link_hash_defweak:
              eh = (struct ppc_link_hash_entry *) h;
-             if (eh->oh != NULL && eh->oh->is_func_descriptor)
+             if (eh->oh != NULL
+                 && eh->oh->is_func_descriptor
+                 && (eh->oh->elf.root.type == bfd_link_hash_defined
+                     || eh->oh->elf.root.type == bfd_link_hash_defweak))
                eh = eh->oh;
 
              /* Function descriptor syms cause the associated
                 function code sym section to be marked.  */
-             if (eh->is_func_descriptor)
+             if (eh->is_func_descriptor
+                 && (eh->oh->elf.root.type == bfd_link_hash_defined
+                     || eh->oh->elf.root.type == bfd_link_hash_defweak))
                {
                  /* They also mark their opd section.  */
                  if (!eh->elf.root.u.def.section->gc_mark)
@@ -4946,6 +5044,9 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
          struct ppc_dyn_relocs *p;
 
          h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
          eh = (struct ppc_link_hash_entry *) h;
 
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
@@ -5050,8 +5151,8 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
 
 struct sfpr_def_parms
 {
-  const char *name;
-  unsigned int lo, hi;
+  const char name[12];
+  unsigned char lo, hi;
   bfd_byte * (*write_ent) (bfd *, bfd_byte *, int);
   bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
 };
@@ -5065,7 +5166,7 @@ sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
   unsigned int i;
   size_t len = strlen (parm->name);
   bfd_boolean writing = FALSE;
-  char sym[20];
+  char sym[16];
 
   memcpy (sym, parm->name, len);
   sym[len + 2] = 0;
@@ -5078,13 +5179,13 @@ sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
       sym[len + 1] = 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)
+         && !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->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+         h->def_regular = 1;
          _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
          writing = TRUE;
          if (htab->sfpr->contents == NULL)
@@ -5309,7 +5410,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
                          &fh->elf.root.u.def.value) != (bfd_vma) -1)
     {
       fh->elf.root.type = fh->oh->elf.root.type;
-      fh->elf.elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
+      fh->elf.forced_local = 1;
     }
 
   /* If this is a function code symbol, transfer dynamic linking
@@ -5339,56 +5440,42 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
       && (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;
-      newsym->value = 0;
-      newsym->flags = BSF_OBJECT;
-      if (fh->elf.root.type == bfd_link_hash_undefweak)
-       newsym->flags |= BSF_WEAK;
-
-      bh = &fdh->elf.root;
-      if ( !(_bfd_generic_link_add_one_symbol
-            (info, abfd, newsym->name, newsym->flags,
-             newsym->section, newsym->value, NULL, FALSE, FALSE, &bh)))
-       {
-         return FALSE;
-       }
-      fdh = (struct ppc_link_hash_entry *) bh;
-      fdh->elf.elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
-      fdh->elf.size = 24;
-      fdh->elf.type = STT_OBJECT;
+      fdh = make_fdh (info, fh);
+      if (fdh == NULL)
+       return FALSE;
+    }
+
+  /* Fake function descriptors are made undefweak.  If the function
+     code symbol is strong undefined, make the fake sym the same.  */
+
+  if (fdh != NULL
+      && fdh->fake
+      && fdh->elf.root.type == bfd_link_hash_undefweak
+      && fh->elf.root.type == bfd_link_hash_undefined)
+    {
+      fdh->elf.root.type = bfd_link_hash_undefined;
+      bfd_link_add_undef (&htab->elf.root, &fdh->elf.root);
     }
 
   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;
-         while (*ep != NULL)
-           ep = &(*ep)->next;
-         *ep = fh->elf.plt.plist;
-         fh->elf.plt.plist = NULL;
-         fdh->elf.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+         move_plt_plist (fh, fdh);
+         fdh->elf.needs_plt = 1;
        }
       fdh->is_func_descriptor = 1;
       fdh->oh = fh;
@@ -5402,12 +5489,10 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
      been imported from another library.  Function code syms that
      are really in the library we must leave global to prevent the
      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
-          || 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));
+  force_local = (!fh->elf.def_regular
+                || fdh == NULL
+                || !fdh->elf.def_regular
+                || fdh->elf.forced_local);
   _bfd_elf_link_hash_hide_symbol (info, &fh->elf, force_local);
 
   return TRUE;
@@ -5477,7 +5562,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.  */
@@ -5491,7 +5576,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
@@ -5500,16 +5585,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;
     }
 
@@ -5522,7 +5605,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)
@@ -5542,7 +5625,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;
        }
     }
@@ -5580,7 +5663,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
@@ -5810,7 +5893,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;
@@ -5868,6 +5951,125 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
   return TRUE;
 }
 
+/* Handles decrementing dynamic reloc counts for the reloc specified by
+   R_INFO in section SEC.  If LOCAL_SYMS is NULL, then H and SYM_SEC
+   have already been determined.  */
+
+static bfd_boolean
+dec_dynrel_count (bfd_vma r_info,
+                 asection *sec,
+                 struct bfd_link_info *info,
+                 Elf_Internal_Sym **local_syms,
+                 struct elf_link_hash_entry *h,
+                 asection *sym_sec)
+{
+  enum elf_ppc64_reloc_type r_type;
+  struct ppc_dyn_relocs *p;
+  struct ppc_dyn_relocs **pp;
+
+  /* Can this reloc be dynamic?  This switch, and later tests here
+     should be kept in sync with the code in check_relocs.  */
+  r_type = ELF64_R_TYPE (r_info);
+  switch (r_type)
+    {
+    default:
+      return TRUE;
+
+    case R_PPC64_TPREL16:
+    case R_PPC64_TPREL16_LO:
+    case R_PPC64_TPREL16_HI:
+    case R_PPC64_TPREL16_HA:
+    case R_PPC64_TPREL16_DS:
+    case R_PPC64_TPREL16_LO_DS:
+    case R_PPC64_TPREL16_HIGHER:
+    case R_PPC64_TPREL16_HIGHERA:
+    case R_PPC64_TPREL16_HIGHEST:
+    case R_PPC64_TPREL16_HIGHESTA:
+      if (!info->shared)
+       return TRUE;
+
+    case R_PPC64_TPREL64:
+    case R_PPC64_DTPMOD64:
+    case R_PPC64_DTPREL64:
+    case R_PPC64_ADDR64:
+    case R_PPC64_REL30:
+    case R_PPC64_REL32:
+    case R_PPC64_REL64:
+    case R_PPC64_ADDR14:
+    case R_PPC64_ADDR14_BRNTAKEN:
+    case R_PPC64_ADDR14_BRTAKEN:
+    case R_PPC64_ADDR16:
+    case R_PPC64_ADDR16_DS:
+    case R_PPC64_ADDR16_HA:
+    case R_PPC64_ADDR16_HI:
+    case R_PPC64_ADDR16_HIGHER:
+    case R_PPC64_ADDR16_HIGHERA:
+    case R_PPC64_ADDR16_HIGHEST:
+    case R_PPC64_ADDR16_HIGHESTA:
+    case R_PPC64_ADDR16_LO:
+    case R_PPC64_ADDR16_LO_DS:
+    case R_PPC64_ADDR24:
+    case R_PPC64_ADDR32:
+    case R_PPC64_UADDR16:
+    case R_PPC64_UADDR32:
+    case R_PPC64_UADDR64:
+    case R_PPC64_TOC:
+      break;
+    }
+
+  if (local_syms != NULL)
+    {
+      unsigned long r_symndx;
+      Elf_Internal_Sym *sym;
+      bfd *ibfd = sec->owner;
+
+      r_symndx = ELF64_R_SYM (r_info);
+      if (!get_sym_h (&h, &sym, &sym_sec, NULL, local_syms, r_symndx, ibfd))
+       return FALSE;
+    }
+
+  if ((info->shared
+       && (MUST_BE_DYN_RELOC (r_type)
+          || (h != NULL
+              && (!info->symbolic
+                  || h->root.type == bfd_link_hash_defweak
+                  || !h->def_regular))))
+      || (ELIMINATE_COPY_RELOCS
+         && !info->shared
+         && h != NULL
+         && (h->root.type == bfd_link_hash_defweak
+             || !h->def_regular)))
+    ;
+  else
+    return TRUE;
+
+  if (h != NULL)
+    pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+  else if (sym_sec != NULL)
+    pp = (struct ppc_dyn_relocs **) &elf_section_data (sym_sec)->local_dynrel;
+  else
+    pp = (struct ppc_dyn_relocs **) &elf_section_data (sec)->local_dynrel;
+
+  while ((p = *pp) != NULL)
+    {
+      if (p->sec == sec)
+       {
+         if (!MUST_BE_DYN_RELOC (r_type))
+           p->pc_count -= 1;
+         p->count -= 1;
+         if (p->count == 0)
+           *pp = p->next;
+         return TRUE;
+       }
+      pp = &p->next;
+    }
+
+  (*_bfd_error_handler) (_("dynreloc miscount for %B, section %A"),
+                          sec->owner, sec);
+  bfd_set_error (bfd_error_bad_value);
+  return FALSE;
+}
+
 /* Remove unused Official Procedure Descriptor entries.  Currently we
    only remove those associated with functions in discarded link-once
    sections, or weakly defined functions that have been overridden.  It
@@ -5903,13 +6105,16 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
       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;
 
@@ -5981,7 +6186,8 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
              if (h != NULL)
                sym_name = h->root.root.string;
              else
-               sym_name = bfd_elf_local_sym_name (ibfd, sym);
+               sym_name = bfd_elf_sym_name (ibfd, symtab_hdr, sym,
+                                            sym_sec);
 
              (*_bfd_error_handler)
                (_("%B: undefined sym `%s' in .opd section"),
@@ -6122,8 +6328,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
 
                  if (h != NULL
                      && h->root.root.string[0] == '.')
-                   fdh = get_fdh ((struct ppc_link_hash_entry *) h,
-                                  ppc_hash_table (info));
+                   {
+                     fdh = get_fdh ((struct ppc_link_hash_entry *) h,
+                                    ppc_hash_table (info));
+                     if (fdh != NULL
+                         && fdh->elf.root.type != bfd_link_hash_defined
+                         && fdh->elf.root.type != bfd_link_hash_defweak)
+                       fdh = NULL;
+                   }
 
                  skip = (sym_sec->owner != ibfd
                          || sym_sec->output_section == bfd_abs_section_ptr);
@@ -6179,33 +6391,9 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
 
              if (skip)
                {
-                 BFD_ASSERT (MUST_BE_DYN_RELOC (ELF64_R_TYPE (rel->r_info)));
-                 if (info->shared)
-                   {
-                     /* We won't be needing dynamic relocs here.  */
-                     struct ppc_dyn_relocs **pp;
-                     struct ppc_dyn_relocs *p;
-
-                     if (h != NULL)
-                       pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
-                     else if (sym_sec != NULL)
-                       pp = ((struct ppc_dyn_relocs **)
-                             &elf_section_data (sym_sec)->local_dynrel);
-                     else
-                       pp = ((struct ppc_dyn_relocs **)
-                             &elf_section_data (sec)->local_dynrel);
-                     while ((p = *pp) != NULL)
-                       {
-                         if (p->sec == sec)
-                           {
-                             p->count -= 1;
-                             if (p->count == 0)
-                               *pp = p->next;
-                             break;
-                           }
-                         pp = &p->next;
-                       }
-                   }
+                 if (!dec_dynrel_count (rel->r_info, sec, info,
+                                        NULL, h, sym_sec))
+                   goto error_ret;
                }
              else
                {
@@ -6263,7 +6451,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
          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;
@@ -6307,7 +6495,9 @@ ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
 
       if (htab->tls_get_addr_fd == NULL
          && h->oh != NULL
-         && h->oh->is_func_descriptor)
+         && h->oh->is_func_descriptor
+         && (h->oh->elf.root.type == bfd_link_hash_defined
+             || h->oh->elf.root.type == bfd_link_hash_defweak))
        htab->tls_get_addr_fd = h->oh;
     }
 
@@ -6402,7 +6592,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;
@@ -6538,85 +6728,531 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                        if (!is_local)
                          continue;
 
-                       /* LD -> LE */
-                       tls_set = TLS_EXPLICIT;
-                       tls_clear = TLS_LD;
-                     }
+                       /* LD -> LE */
+                       tls_set = TLS_EXPLICIT;
+                       tls_clear = TLS_LD;
+                     }
+                   break;
+
+                 default:
+                   expecting_tls_get_addr = 0;
+                   continue;
+                 }
+
+               if ((tls_set & TLS_EXPLICIT) == 0)
+                 {
+                   struct got_entry *ent;
+
+                   /* Adjust got entry for this reloc.  */
+                   if (h != NULL)
+                     ent = h->got.glist;
+                   else
+                     ent = elf_local_got_ents (ibfd)[r_symndx];
+
+                   for (; ent != NULL; ent = ent->next)
+                     if (ent->addend == rel->r_addend
+                         && ent->owner == ibfd
+                         && ent->tls_type == tls_type)
+                       break;
+                   if (ent == NULL)
+                     abort ();
+
+                   if (tls_set == 0)
+                     {
+                       /* We managed to get rid of a got entry.  */
+                       if (ent->got.refcount > 0)
+                         ent->got.refcount -= 1;
+                     }
+                 }
+               else
+                 {
+                   /* If we got rid of a DTPMOD/DTPREL reloc pair then
+                      we'll lose one or two dyn relocs.  */
+                   if (!dec_dynrel_count (rel->r_info, sec, info,
+                                          NULL, h, sym_sec))
+                     return FALSE;
+
+                   if (tls_set == (TLS_EXPLICIT | TLS_GD))
+                     {
+                       if (!dec_dynrel_count ((rel + 1)->r_info, sec, info,
+                                              NULL, h, sym_sec))
+                         return FALSE;
+                     }
+                 }
+
+               *tls_mask |= tls_set;
+               *tls_mask &= ~tls_clear;
+             }
+
+           if (elf_section_data (sec)->relocs != relstart)
+             free (relstart);
+         }
+
+      if (locsyms != NULL
+         && (elf_tdata (ibfd)->symtab_hdr.contents
+             != (unsigned char *) locsyms))
+       {
+         if (!info->keep_memory)
+           free (locsyms);
+         else
+           elf_tdata (ibfd)->symtab_hdr.contents = (unsigned char *) locsyms;
+       }
+    }
+  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;
+      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->size == 0
+         || 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:
-                   expecting_tls_get_addr = 0;
                    continue;
                  }
 
-               if ((tls_set & TLS_EXPLICIT) == 0)
+               r_symndx = ELF64_R_SYM (rel->r_info);
+               if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                               r_symndx, ibfd))
                  {
-                   struct got_entry *ent;
+                   free (used);
+                   goto error_ret;
+                 }
 
-                   /* Adjust got entry for this reloc.  */
-                   if (h != NULL)
-                     ent = h->got.glist;
-                   else
-                     ent = elf_local_got_ents (ibfd)[r_symndx];
+               if (sym_sec != toc)
+                 continue;
 
-                   for (; ent != NULL; ent = ent->next)
-                     if (ent->addend == rel->r_addend
-                         && ent->owner == ibfd
-                         && ent->tls_type == tls_type)
-                       break;
-                   if (ent == NULL)
-                     abort ();
+               if (h != NULL)
+                 val = h->root.u.def.value;
+               else
+                 val = sym->st_value;
+               val += rel->r_addend;
 
-                   if (tls_set == 0)
-                     {
-                       /* We managed to get rid of a got entry.  */
-                       if (ent->got.refcount > 0)
-                         ent->got.refcount -= 1;
-                     }
-                 }
-               else if (h != NULL)
+               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;
+
+         if (toc->reloc_count != 0)
+           {
+             Elf_Internal_Rela *wrel;
+             bfd_size_type sz;
+
+             /* 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)
                  {
-                   struct ppc_link_hash_entry * eh;
-                   struct ppc_dyn_relocs **pp;
-                   struct ppc_dyn_relocs *p;
-
-                   /* Adjust dynamic relocs.  */
-                   eh = (struct ppc_link_hash_entry *) h;
-                   for (pp = &eh->dyn_relocs;
-                        (p = *pp) != NULL;
-                        pp = &p->next)
-                     if (p->sec == sec)
-                       {
-                         /* If we got rid of a DTPMOD/DTPREL reloc
-                            pair then we'll lose one or two dyn
-                            relocs.  */
-                         if (tls_set == (TLS_EXPLICIT | TLS_GD))
-                           p->count -= 1;
-                         p->count -= 1;
-                         if (p->count == 0)
-                           *pp = p->next;
-                         break;
-                       }
+                   wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
+                   wrel->r_info = rel->r_info;
+                   wrel->r_addend = rel->r_addend;
+                   ++wrel;
                  }
+               else if (!dec_dynrel_count (rel->r_info, toc, info,
+                                           &local_syms, NULL, NULL))
+                 goto error_ret;
+
+             toc->reloc_count = wrel - relstart;
+             sz = elf_section_data (toc)->rel_hdr.sh_entsize;
+             elf_section_data (toc)->rel_hdr.sh_size = toc->reloc_count * sz;
+             BFD_ASSERT (elf_section_data (toc)->rel_hdr2 == NULL);
+           }
 
-               *tls_mask |= tls_set;
-               *tls_mask &= ~tls_clear;
-             }
+         /* 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;
 
-           if (elf_section_data (sec)->relocs != relstart)
-             free (relstart);
-         }
+             relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                                   TRUE);
+             if (relstart == NULL)
+               goto error_ret;
 
-      if (locsyms != NULL
-         && (elf_tdata (ibfd)->symtab_hdr.contents
-             != (unsigned char *) locsyms))
+             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_sym_name (ibfd, symtab_hdr, sym,
+                                            NULL));
+                       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 (locsyms);
+           free (local_syms);
          else
-           elf_tdata (ibfd)->symtab_hdr.contents = (unsigned char *) locsyms;
+           symtab_hdr->contents = (unsigned char *) local_syms;
        }
+      free (skip);
     }
+
   return TRUE;
 }
 
@@ -6681,13 +7317,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;
@@ -6725,14 +7361,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;
@@ -6799,14 +7435,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;
@@ -6903,7 +7539,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)
@@ -7073,6 +7709,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)
        {
@@ -7254,7 +7893,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.  */
@@ -7273,9 +7912,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
@@ -7302,6 +7941,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:
@@ -7324,7 +8024,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;
@@ -7444,16 +8144,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)
@@ -7461,10 +8171,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;
        }
     }
 
@@ -7556,7 +8267,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);
            }
 
@@ -7565,6 +8276,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;
@@ -7576,7 +8292,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
    0 when no stubs will be needed, and 1 on success.  */
 
 int
-ppc64_elf_setup_section_lists (bfd *output_bfd, struct bfd_link_info *info)
+ppc64_elf_setup_section_lists (bfd *output_bfd,
+                              struct bfd_link_info *info,
+                              int no_multi_toc)
 {
   bfd *input_bfd;
   int top_id, top_index, id;
@@ -7585,6 +8303,8 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, struct bfd_link_info *info)
   bfd_size_type amt;
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
+  htab->no_multi_toc = no_multi_toc;
+
   if (htab->brlt == NULL)
     return 0;
 
@@ -7644,25 +8364,30 @@ void
 ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  bfd_vma addr = isec->output_offset + isec->output_section->vma;
-  bfd_vma off = addr - htab->toc_curr;
 
-  if (off + isec->size > 0x10000)
-    htab->toc_curr = addr;
+  if (!htab->no_multi_toc)
+    {
+      bfd_vma addr = isec->output_offset + isec->output_section->vma;
+      bfd_vma off = addr - htab->toc_curr;
+
+      if (off + isec->size > 0x10000)
+       htab->toc_curr = addr;
 
-  elf_gp (isec->owner) = (htab->toc_curr
-                         - elf_gp (isec->output_section->owner)
-                         + TOC_BASE_OFF);
+      elf_gp (isec->owner) = (htab->toc_curr
+                             - elf_gp (isec->output_section->owner)
+                             + TOC_BASE_OFF);
+    }
 }
 
 /* Called after the last call to the above function.  */
 
 void
-ppc64_elf_reinit_toc (bfd *output_bfd ATTRIBUTE_UNUSED,
-                     struct bfd_link_info *info)
+ppc64_elf_reinit_toc (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
+  htab->multi_toc_needed = htab->toc_curr != elf_gp (output_bfd);
+
   /* toc_curr tracks the TOC offset used for code sections below in
      ppc64_elf_next_input_section.  Start off at 0x8000.  */
   htab->toc_curr = TOC_BASE_OFF;
@@ -7671,15 +8396,18 @@ ppc64_elf_reinit_toc (bfd *output_bfd ATTRIBUTE_UNUSED,
 /* No toc references were found in ISEC.  If the code in ISEC makes no
    calls, then there's no need to use toc adjusting stubs when branching
    into ISEC.  Actually, indirect calls from ISEC are OK as they will
-   load r2.  */
+   load r2.  Returns -1 on error, 0 for no stub needed, 1 for stub
+   needed, and 2 if a cyclical call-graph was found but no other reason
+   for a stub was detected.  If called from the top level, a return of
+   2 means the same as a return of 0.  */
 
 static int
 toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 {
-  bfd_byte *contents;
-  bfd_size_type i;
+  Elf_Internal_Rela *relstart, *rel;
+  Elf_Internal_Sym *local_syms;
   int ret;
-  int branch_ok;
+  struct ppc_link_hash_table *htab;
 
   /* We know none of our code bearing sections will need toc stubs.  */
   if ((isec->flags & SEC_LINKER_CREATED) != 0)
@@ -7688,44 +8416,188 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
   if (isec->size == 0)
     return 0;
 
+  if (isec->output_section == NULL)
+    return 0;
+
   /* Hack for linux kernel.  .fixup contains branches, but only back to
      the function that hit an exception.  */
-  branch_ok = strcmp (isec->name, ".fixup") == 0;
+  if (strcmp (isec->name, ".fixup") == 0)
+    return 0;
+
+  if (isec->reloc_count == 0)
+    return 0;
+
+  relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL,
+                                       info->keep_memory);
+  if (relstart == NULL)
+    return -1;
 
-  contents = elf_section_data (isec)->this_hdr.contents;
-  if (contents == NULL)
+  /* Look for branches to outside of this section.  */
+  local_syms = NULL;
+  ret = 0;
+  htab = ppc_hash_table (info);
+  for (rel = relstart; rel < relstart + isec->reloc_count; ++rel)
     {
-      if (!bfd_malloc_and_get_section (isec->owner, isec, &contents))
+      enum elf_ppc64_reloc_type r_type;
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h;
+      Elf_Internal_Sym *sym;
+      asection *sym_sec;
+      long *opd_adjust;
+      bfd_vma sym_value;
+      bfd_vma dest;
+
+      r_type = ELF64_R_TYPE (rel->r_info);
+      if (r_type != R_PPC64_REL24
+         && r_type != R_PPC64_REL14
+         && r_type != R_PPC64_REL14_BRTAKEN
+         && r_type != R_PPC64_REL14_BRNTAKEN)
+       continue;
+
+      r_symndx = ELF64_R_SYM (rel->r_info);
+      if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_symndx,
+                     isec->owner))
        {
-         if (contents != NULL)
-           free (contents);
-         return -1;
+         ret = -1;
+         break;
        }
-      if (info->keep_memory)
-       elf_section_data (isec)->this_hdr.contents = contents;
-    }
 
-  /* Code scan, because we don't necessarily have relocs on calls to
-     static functions.  */
-  ret = 0;
-  for (i = 0; i < isec->size; i += 4)
-    {
-      unsigned long insn = bfd_get_32 (isec->owner, contents + i);
-      /* Is this a branch?  */
-      if ((insn & (0x3f << 26)) == (18 << 26)
-         /* If branch and link, it's a function call.  */
-         && ((insn & 1) != 0
-             /* Sibling calls use a plain branch.  I don't know a way
-                of deciding whether a branch is really a sibling call.  */
-             || !branch_ok))
+      /* Calls to dynamic lib functions go through a plt call stub
+        that uses r2.  Branches to undefined symbols might be a call
+        using old-style dot symbols that can be satisfied by a plt
+        call into a new-style dynamic library.  */
+      if (sym_sec == NULL)
+       {
+         struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
+         if (eh != NULL
+             && eh->oh != NULL
+             && eh->oh->elf.plt.plist != NULL)
+           {
+             ret = 1;
+             break;
+           }
+
+         /* Ignore other undefined symbols.  */
+         continue;
+       }
+
+      /* Assume branches to other sections not included in the link need
+        stubs too, to cover -R and absolute syms.  */
+      if (sym_sec->output_section == NULL)
+       {
+         ret = 1;
+         break;
+       }
+
+      if (h == NULL)
+       sym_value = sym->st_value;
+      else
+       {
+         if (h->root.type != bfd_link_hash_defined
+             && h->root.type != bfd_link_hash_defweak)
+           abort ();
+         sym_value = h->root.u.def.value;
+       }
+      sym_value += rel->r_addend;
+
+      /* If this branch reloc uses an opd sym, find the code section.  */
+      opd_adjust = get_opd_info (sym_sec);
+      if (opd_adjust != NULL)
+       {
+         if (h == NULL)
+           {
+             long adjust;
+
+             adjust = opd_adjust[sym->st_value / 8];
+             if (adjust == -1)
+               /* Assume deleted functions won't ever be called.  */
+               continue;
+             sym_value += adjust;
+           }
+
+         dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL);
+         if (dest == (bfd_vma) -1)
+           continue;
+       }
+      else
+       dest = (sym_value
+               + sym_sec->output_offset
+               + sym_sec->output_section->vma);
+
+      /* Ignore branch to self.  */
+      if (sym_sec == isec)
+       continue;
+
+      /* If the called function uses the toc, we need a stub.  */
+      if (sym_sec->has_toc_reloc
+         || sym_sec->makes_toc_func_call)
+       {
+         ret = 1;
+         break;
+       }
+
+      /* Assume any branch that needs a long branch stub might in fact
+        need a plt_branch stub.  A plt_branch stub uses r2.  */
+      else if (dest - (isec->output_offset
+                      + isec->output_section->vma
+                      + rel->r_offset) + (1 << 25) >= (2 << 25))
        {
          ret = 1;
          break;
        }
+
+      /* If calling back to a section in the process of being tested, we
+        can't say for sure that no toc adjusting stubs are needed, so
+        don't return zero.  */
+      else if (sym_sec->call_check_in_progress)
+       ret = 2;
+
+      /* Branches to another section that itself doesn't have any TOC
+        references are OK.  Recursively call ourselves to check.  */
+      else if (sym_sec->id <= htab->top_id
+              && htab->stub_group[sym_sec->id].toc_off == 0)
+       {
+         int recur;
+
+         /* Mark current section as indeterminate, so that other
+            sections that call back to current won't be marked as
+            known.  */
+         isec->call_check_in_progress = 1;
+         recur = toc_adjusting_stub_needed (info, sym_sec);
+         isec->call_check_in_progress = 0;
+
+         if (recur < 0)
+           {
+             /* An error.  Exit.  */
+             ret = -1;
+             break;
+           }
+         else if (recur <= 1)
+           {
+             /* Known result.  Mark as checked and set section flag.  */
+             htab->stub_group[sym_sec->id].toc_off = 1;
+             if (recur != 0)
+               {
+                 sym_sec->makes_toc_func_call = 1;
+                 ret = 1;
+                 break;
+               }
+           }
+         else
+           {
+             /* Unknown result.  Continue checking.  */
+             ret = 2;
+           }
+       }
     }
 
-  if (elf_section_data (isec)->this_hdr.contents != contents)
-    free (contents);
+  if (local_syms != NULL
+      && (elf_tdata (isec->owner)->symtab_hdr.contents
+         != (unsigned char *) local_syms))
+    free (local_syms);
+  if (elf_section_data (isec)->relocs != relstart)
+    free (relstart);
+
   return ret;
 }
 
@@ -7738,7 +8610,6 @@ bfd_boolean
 ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  int ret;
 
   if ((isec->output_section->flags & SEC_CODE) != 0
       && isec->output_section->index <= htab->top_index)
@@ -7752,19 +8623,26 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec)
       *list = isec;
     }
 
-  /* If a code section has a function that uses the TOC then we need
-     to use the right TOC (obviously).  Also, make sure that .opd gets
-     the correct TOC value for R_PPC64_TOC relocs that don't have or
-     can't find their function symbol (shouldn't ever happen now).  */
-  if (isec->has_gp_reloc || (isec->flags & SEC_CODE) == 0)
+  if (htab->multi_toc_needed)
     {
-      if (elf_gp (isec->owner) != 0)
-       htab->toc_curr = elf_gp (isec->owner);
+      /* If a code section has a function that uses the TOC then we need
+        to use the right TOC (obviously).  Also, make sure that .opd gets
+        the correct TOC value for R_PPC64_TOC relocs that don't have or
+        can't find their function symbol (shouldn't ever happen now).  */
+      if (isec->has_toc_reloc || (isec->flags & SEC_CODE) == 0)
+       {
+         if (elf_gp (isec->owner) != 0)
+           htab->toc_curr = elf_gp (isec->owner);
+       }
+      else if (htab->stub_group[isec->id].toc_off == 0)
+       {
+         int ret = toc_adjusting_stub_needed (info, isec);
+         if (ret < 0)
+           return FALSE;
+         else
+           isec->makes_toc_func_call = ret & 1;
+       }
     }
-  else if ((ret = toc_adjusting_stub_needed (info, isec)) < 0)
-    return FALSE;
-  else
-    isec->has_gp_reloc = ret;
 
   /* Functions that don't use the TOC can belong in any TOC group.
      Use the last TOC base.  This happens to make _init and _fini
@@ -8001,8 +8879,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                      sym_value = 0;
                      /* Recognise an old ABI func code entry sym, and
                         use the func descriptor sym instead.  */
-                     if (hash->elf.root.type == bfd_link_hash_undefweak
-                         && hash->elf.root.root.string[0] == '.'
+                     if (hash->elf.root.root.string[0] == '.'
                          && (fdh = get_fdh (hash, htab)) != NULL)
                        {
                          if (fdh->elf.root.type == bfd_link_hash_defined
@@ -8088,8 +8965,8 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                          && code_sec->output_section != NULL
                          && (htab->stub_group[code_sec->id].toc_off
                              != htab->stub_group[section->id].toc_off)
-                         && code_sec->has_gp_reloc
-                         && section->has_gp_reloc)
+                         && (code_sec->has_toc_reloc
+                             || code_sec->makes_toc_func_call))
                        stub_type = ppc_stub_long_branch_r2off;
                    }
 
@@ -8151,6 +9028,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;
                }
 
@@ -8178,10 +9059,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);
@@ -8315,10 +9199,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;
@@ -8386,7 +9271,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);
@@ -8423,13 +9308,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],
@@ -8528,6 +9414,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;
@@ -8584,7 +9475,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
-         sym_name = bfd_elf_local_sym_name (input_bfd, sym);
+         sym_name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, sec);
          sym_type = ELF64_ST_TYPE (sym->st_info);
          relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
          opd_adjust = get_opd_info (sec);
@@ -9228,8 +10119,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
            if (tls_type == (TLS_TLS | TLS_LD)
                && (h == NULL
-                   || (h->elf.elf_link_hash_flags
-                       & ELF_LINK_HASH_DEF_DYNAMIC) == 0))
+                   || !h->elf.def_dynamic))
              offp = &ppc64_tlsld_got (input_bfd)->offset;
            else
              {
@@ -9548,9 +10438,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  && !info->shared
                  && h != NULL
                  && h->elf.dynindx != -1
-                 && !(h->elf.elf_link_hash_flags & ELF_LINK_NON_GOT_REF)
-                 && (h->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
-                 && !(h->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+                 && !h->elf.non_got_ref
+                 && h->elf.def_dynamic
+                 && !h->elf.def_regular))
            {
              Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
@@ -9803,7 +10693,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
         not process them.  */
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && (h->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+              && h->elf.def_dynamic))
        {
          (*_bfd_error_handler)
            (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
@@ -9845,7 +10735,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;
            }
@@ -9864,6 +10755,22 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
     }
 
+  /* If we're emitting relocations, then shortly after this function
+     returns, reloc offsets and addends for this section will be
+     adjusted.  Worse, reloc symbol indices will be for the output
+     file rather than the input.  Save a copy of the relocs for
+     opd_entry_value.  */
+  if (is_opd && info->emitrelocations)
+    {
+      bfd_size_type amt;
+      amt = input_section->reloc_count * sizeof (Elf_Internal_Rela);
+      rel = bfd_alloc (input_bfd, amt);
+      BFD_ASSERT (ppc64_elf_tdata (input_bfd)->opd_relocs == NULL);
+      ppc64_elf_tdata (input_bfd)->opd_relocs = rel;
+      if (rel == NULL)
+       return FALSE;
+      memcpy (rel, relocs, amt);
+    }
   return ret;
 }
 
@@ -9941,7 +10848,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;
@@ -10112,10 +11019,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