2009-12-15 Tristan Gingold <gingold@adacore.com>
[binutils-gdb.git] / bfd / elf64-ppc.c
index f15465806a3beee01c859181c644f99a4c11e5a1..a181c282763601286ba3cbc1fc9c44116e2644ad 100644 (file)
@@ -3771,6 +3771,9 @@ struct ppc_link_hash_table
   /* Set if we should emit symbols for stubs.  */
   unsigned int emit_stub_syms:1;
 
+  /* Set if __tls_get_addr optimization should not be done.  */
+  unsigned int no_tls_get_addr_opt:1;
+
   /* Support for multiple toc sections.  */
   unsigned int no_multi_toc:1;
   unsigned int multi_toc_needed:1;
@@ -4282,6 +4285,29 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Follow indirect and warning symbol links.  */
+
+static inline struct bfd_link_hash_entry *
+follow_link (struct bfd_link_hash_entry *h)
+{
+  while (h->type == bfd_link_hash_indirect
+        || h->type == bfd_link_hash_warning)
+    h = h->u.i.link;
+  return h;
+}
+
+static inline struct elf_link_hash_entry *
+elf_follow_link (struct elf_link_hash_entry *h)
+{
+  return (struct elf_link_hash_entry *) follow_link (&h->root);
+}
+
+static inline struct ppc_link_hash_entry *
+ppc_follow_link (struct ppc_link_hash_entry *h)
+{
+  return (struct ppc_link_hash_entry *) follow_link (&h->elf.root);
+}
+
 /* Merge PLT info on FROM with that on TO.  */
 
 static void
@@ -4364,6 +4390,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
   edir->is_func |= eind->is_func;
   edir->is_func_descriptor |= eind->is_func_descriptor;
   edir->tls_mask |= eind->tls_mask;
+  if (eind->oh != NULL)
+    edir->oh = ppc_follow_link (eind->oh);
 
   /* If called to transfer flags for a weakdef during processing
      of elf_adjust_dynamic_symbol, don't copy NON_GOT_REF.
@@ -4433,7 +4461,7 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
    hash entry FH.  Link the entries via their OH fields.  */
 
 static struct ppc_link_hash_entry *
-get_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
+lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
 {
   struct ppc_link_hash_entry *fdh = fh->oh;
 
@@ -4443,16 +4471,16 @@ get_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
 
       fdh = (struct ppc_link_hash_entry *)
        elf_link_hash_lookup (&htab->elf, fd_name, FALSE, FALSE, FALSE);
-      if (fdh != NULL)
-       {
-         fdh->is_func_descriptor = 1;
-         fdh->oh = fh;
-         fh->is_func = 1;
-         fh->oh = fdh;
-       }
+      if (fdh == NULL)
+       return fdh;
+
+      fdh->is_func_descriptor = 1;
+      fdh->oh = fh;
+      fh->is_func = 1;
+      fh->oh = fdh;
     }
 
-  return fdh;
+  return ppc_follow_link (fdh);
 }
 
 /* Make a fake function descriptor sym for the code sym FH.  */
@@ -4574,23 +4602,24 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info)
     abort ();
 
   htab = ppc_hash_table (info);
-  fdh = get_fdh (eh, htab);
-  if (fdh == NULL
-      && !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 (info, eh);
-      if (fdh == NULL)
-       return FALSE;
-      else
-       fdh->elf.ref_regular = 1;
+  fdh = lookup_fdh (eh, htab);
+  if (fdh == NULL)
+    {
+      if (!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 (info, eh);
+         if (fdh == NULL)
+           return FALSE;
+         fdh->elf.ref_regular = 1;
+       }
     }
-  else if (fdh != NULL)
+  else
     {
       unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1;
       unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1;
@@ -4839,9 +4868,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       else
        {
          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;
+         h = elf_follow_link (h);
        }
 
       tls_type = 0;
@@ -5199,7 +5226,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                {
                  if (h->root.root.string[0] == '.'
                      && h->root.root.string[1] != 0
-                     && get_fdh ((struct ppc_link_hash_entry *) h, htab))
+                     && lookup_fdh ((struct ppc_link_hash_entry *) h, htab))
                    ;
                  else
                    ((struct ppc_link_hash_entry *) h)->is_func = 1;
@@ -5374,8 +5401,6 @@ opd_entry_value (asection *opd_sec,
   /* 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;
 
@@ -5452,9 +5477,7 @@ opd_entry_value (asection *opd_sec,
 
                  sym_hashes = elf_sym_hashes (opd_bfd);
                  rh = sym_hashes[symndx - symtab_hdr->sh_info];
-                 while (rh->root.type == bfd_link_hash_indirect
-                        || rh->root.type == bfd_link_hash_warning)
-                   rh = ((struct elf_link_hash_entry *) rh->root.u.i.link);
+                 rh = elf_follow_link (rh);
                  BFD_ASSERT (rh->root.type == bfd_link_hash_defined
                              || rh->root.type == bfd_link_hash_defweak);
                  val = rh->root.u.def.value;
@@ -5475,6 +5498,39 @@ opd_entry_value (asection *opd_sec,
   return val;
 }
 
+/* If FDH is a function descriptor symbol, return the associated code
+   entry symbol if it is defined.  Return NULL otherwise.  */
+
+static struct ppc_link_hash_entry *
+defined_code_entry (struct ppc_link_hash_entry *fdh)
+{
+  if (fdh->is_func_descriptor)
+    {
+      struct ppc_link_hash_entry *fh = ppc_follow_link (fdh->oh);
+      if (fh->elf.root.type == bfd_link_hash_defined
+         || fh->elf.root.type == bfd_link_hash_defweak)
+       return fh;
+    }
+  return NULL;
+}
+
+/* If FH is a function code entry symbol, return the associated
+   function descriptor symbol if it is defined.  Return NULL otherwise.  */
+
+static struct ppc_link_hash_entry *
+defined_func_desc (struct ppc_link_hash_entry *fh)
+{
+  if (fh->oh != NULL
+      && fh->oh->is_func_descriptor)
+    {
+      struct ppc_link_hash_entry *fdh = ppc_follow_link (fh->oh);
+      if (fdh->elf.root.type == bfd_link_hash_defined
+         || fdh->elf.root.type == bfd_link_hash_defweak)
+       return fdh;
+    }
+  return NULL;
+}
+
 /* Mark all our entry sym sections, both opd and code section.  */
 
 static void
@@ -5485,22 +5541,21 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
 
   for (sym = info->gc_sym_list; sym != NULL; sym = sym->next)
     {
-      struct ppc_link_hash_entry *eh;
+      struct ppc_link_hash_entry *eh, *fh;
       asection *sec;
 
       eh = (struct ppc_link_hash_entry *)
-       elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, FALSE);
+       elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, TRUE);
       if (eh == NULL)
        continue;
       if (eh->elf.root.type != bfd_link_hash_defined
          && eh->elf.root.type != bfd_link_hash_defweak)
        continue;
 
-      if (eh->is_func_descriptor
-         && (eh->oh->elf.root.type == bfd_link_hash_defined
-             || eh->oh->elf.root.type == bfd_link_hash_defweak))
+      fh = defined_code_entry (eh);
+      if (fh != NULL)
        {
-         sec = eh->oh->elf.root.u.def.section;
+         sec = fh->elf.root.u.def.section;
          sec->flags |= SEC_KEEP;
        }
       else if (get_opd_info (eh->elf.root.u.def.section) != NULL
@@ -5523,16 +5578,15 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info = (struct bfd_link_info *) inf;
   struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
+  struct ppc_link_hash_entry *fdh;
 
   if (eh->elf.root.type == bfd_link_hash_warning)
     eh = (struct ppc_link_hash_entry *) eh->elf.root.u.i.link;
 
   /* Dynamic linking info is on the func descriptor sym.  */
-  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;
+  fdh = defined_func_desc (eh);
+  if (fdh != NULL)
+    eh = fdh;
 
   if ((eh->elf.root.type == bfd_link_hash_defined
        || eh->elf.root.type == bfd_link_hash_defweak)
@@ -5543,15 +5597,18 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
              && ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN)))
     {
       asection *code_sec;
+      struct ppc_link_hash_entry *fh;
 
       eh->elf.root.u.def.section->flags |= SEC_KEEP;
 
       /* Function descriptor syms cause the associated
         function code sym section to be marked.  */
-      if (eh->is_func_descriptor
-         && (eh->oh->elf.root.type == bfd_link_hash_defined
-             || eh->oh->elf.root.type == bfd_link_hash_defweak))
-       eh->oh->elf.root.u.def.section->flags |= SEC_KEEP;
+      fh = defined_code_entry (eh);
+      if (fh != NULL)
+       {
+         code_sec = fh->elf.root.u.def.section;
+         code_sec->flags |= SEC_KEEP;
+       }
       else if (get_opd_info (eh->elf.root.u.def.section) != NULL
               && opd_entry_value (eh->elf.root.u.def.section,
                                   eh->elf.root.u.def.value,
@@ -5583,7 +5640,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
   if (h != NULL)
     {
       enum elf_ppc64_reloc_type r_type;
-      struct ppc_link_hash_entry *eh;
+      struct ppc_link_hash_entry *eh, *fh, *fdh;
 
       r_type = ELF64_R_TYPE (rel->r_info);
       switch (r_type)
@@ -5598,22 +5655,19 @@ 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
-                 && (eh->oh->elf.root.type == bfd_link_hash_defined
-                     || eh->oh->elf.root.type == bfd_link_hash_defweak))
-               eh = eh->oh;
+             fdh = defined_func_desc (eh);
+             if (fdh != NULL)
+               eh = fdh;
 
              /* Function descriptor syms cause the associated
                 function code sym section to be marked.  */
-             if (eh->is_func_descriptor
-                 && (eh->oh->elf.root.type == bfd_link_hash_defined
-                     || eh->oh->elf.root.type == bfd_link_hash_defweak))
+             fh = defined_code_entry (eh);
+             if (fh != NULL)
                {
                  /* They also mark their opd section.  */
                  eh->elf.root.u.def.section->gc_mark = 1;
 
-                 rsec = eh->oh->elf.root.u.def.section;
+                 rsec = fh->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,
@@ -5693,9 +5747,7 @@ 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;
+         h = elf_follow_link (h);
          eh = (struct ppc_link_hash_entry *) h;
 
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
@@ -6081,18 +6133,17 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
      in dynamic objects are handled elsewhere.  */
   if (fh->elf.root.type == bfd_link_hash_undefweak
       && fh->was_undefined
-      && (fh->oh->elf.root.type == bfd_link_hash_defined
-         || fh->oh->elf.root.type == bfd_link_hash_defweak)
-      && get_opd_info (fh->oh->elf.root.u.def.section) != NULL
-      && opd_entry_value (fh->oh->elf.root.u.def.section,
-                         fh->oh->elf.root.u.def.value,
+      && (fdh = defined_func_desc (fh)) != NULL
+      && get_opd_info (fdh->elf.root.u.def.section) != NULL
+      && opd_entry_value (fdh->elf.root.u.def.section,
+                         fdh->elf.root.u.def.value,
                          &fh->elf.root.u.def.section,
                          &fh->elf.root.u.def.value) != (bfd_vma) -1)
     {
-      fh->elf.root.type = fh->oh->elf.root.type;
+      fh->elf.root.type = fdh->elf.root.type;
       fh->elf.forced_local = 1;
-      fh->elf.def_regular = fh->oh->elf.def_regular;
-      fh->elf.def_dynamic = fh->oh->elf.def_dynamic;
+      fh->elf.def_regular = fdh->elf.def_regular;
+      fh->elf.def_dynamic = fdh->elf.def_dynamic;
     }
 
   /* If this is a function code symbol, transfer dynamic linking
@@ -6111,12 +6162,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
   /* Find the corresponding function descriptor symbol.  Create it
      as undefined if necessary.  */
 
-  fdh = get_fdh (fh, htab);
-  if (fdh != NULL)
-    while (fdh->elf.root.type == bfd_link_hash_indirect
-          || fdh->elf.root.type == bfd_link_hash_warning)
-      fdh = (struct ppc_link_hash_entry *) fdh->elf.root.u.i.link;
-
+  fdh = lookup_fdh (fh, htab);
   if (fdh == NULL
       && !info->executable
       && (fh->elf.root.type == bfd_link_hash_undefined
@@ -6454,9 +6500,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
       struct elf_link_hash_entry *h;
 
       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;
+      h = elf_follow_link (h);
 
       if (hp != NULL)
        *hp = h;
@@ -7024,8 +7068,8 @@ 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 = lookup_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)
@@ -7175,7 +7219,9 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
 /* Set htab->tls_get_addr and call the generic ELF tls_setup function.  */
 
 asection *
-ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
+ppc64_elf_tls_setup (bfd *obfd,
+                    struct bfd_link_info *info,
+                    int no_tls_get_addr_opt)
 {
   struct ppc_link_hash_table *htab;
 
@@ -7183,9 +7229,83 @@ ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
   htab->tls_get_addr = ((struct ppc_link_hash_entry *)
                        elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
                                              FALSE, FALSE, TRUE));
+  /* Move dynamic linking info to the function descriptor sym.  */
+  if (htab->tls_get_addr != NULL)
+    func_desc_adjust (&htab->tls_get_addr->elf, info);
   htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *)
                           elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
                                                 FALSE, FALSE, TRUE));
+  if (!no_tls_get_addr_opt)
+    {
+      struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd;
+
+      opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt",
+                                 FALSE, FALSE, TRUE);
+      if (opt != NULL)
+       func_desc_adjust (opt, info);
+      opt_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt",
+                                    FALSE, FALSE, TRUE);
+      if (opt_fd != NULL
+         && (opt_fd->root.type == bfd_link_hash_defined
+             || opt_fd->root.type == bfd_link_hash_defweak))
+       {
+         /* If glibc supports an optimized __tls_get_addr call stub,
+            signalled by the presence of __tls_get_addr_opt, and we'll
+            be calling __tls_get_addr via a plt call stub, then
+            make __tls_get_addr point to __tls_get_addr_opt.  */
+         tga_fd = &htab->tls_get_addr_fd->elf;
+         if (htab->elf.dynamic_sections_created
+             && tga_fd != NULL
+             && (tga_fd->type == STT_FUNC
+                 || tga_fd->needs_plt)
+             && !(SYMBOL_CALLS_LOCAL (info, tga_fd)
+                  || (ELF_ST_VISIBILITY (tga_fd->other) != STV_DEFAULT
+                      && tga_fd->root.type == bfd_link_hash_undefweak)))
+           {
+             struct plt_entry *ent;
+
+             for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next)
+               if (ent->plt.refcount > 0)
+                 break;
+             if (ent != NULL)
+               {
+                 tga_fd->root.type = bfd_link_hash_indirect;
+                 tga_fd->root.u.i.link = &opt_fd->root;
+                 ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd);
+                 if (opt_fd->dynindx != -1)
+                   {
+                     /* Use __tls_get_addr_opt in dynamic relocations.  */
+                     opt_fd->dynindx = -1;
+                     _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+                                             opt_fd->dynstr_index);
+                     if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
+                       return FALSE;
+                   }
+                 htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd;
+                 tga = &htab->tls_get_addr->elf;
+                 if (opt != NULL && tga != NULL)
+                   {
+                     tga->root.type = bfd_link_hash_indirect;
+                     tga->root.u.i.link = &opt->root;
+                     ppc64_elf_copy_indirect_symbol (info, opt, tga);
+                     _bfd_elf_link_hash_hide_symbol (info, opt,
+                                                     tga->forced_local);
+                     htab->tls_get_addr = (struct ppc_link_hash_entry *) opt;
+                   }
+                 htab->tls_get_addr_fd->oh = htab->tls_get_addr;
+                 htab->tls_get_addr_fd->is_func_descriptor = 1;
+                 if (htab->tls_get_addr != NULL)
+                   {
+                     htab->tls_get_addr->oh = htab->tls_get_addr_fd;
+                     htab->tls_get_addr->is_func = 1;
+                   }
+               }
+           }
+       }
+      else
+       no_tls_get_addr_opt = TRUE;
+    }
+  htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
   return _bfd_elf_tls_setup (obfd, info);
 }
 
@@ -7208,9 +7328,7 @@ branch_reloc_hash_match (const bfd *ibfd,
       struct elf_link_hash_entry *h;
 
       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;
+      h = elf_follow_link (h);
       if (h == &hash1->elf || h == &hash2->elf)
        return TRUE;
     }
@@ -8455,8 +8573,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          for (ent = *local_plt; ent != NULL; ent = ent->next)
            if (ent->plt.refcount > 0)
              {
-               asection *s = htab->iplt;
-
+               s = htab->iplt;
                ent->plt.offset = s->size;
                s->size += PLT_ENTRY_SIZE;
 
@@ -8625,6 +8742,12 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
            return FALSE;
        }
 
+      if (!htab->no_tls_get_addr_opt
+         && htab->tls_get_addr_fd != NULL
+         && htab->tls_get_addr_fd->elf.plt.plist != NULL
+         && !add_dynamic_entry (DT_PPC64_TLSOPT, 0))
+       return FALSE;
+
       if (relocs)
        {
          if (!add_dynamic_entry (DT_RELA, 0)
@@ -8668,9 +8791,9 @@ ppc_type_of_stub (asection *input_sec,
     {
       struct plt_entry *ent;
       struct ppc_link_hash_entry *fdh = h;
-      if (fdh->oh != NULL
-         && fdh->oh->is_func_descriptor)
-       fdh = fdh->oh;
+      if (h->oh != NULL
+         && h->oh->is_func_descriptor)
+       fdh = ppc_follow_link (h->oh);
 
       for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next)
        if (ent->addend == rel->r_addend
@@ -8818,6 +8941,49 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r)
   return p;
 }
 
+/* Build a special .plt call stub for __tls_get_addr.  */
+
+#define LD_R11_0R3     0xe9630000
+#define LD_R12_0R3     0xe9830000
+#define MR_R0_R3       0x7c601b78
+#define CMPDI_R11_0    0x2c2b0000
+#define ADD_R3_R12_R13 0x7c6c6a14
+#define BEQLR          0x4d820020
+#define MR_R3_R0       0x7c030378
+#define MFLR_R11       0x7d6802a6
+#define STD_R11_0R1    0xf9610000
+#define BCTRL          0x4e800421
+#define LD_R11_0R1     0xe9610000
+#define LD_R2_0R1      0xe8410000
+#define MTLR_R11       0x7d6803a6
+
+static inline bfd_byte *
+build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset,
+                        Elf_Internal_Rela *r)
+{
+  bfd_put_32 (obfd, LD_R11_0R3 + 0, p),                p += 4;
+  bfd_put_32 (obfd, LD_R12_0R3 + 8, p),                p += 4;
+  bfd_put_32 (obfd, MR_R0_R3, p),              p += 4;
+  bfd_put_32 (obfd, CMPDI_R11_0, p),           p += 4;
+  bfd_put_32 (obfd, ADD_R3_R12_R13, p),                p += 4;
+  bfd_put_32 (obfd, BEQLR, p),                 p += 4;
+  bfd_put_32 (obfd, MR_R3_R0, p),              p += 4;
+  bfd_put_32 (obfd, MFLR_R11, p),              p += 4;
+  bfd_put_32 (obfd, STD_R11_0R1 + 32, p),      p += 4;
+
+  if (r != NULL)
+    r[0].r_offset += 9 * 4;
+  p = build_plt_stub (obfd, p, offset, r);
+  bfd_put_32 (obfd, BCTRL, p - 4);
+
+  bfd_put_32 (obfd, LD_R11_0R1 + 32, p),       p += 4;
+  bfd_put_32 (obfd, LD_R2_0R1 + 40, p),                p += 4;
+  bfd_put_32 (obfd, MTLR_R11, p),              p += 4;
+  bfd_put_32 (obfd, BLR, p),                   p += 4;
+
+  return p;
+}
+
 static Elf_Internal_Rela *
 get_relocs (asection *sec, int count)
 {
@@ -8944,7 +9110,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              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;
+               h = ppc_follow_link (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;
@@ -9109,22 +9275,20 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       break;
 
     case ppc_stub_plt_call:
-      /* Do the best we can for shared libraries built without
-        exporting ".foo" for each "foo".  This can happen when symbol
-        versioning scripts strip all bar a subset of symbols.  */
       if (stub_entry->h != NULL
-         && stub_entry->h->oh != NULL
-         && stub_entry->h->oh->elf.root.type != bfd_link_hash_defined
-         && stub_entry->h->oh->elf.root.type != bfd_link_hash_defweak)
+         && stub_entry->h->is_func_descriptor
+         && stub_entry->h->oh != NULL)
        {
-         /* Point the symbol at the stub.  There may be multiple stubs,
-            we don't really care;  The main thing is to make this sym
-            defined somewhere.  Maybe defining the symbol in the stub
-            section is a silly idea.  If we didn't do this, htab->top_id
-            could disappear.  */
-         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;
+         struct ppc_link_hash_entry *fh = ppc_follow_link (stub_entry->h->oh);
+
+         /* If the old-ABI "dot-symbol" is undefined make it weak so
+            we don't get a link error from RELOC_FOR_GLOBAL_SYMBOL.
+            FIXME: We used to define the symbol on one of the call
+            stubs instead, which is why we test symbol section id
+            against htab->top_id in various places.  Likely all
+            these checks could now disappear.  */
+         if (fh->elf.root.type == bfd_link_hash_undefined)
+           fh->elf.root.type = bfd_link_hash_undefweak;
        }
 
       /* Now build the stub.  */
@@ -9188,7 +9352,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            r[0].r_offset += 2;
          r[0].r_addend = dest;
        }
-      p = build_plt_stub (htab->stub_bfd, loc, off, r);
+      if (stub_entry->h != NULL
+         && (stub_entry->h == htab->tls_get_addr_fd
+             || stub_entry->h == htab->tls_get_addr)
+         && !htab->no_tls_get_addr_opt)
+       p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r);
+      else
+       p = build_plt_stub (htab->stub_bfd, loc, off, r);
       size = p - loc;
       break;
 
@@ -9277,6 +9447,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        size -= 4;
       if (PPC_HA (off + 16) != PPC_HA (off))
        size += 4;
+      if (stub_entry->h != NULL
+         && (stub_entry->h == htab->tls_get_addr_fd
+             || stub_entry->h == htab->tls_get_addr)
+         && !htab->no_tls_get_addr_opt)
+       size += 13 * 4;
       if (info->emitrelocations)
        {
          stub_entry->stub_sec->reloc_count
@@ -9561,7 +9736,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
       if (eh != NULL
          && (eh->elf.plt.plist != NULL
              || (eh->oh != NULL
-                 && eh->oh->elf.plt.plist != NULL)))
+                 && ppc_follow_link (eh->oh)->elf.plt.plist != NULL)))
        {
          ret = 1;
          break;
@@ -10000,7 +10175,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                         use the func descriptor sym instead if it is
                         defined.  */
                      if (hash->elf.root.root.string[0] == '.'
-                         && (fdh = get_fdh (hash, htab)) != NULL)
+                         && (fdh = lookup_fdh (hash, htab)) != NULL)
                        {
                          if (fdh->elf.root.type == bfd_link_hash_defined
                              || fdh->elf.root.type == bfd_link_hash_defweak)
@@ -10840,37 +11015,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (tls_mask != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
-             bfd_vma rtra;
              insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
-             if ((insn & ((0x3f << 26) | (31 << 11)))
-                 == ((31 << 26) | (13 << 11)))
-               rtra = insn & ((1 << 26) - (1 << 16));
-             else if ((insn & ((0x3f << 26) | (31 << 16)))
-                      == ((31 << 26) | (13 << 16)))
-               rtra = (insn & (31 << 21)) | ((insn & (31 << 11)) << 5);
-             else
+             insn = _bfd_elf_ppc_at_tls_transform (insn, 13);
+             if (insn == 0)
                abort ();
-             if ((insn & ((1 << 11) - (1 << 1))) == 266 << 1)
-               /* add -> addi.  */
-               insn = 14 << 26;
-             else if ((insn & (31 << 1)) == 23 << 1
-                      && ((insn & (31 << 6)) < 14 << 6
-                          || ((insn & (31 << 6)) >= 16 << 6
-                              && (insn & (31 << 6)) < 24 << 6)))
-               /* load and store indexed -> dform.  */
-               insn = (32 | ((insn >> 6) & 31)) << 26;
-             else if ((insn & (31 << 1)) == 21 << 1
-                      && (insn & (0x1a << 6)) == 0)
-               /* ldx, ldux, stdx, stdux -> ld, ldu, std, stdu.  */
-               insn = (((58 | ((insn >> 6) & 4)) << 26)
-                       | ((insn >> 6) & 1));
-             else if ((insn & (31 << 1)) == 21 << 1
-                      && (insn & ((1 << 11) - (1 << 1))) == 341 << 1)
-               /* lwax -> lwa.  */
-               insn = (58 << 26) | 2;
-             else
-               abort ();
-             insn |= rtra;
              bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
              /* Was PPC64_TLS which sits on insn boundary, now
                 PPC64_TPREL16_LO which is at low-order half-word.  */
@@ -10951,8 +11099,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  insn1 |= 58 << 26;    /* ld */
                  insn2 = 0x7c636a14;   /* add 3,3,13 */
                  if (offset != (bfd_vma) -1)
-                   rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
-                                                 R_PPC64_NONE);
+                   rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
                  if ((tls_mask & TLS_EXPLICIT) == 0)
                    r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3)
                              + R_PPC64_GOT_TPREL16_DS);
@@ -11051,8 +11198,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
              /* Zap the reloc on the _tls_get_addr call too.  */
              BFD_ASSERT (offset == rel[1].r_offset);
-             rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
-                                           R_PPC64_NONE);
+             rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
              insn3 = bfd_get_32 (output_bfd,
                                  contents + offset + 4);
              if (insn3 == NOP
@@ -11097,8 +11243,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              rel->r_offset = offset + d_offset;
              /* Zap the reloc on the _tls_get_addr call too.  */
              BFD_ASSERT (offset == rel[1].r_offset);
-             rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
-                                           R_PPC64_NONE);
+             rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
              insn2 = 0x38630000;       /* addi 3,3,0 */
              insn3 = bfd_get_32 (output_bfd,
                                  contents + offset + 4);
@@ -11188,10 +11333,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             base pointer.  */
          stub_entry = NULL;
          fdh = h;
-         if (((h != NULL
-               && (((fdh = h->oh) != NULL
-                    && fdh->elf.plt.plist != NULL)
-                   || (fdh = h)->elf.plt.plist != NULL))
+         if (h != NULL
+             && h->oh != NULL
+             && h->oh->is_func_descriptor)
+           fdh = ppc_follow_link (h->oh);
+         if (((fdh != NULL
+               && fdh->elf.plt.plist != NULL)
               || (sec != NULL
                   && sec->output_section != NULL
                   && sec->id <= htab->top_id
@@ -11214,8 +11361,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  if (nop == NOP
                      || nop == CROR_151515 || nop == CROR_313131)
                    {
-                     bfd_put_32 (input_bfd, LD_R2_40R1,
-                                 contents + rel->r_offset + 4);
+                     if (h != NULL
+                         && (h == htab->tls_get_addr_fd
+                             || h == htab->tls_get_addr)
+                         && !htab->no_tls_get_addr_opt)
+                       {
+                         /* Special stub used, leave nop alone.  */
+                       }
+                     else
+                       bfd_put_32 (input_bfd, LD_R2_40R1,
+                                   contents + rel->r_offset + 4);
                      can_plt_call = TRUE;
                    }
                }
@@ -11762,10 +11917,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      ? h->elf.type == STT_GNU_IFUNC
                      : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
            {
-             Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
              asection *sreloc;
-             bfd_byte *loc;
              bfd_vma out_off;
 
              /* When generating a dynamic object, these relocations
@@ -12226,9 +12379,6 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
 
   if (h->needs_copy)
     {
-      Elf_Internal_Rela rela;
-      bfd_byte *loc;
-
       /* This symbol needs a copy reloc.  Set it up.  */
 
       if (h->dynindx == -1