]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf64-ppc.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / bfd / elf64-ppc.c
index 9372ea46943c0aa40b42cf3f8f87bf36adf3e9a2..a118c32c7978464e6369880fef3ce6433a4f991e 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright (C) 1999-2019 Free Software Foundation, Inc.
+   Copyright (C) 1999-2021 Free Software Foundation, Inc.
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
    Largely rewritten by Alan Modra.
@@ -35,6 +35,9 @@
 #include "elf64-ppc.h"
 #include "dwarf2.h"
 
+/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1.  */
+#define OCTETS_PER_BYTE(ABFD, SEC) 1
+
 static bfd_reloc_status_type ppc64_elf_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_branch_reloc
@@ -111,7 +114,7 @@ static bfd_vma opd_entry_value
 #define elf_backend_adjust_dynamic_symbol     ppc64_elf_adjust_dynamic_symbol
 #define elf_backend_hide_symbol                      ppc64_elf_hide_symbol
 #define elf_backend_maybe_function_sym       ppc64_elf_maybe_function_sym
-#define elf_backend_always_size_sections      ppc64_elf_func_desc_adjust
+#define elf_backend_always_size_sections      ppc64_elf_edit
 #define elf_backend_size_dynamic_sections     ppc64_elf_size_dynamic_sections
 #define elf_backend_hash_symbol                      ppc64_elf_hash_symbol
 #define elf_backend_init_index_section       _bfd_elf_init_2_index_sections
@@ -122,6 +125,7 @@ static bfd_vma opd_entry_value
 #define elf_backend_finish_dynamic_sections   ppc64_elf_finish_dynamic_sections
 #define elf_backend_link_output_symbol_hook   ppc64_elf_output_symbol_hook
 #define elf_backend_special_sections         ppc64_elf_special_sections
+#define elf_backend_section_flags            ppc64_elf_section_flags
 #define elf_backend_merge_symbol_attribute    ppc64_elf_merge_symbol_attribute
 #define elf_backend_merge_symbol             ppc64_elf_merge_symbol
 #define elf_backend_get_reloc_section        bfd_get_section_by_name
@@ -207,9 +211,10 @@ static bfd_vma opd_entry_value
 #define PLD_R12_PC     0x04100000e5800000ULL
 #define PNOP           0x0700000000000000ULL
 
-/* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
+/* __glink_PLTresolve stub instructions.  We enter with the index in
+   R0 for ELFv1, and the address of a glink branch in R12 for ELFv2.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)                    \
-  (8u + (htab->opd_abi ? 11 * 4 : 14 * 4))
+  (8u + (htab->opd_abi ? 11 * 4 : htab->has_plt_localentry0 ? 14 * 4 : 13 * 4))
                                        /* 0:                           */
                                        /*  .quad plt0-1f               */
                                        /* __glink:                     */
@@ -225,11 +230,14 @@ static bfd_vma opd_entry_value
                                        /*  mtctr %12                   */
                                        /*  ld %11,16(%11)              */
                                        /*  bctr                        */
-#define MFLR_R0                0x7c0802a6      /*  mflr %r0                    */
-#define MTLR_R0                0x7c0803a6      /*  mtlr %r0                    */
-#define SUB_R12_R12_R11        0x7d8b6050      /*  subf %r12,%r11,%r12         */
-#define ADDI_R0_R12    0x380c0000      /*  addi %r0,%r12,0             */
-#define SRDI_R0_R0_2   0x7800f082      /*  rldicl %r0,%r0,62,2         */
+
+#define MFLR_R0                0x7c0802a6      /* mflr %r0                     */
+#define MTLR_R0                0x7c0803a6      /* mtlr %r0                     */
+#define SUB_R12_R12_R11        0x7d8b6050      /* subf %r12,%r11,%r12          */
+#define ADDI_R0_R12    0x380c0000      /* addi %r0,%r12,0              */
+#define SRDI_R0_R0_2   0x7800f082      /* rldicl %r0,%r0,62,2          */
+#define LD_R0_0R11     0xe80b0000      /* ld %r0,0(%r11)               */
+#define ADD_R11_R0_R11 0x7d605a14      /* add %r11,%r0,%r11            */
 
 /* Pad with this.  */
 #define NOP            0x60000000
@@ -920,6 +928,24 @@ static reloc_howto_type ppc64_elf_howto_raw[] =
   HOW (R_PPC64_PLT_PCREL34_NOTOC, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
        ppc64_elf_unhandled_reloc),
 
+  HOW (R_PPC64_TPREL34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_DTPREL34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_GOT_TLSGD_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_GOT_TLSLD_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_GOT_TPREL_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_GOT_DTPREL_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
   HOW (R_PPC64_ADDR16_HIGHER34, 1, 16, 0xffff, 34, FALSE, dont,
        bfd_elf_generic_reloc),
 
@@ -977,8 +1003,7 @@ ppc_howto_init (void)
 }
 
 static reloc_howto_type *
-ppc64_elf_reloc_type_lookup (bfd *abfd,
-                            bfd_reloc_code_real_type code)
+ppc64_elf_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
 {
   enum elf_ppc64_reloc_type r = R_PPC64_NONE;
 
@@ -1119,6 +1144,7 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC64_PLTGOT16_LO_DS:       r = R_PPC64_PLTGOT16_LO_DS;
       break;
+    case BFD_RELOC_PPC64_TLS_PCREL:
     case BFD_RELOC_PPC_TLS:                    r = R_PPC64_TLS;
       break;
     case BFD_RELOC_PPC_TLSGD:                  r = R_PPC64_TLSGD;
@@ -1253,6 +1279,18 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC64_PLT_PCREL34:          r = R_PPC64_PLT_PCREL34;
       break;
+    case BFD_RELOC_PPC64_TPREL34:              r = R_PPC64_TPREL34;
+      break;
+    case BFD_RELOC_PPC64_DTPREL34:             r = R_PPC64_DTPREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_TLSGD_PCREL34:    r = R_PPC64_GOT_TLSGD_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_TLSLD_PCREL34:    r = R_PPC64_GOT_TLSLD_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_TPREL_PCREL34:    r = R_PPC64_GOT_TPREL_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_DTPREL_PCREL34:   r = R_PPC64_GOT_DTPREL_PCREL34;
+      break;
     case BFD_RELOC_PPC64_ADDR16_HIGHER34:      r = R_PPC64_ADDR16_HIGHER34;
       break;
     case BFD_RELOC_PPC64_ADDR16_HIGHERA34:     r = R_PPC64_ADDR16_HIGHERA34;
@@ -1283,16 +1321,33 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
 };
 
 static reloc_howto_type *
-ppc64_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
-                            const char *r_name)
+ppc64_elf_reloc_name_lookup (bfd *abfd, const char *r_name)
 {
   unsigned int i;
+  static char *compat_map[][2] = {
+    { "R_PPC64_GOT_TLSGD34", "R_PPC64_GOT_TLSGD_PCREL34" },
+    { "R_PPC64_GOT_TLSLD34", "R_PPC64_GOT_TLSLD_PCREL34" },
+    { "R_PPC64_GOT_TPREL34", "R_PPC64_GOT_TPREL_PCREL34" },
+    { "R_PPC64_GOT_DTPREL34", "R_PPC64_GOT_DTPREL_PCREL34" }
+  };
 
   for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++)
     if (ppc64_elf_howto_raw[i].name != NULL
        && strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
       return &ppc64_elf_howto_raw[i];
 
+  /* Handle old names of relocations in case they were used by
+     .reloc directives.
+     FIXME: Remove this soon.  Mapping the reloc names is very likely
+     completely unnecessary.  */
+  for (i = 0; i < ARRAY_SIZE (compat_map); i++)
+    if (strcasecmp (compat_map[i][0], r_name) == 0)
+      {
+       _bfd_error_handler (_("warning: %s should be used rather than %s"),
+                           compat_map[i][1], compat_map[i][0]);
+       return ppc64_elf_reloc_name_lookup (abfd, compat_map[i][1]);
+      }
+
   return NULL;
 }
 
@@ -1374,7 +1429,7 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
            + input_section->output_section->vma);
   value = (bfd_signed_vma) value >> 16;
 
-  octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+  octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section);
   insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
   insn &= ~0x1fffc1;
   insn |= (value & 0xffc1) | ((value & 0x3e) << 15);
@@ -1449,7 +1504,7 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
     return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
                                  input_section, output_bfd, error_message);
 
-  octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+  octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section);
   insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
   insn &= ~(0x01 << 21);
   r_type = reloc_entry->howto->type;
@@ -1599,7 +1654,7 @@ ppc64_elf_toc64_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
   if (TOCstart == 0)
     TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner);
 
-  octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+  octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section);
   bfd_put_64 (abfd, TOCstart + TOC_BASE_OFF, (bfd_byte *) data + octets);
   return bfd_reloc_ok;
 }
@@ -1660,10 +1715,12 @@ ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
 
   if (error_message != NULL)
     {
-      static char buf[60];
-      sprintf (buf, "generic linker can't handle %s",
-              reloc_entry->howto->name);
-      *error_message = buf;
+      static char *message;
+      free (message);
+      if (asprintf (&message, _("generic linker can't handle %s"),
+                   reloc_entry->howto->name) < 0)
+       message = NULL;
+      *error_message = message;
     }
   return bfd_reloc_dangerous;
 }
@@ -1749,8 +1806,9 @@ struct ppc64_elf_obj_tdata
      instruction not one we handle.  */
   unsigned int unexpected_toc_insn : 1;
 
-  /* Set if got relocs that can be optimised are present in this file.  */
-  unsigned int has_gotrel : 1;
+  /* Set if PLT/GOT/TOC relocs that can be optimised are present in
+     this file.  */
+  unsigned int has_optrel : 1;
 };
 
 #define ppc64_elf_tdata(bfd) \
@@ -1951,8 +2009,9 @@ struct _ppc64_elf_section_data
   /* Flag set when PLTCALL relocs are detected.  */
   unsigned int has_pltcall:1;
 
-  /* Flag set when section has GOT relocations that can be optimised.  */
-  unsigned int has_gotrel:1;
+  /* Flag set when section has PLT/GOT/TOC relocations that can be
+     optimised.  */
+  unsigned int has_optrel:1;
 };
 
 #define ppc64_elf_section_data(sec) \
@@ -1964,7 +2023,7 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   if (!sec->used_by_bfd)
     {
       struct _ppc64_elf_section_data *sdata;
-      bfd_size_type amt = sizeof (*sdata);
+      size_t amt = sizeof (*sdata);
 
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
@@ -1975,6 +2034,18 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
+static bfd_boolean
+ppc64_elf_section_flags (const Elf_Internal_Shdr *hdr)
+{
+  const char *name = hdr->bfd_section->name;
+
+  if (strncmp (name, ".sbss", 5) == 0
+      || strncmp (name, ".sdata", 6) == 0)
+    hdr->bfd_section->flags |= SEC_SMALL_DATA;
+
+  return TRUE;
+}
+
 static struct _opd_sec_data *
 get_opd_info (asection * sec)
 {
@@ -1987,7 +2058,7 @@ get_opd_info (asection * sec)
 \f
 /* Parameters for the qsort hook.  */
 static bfd_boolean synthetic_relocatable;
-static asection *synthetic_opd;
+static const asection *synthetic_opd;
 
 /* qsort comparison function for ppc64_elf_get_synthetic_symtab.  */
 
@@ -2068,15 +2139,26 @@ compare_symbols (const void *ap, const void *bp)
   if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0)
     return 1;
 
-  return a > b;
+  /* Finally, sort on where the symbol is in memory.  The symbols will
+     be in at most two malloc'd blocks, one for static syms, one for
+     dynamic syms, and we distinguish the two blocks above by testing
+     BSF_DYNAMIC.  Since we are sorting the symbol pointers which were
+     originally in the same order as the symbols (and we're not
+     sorting the symbols themselves), this ensures a stable sort.  */
+  if (a < b)
+    return -1;
+  if (a > b)
+    return 1;
+  return 0;
 }
 
 /* Search SYMS for a symbol of the given VALUE.  */
 
 static asymbol *
-sym_exists_at (asymbol **syms, long lo, long hi, unsigned int id, bfd_vma value)
+sym_exists_at (asymbol **syms, size_t lo, size_t hi, unsigned int id,
+              bfd_vma value)
 {
-  long mid;
+  size_t mid;
 
   if (id == (unsigned) -1)
     {
@@ -2362,8 +2444,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
        free_contents_and_exit_err:
          count = -1;
        free_contents_and_exit:
-         if (contents)
-           free (contents);
+         free (contents);
          goto done;
        }
 
@@ -2712,6 +2793,12 @@ must_be_dyn_reloc (struct bfd_link_info *info,
     case R_PPC64_REL32:
     case R_PPC64_REL64:
     case R_PPC64_REL30:
+    case R_PPC64_TOC16:
+    case R_PPC64_TOC16_DS:
+    case R_PPC64_TOC16_LO:
+    case R_PPC64_TOC16_HI:
+    case R_PPC64_TOC16_HA:
+    case R_PPC64_TOC16_LO_DS:
       return 0;
 
     case R_PPC64_TPREL16:
@@ -2727,6 +2814,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
     case R_PPC64_TPREL16_HIGHEST:
     case R_PPC64_TPREL16_HIGHESTA:
     case R_PPC64_TPREL64:
+    case R_PPC64_TPREL34:
       /* These relocations are relative but in a shared library the
         linker doesn't know the thread pointer base.  */
       return bfd_link_dll (info);
@@ -2734,20 +2822,20 @@ must_be_dyn_reloc (struct bfd_link_info *info,
 }
 
 /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
-   copying dynamic variables from a shared lib into an app's dynbss
+   copying dynamic variables from a shared lib into an app's .dynbss
    section, and instead use a dynamic relocation to point into the
-   shared lib.  With code that gcc generates, it's vital that this be
-   enabled;  In the PowerPC64 ABI, the address of a function is actually
-   the address of a function descriptor, which resides in the .opd
-   section.  gcc uses the descriptor directly rather than going via the
-   GOT as some other ABI's do, which means that initialized function
-   pointers must reference the descriptor.  Thus, a function pointer
-   initialized to the address of a function in a shared library will
-   either require a copy reloc, or a dynamic reloc.  Using a copy reloc
-   redefines the function descriptor symbol to point to the copy.  This
-   presents a problem as a plt entry for that function is also
-   initialized from the function descriptor symbol and the copy reloc
-   may not be initialized first.  */
+   shared lib.  With code that gcc generates it is vital that this be
+   enabled;  In the PowerPC64 ELFv1 ABI the address of a function is
+   actually the address of a function descriptor which resides in the
+   .opd section.  gcc uses the descriptor directly rather than going
+   via the GOT as some other ABIs do, which means that initialized
+   function pointers reference the descriptor.  Thus, a function
+   pointer initialized to the address of a function in a shared
+   library will either require a .dynbss copy and a copy reloc, or a
+   dynamic reloc.  Using a .dynbss copy redefines the function
+   descriptor symbol to point to the copy.  This presents a problem as
+   a PLT entry for that function is also initialized from the function
+   descriptor symbol and the copy may not be initialized first.  */
 #define ELIMINATE_COPY_RELOCS 1
 
 /* Section name for stubs is the associated section name plus this
@@ -2763,8 +2851,8 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    ppc_stub_plt_branch:
    Similar to the above, but a 24 bit branch in the stub section won't
    reach its destination.
-   .   addis   %r11,%r2,xxx@toc@ha
-   .   ld      %r12,xxx@toc@l(%r11)
+   .   addis   %r12,%r2,xxx@toc@ha
+   .   ld      %r12,xxx@toc@l(%r12)
    .   mtctr   %r12
    .   bctr
 
@@ -2790,8 +2878,8 @@ must_be_dyn_reloc (struct bfd_link_info *info,
 
    A ppc_stub_plt_branch with an r2 offset looks like:
    .   std     %r2,40(%r1)
-   .   addis   %r11,%r2,xxx@toc@ha
-   .   ld      %r12,xxx@toc@l(%r11)
+   .   addis   %r12,%r2,xxx@toc@ha
+   .   ld      %r12,xxx@toc@l(%r12)
    .   addis   %r2,%r2,off@ha
    .   addi    %r2,%r2,off@l
    .   mtctr   %r12
@@ -2847,7 +2935,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    .   mtctr   %r12
    .   bctr
 
-   There are also ELFv1 powerxx variants of these stubs.
+   There are also ELFv1 power10 variants of these stubs.
    ppc_stub_long_branch_notoc:
    .   pla     %r12,dest@pcrel
    .   b       dest
@@ -2870,7 +2958,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
 
    In cases where the high instructions would add zero, they are
    omitted and following instructions modified in some cases.
-   For example, a powerxx ppc_stub_plt_call_notoc might simplify down
+   For example, a power10 ppc_stub_plt_call_notoc might simplify down
    to
    .   pld     %r12,xxx@pcrel
    .   mtctr   %r12
@@ -3009,9 +3097,6 @@ struct ppc_link_hash_entry
     struct ppc_link_hash_entry *next_dot_sym;
   } u;
 
-  /* Track dynamic relocs copied for this symbol.  */
-  struct elf_dyn_relocs *dyn_relocs;
-
   /* Link between function code and descriptor symbols.  */
   struct ppc_link_hash_entry *oh;
 
@@ -3039,9 +3124,7 @@ struct ppc_link_hash_entry
      of the other TLS bits are set.  tls_optimize clears bits when
      optimizing to indicate the corresponding GOT entry type is not
      needed.  If set, TLS_TLS is never cleared.  tls_optimize may also
-     set TLS_TPRELGD when a GD reloc turns into a TPREL one.  We use a
-     separate flag rather than setting TPREL just for convenience in
-     distinguishing the two cases.
+     set TLS_GDIE when a GD reloc turns into an IE one.
      These flags are also kept for local symbols.  */
 #define TLS_TLS                 1      /* Any TLS reloc.  */
 #define TLS_GD          2      /* GD reloc. */
@@ -3049,8 +3132,8 @@ struct ppc_link_hash_entry
 #define TLS_TPREL       8      /* TPREL reloc, => IE. */
 #define TLS_DTPREL     16      /* DTPREL reloc, => LD. */
 #define TLS_MARK       32      /* __tls_get_addr call marked. */
-#define TLS_TPRELGD    64      /* TPREL reloc resulting from GD->IE. */
-#define TLS_EXPLICIT   128     /* Marks TOC section TLS relocs. */
+#define TLS_GDIE       64      /* GOT TPREL reloc resulting from GD->IE. */
+#define TLS_EXPLICIT   256     /* TOC section TLS reloc, not stored. */
   unsigned char tls_mask;
 
   /* The above field is also used to mark function symbols.  In which
@@ -3060,6 +3143,12 @@ struct ppc_link_hash_entry
 #define NON_GOT        256     /* local symbol plt, not stored.  */
 };
 
+static inline struct ppc_link_hash_entry *
+ppc_elf_hash_entry (struct elf_link_hash_entry *ent)
+{
+  return (struct ppc_link_hash_entry *) ent;
+}
+
 /* ppc64 ELF linker hash table.  */
 
 struct ppc_link_hash_table
@@ -3122,6 +3211,9 @@ struct ppc_link_hash_table
   /* Shortcut to .__tls_get_addr and __tls_get_addr.  */
   struct ppc_link_hash_entry *tls_get_addr;
   struct ppc_link_hash_entry *tls_get_addr_fd;
+  struct ppc_link_hash_entry *tga_desc;
+  struct ppc_link_hash_entry *tga_desc_fd;
+  struct map_stub *tga_group;
 
   /* The size of reliplt used by got entry relocs.  */
   bfd_size_type got_reli_size;
@@ -3153,25 +3245,17 @@ struct ppc_link_hash_table
   /* Whether func_desc_adjust needs to be run over symbols.  */
   unsigned int need_func_desc_adj:1;
 
-  /* Whether there exist local gnu indirect function resolvers,
-     referenced by dynamic relocations.  */
-  unsigned int local_ifunc_resolver:1;
-  unsigned int maybe_local_ifunc_resolver:1;
-
   /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized.  */
   unsigned int has_plt_localentry0:1;
 
   /* Whether calls are made via the PLT from NOTOC functions.  */
   unsigned int notoc_plt:1;
 
-  /* Whether to use powerxx instructions in linkage stubs.  */
-  unsigned int powerxx_stubs:1;
+  /* Whether any code linked seems to be Power10.  */
+  unsigned int has_power10_relocs:1;
 
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
-
-  /* Small local sym cache.  */
-  struct sym_cache sym_cache;
 };
 
 /* Rename some of the generic section flags to better document how they
@@ -3180,8 +3264,9 @@ struct ppc_link_hash_table
 /* Nonzero if this section has TLS related relocations.  */
 #define has_tls_reloc sec_flg0
 
-/* Nonzero if this section has an old-style call to __tls_get_addr.  */
-#define has_tls_get_addr_call sec_flg1
+/* Nonzero if this section has a call to __tls_get_addr lacking marker
+   relocations.  */
+#define nomark_tls_get_addr sec_flg1
 
 /* Nonzero if this section has any toc or got relocs.  */
 #define has_toc_reloc sec_flg2
@@ -3197,8 +3282,9 @@ struct ppc_link_hash_table
 /* Get the ppc64 ELF linker hash table from a link_info structure.  */
 
 #define ppc_hash_table(p) \
-  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
-  == PPC64_ELF_DATA ? ((struct ppc_link_hash_table *) ((p)->hash)) : NULL)
+  ((is_elf_hash_table ((p)->hash)                                      \
+    && elf_hash_table_id (elf_hash_table (p)) == PPC64_ELF_DATA)       \
+   ? (struct ppc_link_hash_table *) (p)->hash : NULL)
 
 #define ppc_stub_hash_lookup(table, string, create, copy) \
   ((struct ppc_stub_hash_entry *) \
@@ -3373,7 +3459,7 @@ static struct bfd_link_hash_table *
 ppc64_elf_link_hash_table_create (bfd *abfd)
 {
   struct ppc_link_hash_table *htab;
-  bfd_size_type amt = sizeof (struct ppc_link_hash_table);
+  size_t amt = sizeof (struct ppc_link_hash_table);
 
   htab = bfd_zmalloc (amt);
   if (htab == NULL)
@@ -3449,7 +3535,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
       htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
                                                       flags);
       if (htab->sfpr == NULL
-         || !bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+         || !bfd_set_section_alignment (htab->sfpr, 2))
        return FALSE;
     }
 
@@ -3460,7 +3546,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink",
                                                    flags);
   if (htab->glink == NULL
-      || !bfd_set_section_alignment (dynobj, htab->glink, 3))
+      || !bfd_set_section_alignment (htab->glink, 3))
     return FALSE;
 
   /* The part of .glink used by global entry stubs, separate so that
@@ -3468,7 +3554,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->global_entry = bfd_make_section_anyway_with_flags (dynobj, ".glink",
                                                           flags);
   if (htab->global_entry == NULL
-      || !bfd_set_section_alignment (dynobj, htab->global_entry, 2))
+      || !bfd_set_section_alignment (htab->global_entry, 2))
     return FALSE;
 
   if (!info->no_ld_generated_unwind_info)
@@ -3479,14 +3565,14 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
                                                                 ".eh_frame",
                                                                 flags);
       if (htab->glink_eh_frame == NULL
-         || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2))
+         || !bfd_set_section_alignment (htab->glink_eh_frame, 2))
        return FALSE;
     }
 
   flags = SEC_ALLOC | SEC_LINKER_CREATED;
   htab->elf.iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags);
   if (htab->elf.iplt == NULL
-      || !bfd_set_section_alignment (dynobj, htab->elf.iplt, 3))
+      || !bfd_set_section_alignment (htab->elf.iplt, 3))
     return FALSE;
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
@@ -3494,7 +3580,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->elf.irelplt
     = bfd_make_section_anyway_with_flags (dynobj, ".rela.iplt", flags);
   if (htab->elf.irelplt == NULL
-      || !bfd_set_section_alignment (dynobj, htab->elf.irelplt, 3))
+      || !bfd_set_section_alignment (htab->elf.irelplt, 3))
     return FALSE;
 
   /* Create branch lookup table for plt_branch stubs.  */
@@ -3503,7 +3589,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
                                                   flags);
   if (htab->brlt == NULL
-      || !bfd_set_section_alignment (dynobj, htab->brlt, 3))
+      || !bfd_set_section_alignment (htab->brlt, 3))
     return FALSE;
 
   /* Local plt entries, put in .branch_lt but a separate section for
@@ -3511,7 +3597,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->pltlocal = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
                                                       flags);
   if (htab->pltlocal == NULL
-      || !bfd_set_section_alignment (dynobj, htab->pltlocal, 3))
+      || !bfd_set_section_alignment (htab->pltlocal, 3))
     return FALSE;
 
   if (!bfd_link_pic (info))
@@ -3522,13 +3608,13 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->relbrlt
     = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags);
   if (htab->relbrlt == NULL
-      || !bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
+      || !bfd_set_section_alignment (htab->relbrlt, 3))
     return FALSE;
 
   htab->relpltlocal
     = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags);
   if (htab->relpltlocal == NULL
-      || !bfd_set_section_alignment (dynobj, htab->relpltlocal, 3))
+      || !bfd_set_section_alignment (htab->relpltlocal, 3))
     return FALSE;
 
   return TRUE;
@@ -3600,6 +3686,37 @@ ppc_stub_name (const asection *input_section,
   return stub_name;
 }
 
+/* If mixing power10 with non-power10 code and --power10-stubs is not
+   specified (or is auto) then calls using @notoc relocations that
+   need a stub will utilize power10 instructions in the stub, and
+   calls without @notoc relocations will not use power10 instructions.
+   The two classes of stubs are stored in separate stub_hash_table
+   entries having the same key string.  The two entries will always be
+   adjacent on entry->root.next chain, even if hash table resizing
+   occurs.  This function selects the correct entry to use.  */
+
+static struct ppc_stub_hash_entry *
+select_alt_stub (struct ppc_stub_hash_entry *entry, bfd_boolean notoc)
+{
+  bfd_boolean have_notoc;
+
+  have_notoc = (entry->stub_type == ppc_stub_plt_call_notoc
+               || entry->stub_type == ppc_stub_plt_branch_notoc
+               || entry->stub_type == ppc_stub_long_branch_notoc);
+
+  if (have_notoc != notoc)
+    {
+      const char *stub_name = entry->root.string;
+
+      entry = (struct ppc_stub_hash_entry *) entry->root.next;
+      if (entry != NULL
+         && entry->root.string != stub_name)
+       entry = NULL;
+    }
+
+  return entry;
+}
+
 /* Look up an entry in the stub hash.  Stub entries are cached because
    creating the stub name takes a bit of time.  */
 
@@ -3644,6 +3761,13 @@ ppc_get_stub_entry (const asection *input_section,
       free (stub_name);
     }
 
+  if (stub_entry != NULL && htab->params->power10_stubs == -1)
+    {
+      bfd_boolean notoc = ELF64_R_TYPE (rel->r_info) == R_PPC64_REL24_NOTOC;
+
+      stub_entry = select_alt_stub (stub_entry, notoc);
+    }
+
   return stub_entry;
 }
 
@@ -3724,13 +3848,13 @@ create_got_section (bfd *abfd, struct bfd_link_info *info)
 
   got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
   if (!got
-      || !bfd_set_section_alignment (abfd, got, 3))
+      || !bfd_set_section_alignment (got, 3))
     return FALSE;
 
   relgot = bfd_make_section_anyway_with_flags (abfd, ".rela.got",
                                               flags | SEC_READONLY);
   if (!relgot
-      || !bfd_set_section_alignment (abfd, relgot, 3))
+      || !bfd_set_section_alignment (relgot, 3))
     return FALSE;
 
   ppc64_elf_tdata (abfd)->got = got;
@@ -3758,7 +3882,7 @@ elf_follow_link (struct elf_link_hash_entry *h)
 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);
+  return ppc_elf_hash_entry (elf_follow_link (&h->elf));
 }
 
 /* Merge PLT info on FROM with that on TO.  */
@@ -3805,8 +3929,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
 {
   struct ppc_link_hash_entry *edir, *eind;
 
-  edir = (struct ppc_link_hash_entry *) dir;
-  eind = (struct ppc_link_hash_entry *) ind;
+  edir = ppc_elf_hash_entry (dir);
+  eind = ppc_elf_hash_entry (ind);
 
   edir->is_func |= eind->is_func;
   edir->is_func_descriptor |= eind->is_func_descriptor;
@@ -3832,20 +3956,20 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
     return;
 
   /* Copy over any dynamic relocs we may have on the indirect sym.  */
-  if (eind->dyn_relocs != NULL)
+  if (ind->dyn_relocs != NULL)
     {
-      if (edir->dyn_relocs != NULL)
+      if (dir->dyn_relocs != NULL)
        {
          struct elf_dyn_relocs **pp;
          struct elf_dyn_relocs *p;
 
          /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
-         for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+         for (pp = &ind->dyn_relocs; (p = *pp) != NULL; )
            {
              struct elf_dyn_relocs *q;
 
-             for (q = edir->dyn_relocs; q != NULL; q = q->next)
+             for (q = dir->dyn_relocs; q != NULL; q = q->next)
                if (q->sec == p->sec)
                  {
                    q->pc_count += p->pc_count;
@@ -3856,11 +3980,11 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
              if (q == NULL)
                pp = &p->next;
            }
-         *pp = edir->dyn_relocs;
+         *pp = dir->dyn_relocs;
        }
 
-      edir->dyn_relocs = eind->dyn_relocs;
-      eind->dyn_relocs = NULL;
+      dir->dyn_relocs = ind->dyn_relocs;
+      ind->dyn_relocs = NULL;
     }
 
   /* Copy over got entries that we may have already seen to the
@@ -3922,8 +4046,8 @@ lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
     {
       const char *fd_name = fh->elf.root.root.string + 1;
 
-      fdh = (struct ppc_link_hash_entry *)
-       elf_link_hash_lookup (&htab->elf, fd_name, FALSE, FALSE, FALSE);
+      fdh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, fd_name,
+                                                     FALSE, FALSE, FALSE));
       if (fdh == NULL)
        return fdh;
 
@@ -4030,12 +4154,12 @@ ppc64_elf_add_symbol_hook (bfd *ibfd,
 
 static void
 ppc64_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
-                                 const Elf_Internal_Sym *isym,
+                                 unsigned int st_other,
                                  bfd_boolean definition,
                                  bfd_boolean dynamic)
 {
   if (definition && (!dynamic || !h->def_regular))
-    h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
+    h->other = ((st_other & ~ELF_ST_VISIBILITY (-1))
                | ELF_ST_VISIBILITY (h->other));
 }
 
@@ -4051,9 +4175,9 @@ ppc64_elf_merge_symbol (struct elf_link_hash_entry *h,
                        bfd *oldbfd ATTRIBUTE_UNUSED,
                        const asection *oldsec ATTRIBUTE_UNUSED)
 {
-  ((struct ppc_link_hash_entry *) h)->fake = 0;
+  ppc_elf_hash_entry (h)->fake = 0;
   if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
-    ((struct ppc_link_hash_entry *) h)->non_zero_localentry = 1;
+    ppc_elf_hash_entry (h)->non_zero_localentry = 1;
   return TRUE;
 }
 
@@ -4075,7 +4199,7 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
   if (h != NULL
       /* Don't return this sym if it is a fake function descriptor
         created by add_symbol_adjust.  */
-      && !((struct ppc_link_hash_entry *) h)->fake)
+      && !ppc_elf_hash_entry (h)->fake)
     return h;
 
   if (name[0] == '.')
@@ -4089,6 +4213,11 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
   memcpy (dot_name + 1, name, len + 1);
   h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name);
   bfd_release (abfd, dot_name);
+  if (h != NULL)
+    return h;
+
+  if (strcmp (name, "__tls_get_addr_opt") == 0)
+    h = _bfd_elf_archive_symbol_lookup (abfd, info, "__tls_get_addr_desc");
   return h;
 }
 
@@ -4258,7 +4387,8 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
              Elf_Internal_Sym *isym;
              asection *s;
 
-             isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, r_symndx);
+             isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, ibfd,
+                                           r_symndx);
              if (isym == NULL)
                {
                  if (elf_section_data (opd)->relocs != relocs)
@@ -4364,7 +4494,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
          break;
       if (ent == NULL)
        {
-         bfd_size_type amt = sizeof (*ent);
+         size_t amt = sizeof (*ent);
          ent = bfd_alloc (abfd, amt);
          if (ent == NULL)
            return FALSE;
@@ -4396,7 +4526,7 @@ update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend)
       break;
   if (ent == NULL)
     {
-      bfd_size_type amt = sizeof (*ent);
+      size_t amt = sizeof (*ent);
       ent = bfd_alloc (abfd, amt);
       if (ent == NULL)
        return FALSE;
@@ -4460,15 +4590,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   if (bfd_link_relocatable (info))
     return TRUE;
 
-  /* Don't do anything special with non-loaded, non-alloced sections.
-     In particular, any relocs in such sections should not affect GOT
-     and PLT reference counting (ie. we don't allow them to create GOT
-     or PLT entries), there's no possibility or desire to optimize TLS
-     relocs, and there's not much point in propagating relocs to shared
-     libs that the dynamic linker won't relocate.  */
-  if ((sec->flags & SEC_ALLOC) == 0)
-    return TRUE;
-
   BFD_ASSERT (is_ppc64_elf (abfd));
 
   htab = ppc_hash_table (info);
@@ -4492,7 +4613,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int tls_type;
       struct _ppc64_elf_section_data *ppc64_sec;
       struct plt_entry **ifunc, **plt_list;
-      bfd_vma sym_addend;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -4506,8 +4626,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            sec->has_toc_reloc = 1;
        }
 
-      tls_type = 0;
-      ifunc = NULL;
       r_type = ELF64_R_TYPE (rel->r_info);
       switch (r_type)
        {
@@ -4516,27 +4634,51 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_D34_HI30:
        case R_PPC64_D34_HA30:
        case R_PPC64_D28:
-         htab->powerxx_stubs = 1;
-         /* Fall through.  */
-       default:
-         /* Somewhat foolishly, because the ABIs don't specifically
-            allow it, ppc64 gas and ld support GOT and PLT relocs
-            with non-zero addends where the addend results in
-            sym+addend being stored in the GOT or PLT entry.  This
-            can't be supported for pcrel relocs because the addend is
-            used to specify the pcrel offset.  */
-         sym_addend = rel->r_addend;
-         break;
-
+       case R_PPC64_TPREL34:
+       case R_PPC64_DTPREL34:
        case R_PPC64_PCREL34:
        case R_PPC64_GOT_PCREL34:
+       case R_PPC64_GOT_TLSGD_PCREL34:
+       case R_PPC64_GOT_TLSLD_PCREL34:
+       case R_PPC64_GOT_TPREL_PCREL34:
+       case R_PPC64_GOT_DTPREL_PCREL34:
        case R_PPC64_PLT_PCREL34:
        case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_PCREL28:
-         htab->powerxx_stubs = 1;
-         sym_addend = 0;
+         htab->has_power10_relocs = 1;
+         break;
+       default:
+         break;
+       }
+
+      switch (r_type)
+       {
+       case R_PPC64_PLT16_HA:
+       case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_GOT16_HA:
+       case R_PPC64_TOC16_HA:
+       case R_PPC64_PLT16_LO:
+       case R_PPC64_PLT16_LO_DS:
+       case R_PPC64_GOT_TLSLD16_LO:
+       case R_PPC64_GOT_TLSGD16_LO:
+       case R_PPC64_GOT_TPREL16_LO_DS:
+       case R_PPC64_GOT_DTPREL16_LO_DS:
+       case R_PPC64_GOT16_LO:
+       case R_PPC64_GOT16_LO_DS:
+       case R_PPC64_TOC16_LO:
+       case R_PPC64_TOC16_LO_DS:
+       case R_PPC64_GOT_PCREL34:
+         ppc64_elf_tdata (abfd)->has_optrel = 1;
+         ppc64_elf_section_data (sec)->has_optrel = 1;
+         break;
+       default:
          break;
        }
+
+      ifunc = NULL;
       if (h != NULL)
        {
          if (h->type == STT_GNU_IFUNC)
@@ -4547,7 +4689,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
       else
        {
-         Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+         Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                          abfd, r_symndx);
          if (isym == NULL)
            return FALSE;
@@ -4555,13 +4697,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                            sym_addend,
+                                            rel->r_addend,
                                             NON_GOT | PLT_IFUNC);
              if (ifunc == NULL)
                return FALSE;
            }
        }
 
+      tls_type = 0;
       switch (r_type)
        {
        case R_PPC64_TLSGD:
@@ -4569,10 +4712,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* These special tls relocs tie a call to __tls_get_addr with
             its parameter symbol.  */
          if (h != NULL)
-           ((struct ppc_link_hash_entry *) h)->tls_mask |= TLS_TLS | TLS_MARK;
+           ppc_elf_hash_entry (h)->tls_mask |= TLS_TLS | TLS_MARK;
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend,
+                                       rel->r_addend,
                                        NON_GOT | TLS_TLS | TLS_MARK))
              return FALSE;
          sec->has_tls_reloc = 1;
@@ -4582,6 +4725,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT_TLSLD16_LO:
        case R_PPC64_GOT_TLSLD16_HI:
        case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TLSLD_PCREL34:
          tls_type = TLS_TLS | TLS_LD;
          goto dogottls;
 
@@ -4589,6 +4733,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT_TLSGD16_LO:
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TLSGD_PCREL34:
          tls_type = TLS_TLS | TLS_GD;
          goto dogottls;
 
@@ -4596,6 +4741,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT_TPREL16_LO_DS:
        case R_PPC64_GOT_TPREL16_HI:
        case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_TPREL_PCREL34:
          if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          tls_type = TLS_TLS | TLS_TPREL;
@@ -4605,22 +4751,19 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT_DTPREL16_LO_DS:
        case R_PPC64_GOT_DTPREL16_HI:
        case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_GOT_DTPREL_PCREL34:
          tls_type = TLS_TLS | TLS_DTPREL;
        dogottls:
          sec->has_tls_reloc = 1;
          goto dogot;
 
-       case R_PPC64_GOT16_DS:
+       case R_PPC64_GOT16:
+       case R_PPC64_GOT16_LO:
+       case R_PPC64_GOT16_HI:
        case R_PPC64_GOT16_HA:
+       case R_PPC64_GOT16_DS:
        case R_PPC64_GOT16_LO_DS:
        case R_PPC64_GOT_PCREL34:
-         ppc64_elf_tdata (abfd)->has_gotrel = 1;
-         ppc64_elf_section_data (sec)->has_gotrel = 1;
-         /* Fall through.  */
-
-       case R_PPC64_GOT16:
-       case R_PPC64_GOT16_HI:
-       case R_PPC64_GOT16_LO:
        dogot:
          /* This symbol requires a global offset table entry.  */
          sec->has_toc_reloc = 1;
@@ -4644,20 +4787,20 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              struct ppc_link_hash_entry *eh;
              struct got_entry *ent;
 
-             eh = (struct ppc_link_hash_entry *) h;
+             eh = ppc_elf_hash_entry (h);
              for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
-               if (ent->addend == sym_addend
+               if (ent->addend == rel->r_addend
                    && ent->owner == abfd
                    && ent->tls_type == tls_type)
                  break;
              if (ent == NULL)
                {
-                 bfd_size_type amt = sizeof (*ent);
+                 size_t amt = sizeof (*ent);
                  ent = bfd_alloc (abfd, amt);
                  if (ent == NULL)
                    return FALSE;
                  ent->next = eh->elf.got.glist;
-                 ent->addend = sym_addend;
+                 ent->addend = rel->r_addend;
                  ent->owner = abfd;
                  ent->tls_type = tls_type;
                  ent->is_indirect = FALSE;
@@ -4670,16 +4813,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          else
            /* This is a global offset table entry for a local symbol.  */
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend, tls_type))
+                                       rel->r_addend, tls_type))
              return FALSE;
-
-         /* We may also need a plt entry if the symbol turns out to be
-            an ifunc.  */
-         if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
-           {
-             if (!update_plt_info (abfd, &h->plt.plist, sym_addend))
-               return FALSE;
-           }
          break;
 
        case R_PPC64_PLT16_HA:
@@ -4697,15 +4832,15 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              h->needs_plt = 1;
              if (h->root.root.string[0] == '.'
                  && h->root.root.string[1] != '\0')
-               ((struct ppc_link_hash_entry *) h)->is_func = 1;
-             ((struct ppc_link_hash_entry *) h)->tls_mask |= PLT_KEEP;
+               ppc_elf_hash_entry (h)->is_func = 1;
+             ppc_elf_hash_entry (h)->tls_mask |= PLT_KEEP;
              plt_list = &h->plt.plist;
            }
          if (plt_list == NULL)
            plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                             sym_addend,
+                                             rel->r_addend,
                                              NON_GOT | PLT_KEEP);
-         if (!update_plt_info (abfd, plt_list, sym_addend))
+         if (!update_plt_info (abfd, plt_list, rel->r_addend))
            return FALSE;
          break;
 
@@ -4776,6 +4911,16 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_TOC16_HA:
        case R_PPC64_TOC16_LO_DS:
          sec->has_toc_reloc = 1;
+         if (h != NULL && bfd_link_executable (info))
+           {
+             /* We may need a copy reloc.  */
+             h->non_got_ref = 1;
+             /* Strongly prefer a copy reloc over a dynamic reloc.
+                glibc ld.so as of 2019-08 will error out if one of
+                these relocations is emitted.  */
+             h->needs_copy = 1;
+             goto dodyn;
+           }
          break;
 
          /* Marker reloc.  */
@@ -4815,7 +4960,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              {
                Elf_Internal_Sym *isym;
 
-               isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+               isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                              abfd, r_symndx);
                if (isym == NULL)
                  return FALSE;
@@ -4842,7 +4987,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              h->needs_plt = 1;
              if (h->root.root.string[0] == '.'
                  && h->root.root.string[1] != '\0')
-               ((struct ppc_link_hash_entry *) h)->is_func = 1;
+               ppc_elf_hash_entry (h)->is_func = 1;
 
              if (h == tga || h == dottga)
                {
@@ -4855,7 +5000,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    ;
                  else
                    /* Mark this section as having an old-style call.  */
-                   sec->has_tls_get_addr_call = 1;
+                   sec->nomark_tls_get_addr = 1;
                }
              plt_list = &h->plt.plist;
            }
@@ -4863,7 +5008,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* We may need a .plt entry if the function this reloc
             refers to is in a shared lib.  */
          if (plt_list
-             && !update_plt_info (abfd, plt_list, sym_addend))
+             && !update_plt_info (abfd, plt_list, rel->r_addend))
            return FALSE;
          break;
 
@@ -4900,14 +5045,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        dotlstoc:
          sec->has_tls_reloc = 1;
          if (h != NULL)
-           {
-             struct ppc_link_hash_entry *eh;
-             eh = (struct ppc_link_hash_entry *) h;
-             eh->tls_mask |= tls_type;
-           }
+           ppc_elf_hash_entry (h)->tls_mask |= tls_type & 0xff;
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend, tls_type))
+                                       rel->r_addend, tls_type))
              return FALSE;
 
          ppc64_sec = ppc64_elf_section_data (sec);
@@ -4929,7 +5070,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
          BFD_ASSERT (rel->r_offset % 8 == 0);
          ppc64_sec->u.toc.symndx[rel->r_offset / 8] = r_symndx;
-         ppc64_sec->u.toc.add[rel->r_offset / 8] = sym_addend;
+         ppc64_sec->u.toc.add[rel->r_offset / 8] = rel->r_addend;
 
          /* Mark the second slot of a GD or LD entry.
             -1 to indicate GD and -2 to indicate LD.  */
@@ -4939,18 +5080,21 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            ppc64_sec->u.toc.symndx[rel->r_offset / 8 + 1] = -2;
          goto dodyn;
 
-       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_HIGH:
        case R_PPC64_TPREL16_HIGHA:
        case R_PPC64_TPREL16_HIGHER:
        case R_PPC64_TPREL16_HIGHERA:
        case R_PPC64_TPREL16_HIGHEST:
        case R_PPC64_TPREL16_HIGHESTA:
+         sec->has_tls_reloc = 1;
+         /* Fall through.  */
+       case R_PPC64_TPREL34:
+       case R_PPC64_TPREL16:
+       case R_PPC64_TPREL16_DS:
+       case R_PPC64_TPREL16_LO:
+       case R_PPC64_TPREL16_LO_DS:
          if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
@@ -4961,7 +5105,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              && ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC)
            {
              if (h != NULL)
-               ((struct ppc_link_hash_entry *) h)->is_func = 1;
+               ppc_elf_hash_entry (h)->is_func = 1;
            }
          /* Fall through.  */
 
@@ -5005,7 +5149,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
        case R_PPC64_TOC:
-         if (h != NULL && !bfd_link_pic (info))
+         if (h != NULL && bfd_link_executable (info))
            /* We may need a copy reloc.  */
            h->non_got_ref = 1;
 
@@ -5035,17 +5179,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
        dodyn:
-         if ((bfd_link_pic (info)
-              && (must_be_dyn_reloc (info, r_type)
-                  || (h != NULL
-                      && (!SYMBOLIC_BIND (info, h)
-                          || h->root.type == bfd_link_hash_defweak
-                          || !h->def_regular))))
-             || (ELIMINATE_COPY_RELOCS
-                 && !bfd_link_pic (info)
-                 && h != NULL
-                 && (h->root.type == bfd_link_hash_defweak
-                     || !h->def_regular))
+         if ((h != NULL
+              && (h->root.type == bfd_link_hash_defweak
+                  || !h->def_regular))
+             || (h != NULL
+                 && !bfd_link_executable (info)
+                 && !SYMBOLIC_BIND (info, h))
+             || (bfd_link_pic (info)
+                 && must_be_dyn_reloc (info, r_type))
              || (!bfd_link_pic (info)
                  && ifunc != NULL))
            {
@@ -5068,7 +5209,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  struct elf_dyn_relocs *p;
                  struct elf_dyn_relocs **head;
 
-                 head = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+                 head = &h->dyn_relocs;
                  p = *head;
                  if (p == NULL || p->sec != sec)
                    {
@@ -5097,7 +5238,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  void *vpp;
                  Elf_Internal_Sym *isym;
 
-                 isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                 isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                abfd, r_symndx);
                  if (isym == NULL)
                    return FALSE;
@@ -5449,7 +5590,7 @@ is_elfv2_localentry0 (struct elf_link_hash_entry *h)
          && h->type == STT_FUNC
          && h->root.type == bfd_link_hash_defined
          && (STO_PPC64_LOCAL_MASK & h->other) == 0
-         && !((struct ppc_link_hash_entry *) h)->non_zero_localentry
+         && !ppc_elf_hash_entry (h)->non_zero_localentry
          && is_ppc64_elf (h->root.u.def.section->owner)
          && abiversion (h->root.u.def.section->owner) >= 2);
 }
@@ -5498,6 +5639,29 @@ defined_func_desc (struct ppc_link_hash_entry *fh)
   return NULL;
 }
 
+/* Given H is a symbol that satisfies is_static_defined, return the
+   value in the output file.  */
+
+static bfd_vma
+defined_sym_val (struct elf_link_hash_entry *h)
+{
+  return (h->root.u.def.section->output_section->vma
+         + h->root.u.def.section->output_offset
+         + h->root.u.def.value);
+}
+
+/* Return true if H matches __tls_get_addr or one of its variants.  */
+
+static bfd_boolean
+is_tls_get_addr (struct elf_link_hash_entry *h,
+                struct ppc_link_hash_table *htab)
+{
+  return (h == (struct elf_link_hash_entry *) htab->tls_get_addr_fd
+         || h == (struct elf_link_hash_entry *) htab->tga_desc_fd
+         || h == (struct elf_link_hash_entry *) htab->tls_get_addr
+         || h == (struct elf_link_hash_entry *) htab->tga_desc);
+}
+
 static bfd_boolean func_desc_adjust (struct elf_link_hash_entry *, void *);
 
 /* Garbage collect sections, after first dealing with dot-symbols.  */
@@ -5531,8 +5695,8 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
       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, TRUE);
+      eh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym->name,
+                                                    FALSE, FALSE, TRUE));
       if (eh == NULL)
        continue;
       if (eh->elf.root.type != bfd_link_hash_defined
@@ -5564,7 +5728,7 @@ static bfd_boolean
 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 *eh = ppc_elf_hash_entry (h);
   struct ppc_link_hash_entry *fdh;
   struct bfd_elf_dynamic_list *d = info->dynamic_list;
 
@@ -5648,7 +5812,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
            {
            case bfd_link_hash_defined:
            case bfd_link_hash_defweak:
-             eh = (struct ppc_link_hash_entry *) h;
+             eh = ppc_elf_hash_entry (h);
              fdh = defined_func_desc (eh);
              if (fdh != NULL)
                {
@@ -5744,8 +5908,8 @@ sfpr_define (struct bfd_link_info *info,
 
       sym[len + 0] = i / 10 + '0';
       sym[len + 1] = i % 10 + '0';
-      h = (struct ppc_link_hash_entry *)
-       elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE);
+      h = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym,
+                                                   writing, TRUE, TRUE));
       if (stub_sec != NULL)
        {
          if (h != NULL
@@ -5758,9 +5922,7 @@ sfpr_define (struct bfd_link_info *info,
              s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE);
              if (s == NULL)
                return FALSE;
-             if (s->root.type == bfd_link_hash_new
-                 || (s->root.type = bfd_link_hash_defined
-                     && s->root.u.def.section == stub_sec))
+             if (s->root.type == bfd_link_hash_new)
                {
                  s->root.type = bfd_link_hash_defined;
                  s->root.u.def.section = stub_sec;
@@ -5974,6 +6136,84 @@ restvr_tail (bfd *abfd, bfd_byte *p, int r)
   return p + 4;
 }
 
+#define STDU_R1_0R1    0xf8210001
+#define ADDI_R1_R1     0x38210000
+
+/* Emit prologue of wrapper preserving regs around a call to
+   __tls_get_addr_opt.  */
+
+static bfd_byte *
+tls_get_addr_prologue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab)
+{
+  unsigned int i;
+
+  bfd_put_32 (obfd, MFLR_R0, p);
+  p += 4;
+  bfd_put_32 (obfd, STD_R0_0R1 + 16, p);
+  p += 4;
+
+  if (htab->opd_abi)
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd,
+                     STD_R0_0R1 | i << 21 | (-(13 - i) * 8 & 0xffff), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, STDU_R1_0R1 | (-128 & 0xffff), p);
+      p += 4;
+    }
+  else
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd,
+                     STD_R0_0R1 | i << 21 | (-(12 - i) * 8 & 0xffff), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, STDU_R1_0R1 | (-96 & 0xffff), p);
+      p += 4;
+    }
+  return p;
+}
+
+/* Emit epilogue of wrapper preserving regs around a call to
+   __tls_get_addr_opt.  */
+
+static bfd_byte *
+tls_get_addr_epilogue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab)
+{
+  unsigned int i;
+
+  if (htab->opd_abi)
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (128 - (13 - i) * 8), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, ADDI_R1_R1 | 128, p);
+      p += 4;
+    }
+  else
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (96 - (12 - i) * 8), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, ADDI_R1_R1 | 96, p);
+      p += 4;
+    }
+  bfd_put_32 (obfd, LD_R0_0R1 | 16, p);
+  p += 4;
+  bfd_put_32 (obfd, MTLR_R0, p);
+  p += 4;
+  bfd_put_32 (obfd, BLR, p);
+  p += 4;
+  return p;
+}
+
 /* Called via elf_link_hash_traverse to transfer dynamic linking
    information on function code symbol entries to their corresponding
    function descriptor symbol entries.  */
@@ -5987,7 +6227,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
   struct ppc_link_hash_entry *fdh;
   bfd_boolean force_local;
 
-  fh = (struct ppc_link_hash_entry *) h;
+  fh = ppc_elf_hash_entry (h);
   if (fh->elf.root.type == bfd_link_hash_indirect)
     return TRUE;
 
@@ -6107,13 +6347,13 @@ static const struct sfpr_def_parms save_res_funcs[] =
   };
 
 /* Called near the start of bfd_elf_size_dynamic_sections.  We use
-   this hook to a) provide some gcc support functions, and b) transfer
-   dynamic linking information gathered so far on function code symbol
-   entries, to their corresponding function descriptor symbol entries.  */
+   this hook to a) run the edit functions in this file, b) provide
+   some gcc support functions, and c) transfer dynamic linking
+   information gathered so far on function code symbol entries, to
+   their corresponding function descriptor symbol entries.  */
 
 static bfd_boolean
-ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
-                           struct bfd_link_info *info)
+ppc64_elf_edit (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
 
@@ -6121,6 +6361,9 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
   if (htab == NULL)
     return FALSE;
 
+  /* Call back into the linker, which then runs the edit functions.  */
+  htab->params->edit ();
+
   /* Provide any missing _save* and _rest* functions.  */
   if (htab->sfpr != NULL)
     {
@@ -6165,25 +6408,6 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
-/* Find dynamic relocs for H that apply to read-only sections.  */
-
-static asection *
-readonly_dynrelocs (struct elf_link_hash_entry *h)
-{
-  struct ppc_link_hash_entry *eh;
-  struct elf_dyn_relocs *p;
-
-  eh = (struct ppc_link_hash_entry *) h;
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
-    {
-      asection *s = p->sec->output_section;
-
-      if (s != NULL && (s->flags & SEC_READONLY) != 0)
-       return p->sec;
-    }
-  return NULL;
-}
-
 /* Return true if we have dynamic relocs against H or any of its weak
    aliases, that apply to read-only sections.  Cannot be used after
    size_dynamic_sections.  */
@@ -6191,14 +6415,12 @@ readonly_dynrelocs (struct elf_link_hash_entry *h)
 static bfd_boolean
 alias_readonly_dynrelocs (struct elf_link_hash_entry *h)
 {
-  struct ppc_link_hash_entry *eh;
-
-  eh = (struct ppc_link_hash_entry *) h;
+  struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h);
   do
     {
-      if (readonly_dynrelocs (&eh->elf))
+      if (_bfd_elf_readonly_dynrelocs (&eh->elf))
        return TRUE;
-      eh = (struct ppc_link_hash_entry *) eh->elf.u.alias;
+      eh = ppc_elf_hash_entry (eh->elf.u.alias);
     }
   while (eh != NULL && &eh->elf != h);
 
@@ -6212,7 +6434,7 @@ pc_dynrelocs (struct ppc_link_hash_entry *eh)
 {
   struct elf_dyn_relocs *p;
 
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+  for (p = eh->elf.dyn_relocs; p != NULL; p = p->next)
     if (p->pc_count != 0)
       return TRUE;
   return FALSE;
@@ -6260,7 +6482,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       || h->type == STT_GNU_IFUNC
       || h->needs_plt)
     {
-      bfd_boolean local = (((struct ppc_link_hash_entry *) h)->save_res
+      bfd_boolean local = (ppc_elf_hash_entry (h)->save_res
                           || SYMBOL_CALLS_LOCAL (info, h)
                           || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
       /* Discard dyn_relocs when non-pic if we've decided that a
@@ -6275,7 +6497,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       if (!bfd_link_pic (info)
          && h->type != STT_GNU_IFUNC
          && local)
-       ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
+       h->dyn_relocs = NULL;
 
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
@@ -6287,7 +6509,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          || (h->type != STT_GNU_IFUNC
              && local
              && (htab->can_convert_all_inline_plt
-                 || (((struct ppc_link_hash_entry *) h)->tls_mask
+                 || (ppc_elf_hash_entry (h)->tls_mask
                      & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)))
        {
          h->plt.plist = NULL;
@@ -6305,7 +6527,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
             extra work in ld.so when resolving these symbols.  */
          if (global_entry_stub (h))
            {
-             if (!readonly_dynrelocs (h))
+             if (!_bfd_elf_readonly_dynrelocs (h))
                {
                  h->pointer_equality_needed = 0;
                  /* If we haven't seen a branch reloc and the symbol
@@ -6316,14 +6538,14 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
              else if (!bfd_link_pic (info))
                /* We are going to be defining the function symbol on the
                   plt stub, so no dyn_relocs needed when non-pic.  */
-               ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
+               h->dyn_relocs = NULL;
            }
 
          /* ELFv2 function symbols can't have copy relocs.  */
          return TRUE;
        }
       else if (!h->needs_plt
-              && !readonly_dynrelocs (h))
+              && !_bfd_elf_readonly_dynrelocs (h))
        {
          /* If we haven't seen a branch reloc and the symbol isn't an
             ifunc then we don't need a plt entry.  */
@@ -6346,7 +6568,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       h->root.u.def.value = def->root.u.def.value;
       if (def->root.u.def.section == htab->elf.sdynbss
          || def->root.u.def.section == htab->elf.sdynrelro)
-       ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
+       h->dyn_relocs = NULL;
       return TRUE;
     }
 
@@ -6354,7 +6576,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      only references to the symbol are via the global offset table.
      For such cases we need not do anything here; the relocations will
      be handled correctly by relocate_section.  */
-  if (bfd_link_pic (info))
+  if (!bfd_link_executable (info))
     return TRUE;
 
   /* If there are no references to this symbol that do not use the
@@ -6370,7 +6592,9 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
       /* If we don't find any dynamic relocs in read-only sections, then
         we'll be keeping the dynamic relocs and avoiding the copy reloc.  */
-      || (ELIMINATE_COPY_RELOCS && !alias_readonly_dynrelocs (h))
+      || (ELIMINATE_COPY_RELOCS
+         && !h->needs_copy
+         && !alias_readonly_dynrelocs (h))
 
       /* Protected variables do not work with .dynbss.  The copy in
         .dynbss won't be used by the shared library with the protected
@@ -6379,13 +6603,23 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       || h->protected_def)
     return TRUE;
 
-  if (h->plt.plist != NULL)
+  if (h->type == STT_FUNC
+      || h->type == STT_GNU_IFUNC)
     {
-      /* We should never get here, but unfortunately there are versions
-        of gcc out there that improperly (for this ABI) put initialized
-        function pointers, vtable refs and suchlike in read-only
-        sections.  Allow them to proceed, but warn that this might
-        break at runtime.  */
+      /* .dynbss copies of function symbols only work if we have
+        ELFv1 dot-symbols.  ELFv1 compilers since 2004 default to not
+        use dot-symbols and set the function symbol size to the text
+        size of the function rather than the size of the descriptor.
+        That's wrong for copying a descriptor.  */
+      if (ppc_elf_hash_entry (h)->oh == NULL
+         || !(h->size == 24 || h->size == 16))
+       return TRUE;
+
+      /* We should never get here, but unfortunately there are old
+        versions of gcc (circa gcc-3.2) that improperly for the
+        ELFv1 ABI put initialized function pointers, vtable refs and
+        suchlike in read-only sections.  Allow them to proceed, but
+        warn that this might break at runtime.  */
       info->callbacks->einfo
        (_("%P: copy reloc against `%pT' requires lazy plt linking; "
           "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"),
@@ -6424,7 +6658,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
     }
 
   /* We no longer want dyn_relocs.  */
-  ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
+  h->dyn_relocs = NULL;
   return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
@@ -6441,7 +6675,7 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
   if (ppc_hash_table (info) == NULL)
     return;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   if (eh->is_func_descriptor)
     {
       struct ppc_link_hash_entry *fh = eh->oh;
@@ -6464,8 +6698,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
          p = eh->elf.root.root.string - 1;
          save = *p;
          *(char *) p = '.';
-         fh = (struct ppc_link_hash_entry *)
-           elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE);
+         fh = ppc_elf_hash_entry (elf_link_hash_lookup (htab, p, FALSE,
+                                                        FALSE, FALSE));
          *(char *) p = save;
 
          /* Unfortunately, if it so happens that the string we were
@@ -6478,8 +6712,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
              while (q >= eh->elf.root.root.string && *q == *p)
                --q, --p;
              if (q < eh->elf.root.root.string && *p == '.')
-               fh = (struct ppc_link_hash_entry *)
-                 elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE);
+               fh = ppc_elf_hash_entry (elf_link_hash_lookup (htab, p, FALSE,
+                                                              FALSE, FALSE));
            }
          if (fh != NULL)
            {
@@ -6527,12 +6761,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
        }
 
       if (tls_maskp != NULL)
-       {
-         struct ppc_link_hash_entry *eh;
-
-         eh = (struct ppc_link_hash_entry *) h;
-         *tls_maskp = &eh->tls_mask;
-       }
+       *tls_maskp = &ppc_elf_hash_entry (h)->tls_mask;
     }
   else
     {
@@ -6703,7 +6932,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   if (eh->adjust_done)
     return TRUE;
 
@@ -6758,6 +6987,16 @@ dec_dynrel_count (bfd_vma r_info,
     default:
       return TRUE;
 
+    case R_PPC64_TOC16:
+    case R_PPC64_TOC16_DS:
+    case R_PPC64_TOC16_LO:
+    case R_PPC64_TOC16_HI:
+    case R_PPC64_TOC16_HA:
+    case R_PPC64_TOC16_LO_DS:
+      if (h == NULL)
+       return TRUE;
+      break;
+
     case R_PPC64_TPREL16:
     case R_PPC64_TPREL16_LO:
     case R_PPC64_TPREL16_HI:
@@ -6771,6 +7010,7 @@ dec_dynrel_count (bfd_vma r_info,
     case R_PPC64_TPREL16_HIGHEST:
     case R_PPC64_TPREL16_HIGHESTA:
     case R_PPC64_TPREL64:
+    case R_PPC64_TPREL34:
     case R_PPC64_DTPMOD64:
     case R_PPC64_DTPREL64:
     case R_PPC64_ADDR64:
@@ -6820,17 +7060,18 @@ dec_dynrel_count (bfd_vma r_info,
        return FALSE;
     }
 
-  if ((bfd_link_pic (info)
-       && (must_be_dyn_reloc (info, r_type)
-          || (h != NULL
-              && (!SYMBOLIC_BIND (info, h)
-                  || h->root.type == bfd_link_hash_defweak
-                  || !h->def_regular))))
-      || (ELIMINATE_COPY_RELOCS
-         && !bfd_link_pic (info)
-         && h != NULL
-         && (h->root.type == bfd_link_hash_defweak
-             || !h->def_regular)))
+  if ((h != NULL
+       && (h->root.type == bfd_link_hash_defweak
+          || !h->def_regular))
+      || (h != NULL
+         && !bfd_link_executable (info)
+         && !SYMBOLIC_BIND (info, h))
+      || (bfd_link_pic (info)
+         && must_be_dyn_reloc (info, r_type))
+      || (!bfd_link_pic (info)
+         && (h != NULL
+             ? h->type == STT_GNU_IFUNC
+             : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
     ;
   else
     return TRUE;
@@ -6839,7 +7080,7 @@ dec_dynrel_count (bfd_vma r_info,
     {
       struct elf_dyn_relocs *p;
       struct elf_dyn_relocs **pp;
-      pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+      pp = &h->dyn_relocs;
 
       /* elf_gc_sweep may have already removed all dyn relocs associated
         with local syms for a given section.  Also, symbol flags are
@@ -7092,11 +7333,9 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
              bfd_byte *loc;
              if (!bfd_malloc_and_get_section (ibfd, sec, &loc))
                {
-                 if (loc != NULL)
-                   free (loc);
+                 free (loc);
                error_ret:
-                 if (local_syms != NULL
-                     && symtab_hdr->contents != (unsigned char *) local_syms)
+                 if (symtab_hdr->contents != (unsigned char *) local_syms)
                    free (local_syms);
                  if (elf_section_data (sec)->relocs != relstart)
                    free (relstart);
@@ -7156,7 +7395,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
              if (h != NULL
                  && h->root.root.string[0] == '.')
                {
-                 fdh = ((struct ppc_link_hash_entry *) h)->oh;
+                 fdh = ppc_elf_hash_entry (h)->oh;
                  if (fdh != NULL)
                    {
                      fdh = ppc_follow_link (fdh);
@@ -7402,7 +7641,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
              return FALSE;
 
            relend = relstart + sec->reloc_count;
-           for (rel = relstart; rel < relend; )
+           for (rel = relstart; rel < relend; rel++)
              {
                enum elf_ppc64_reloc_type r_type;
                unsigned long r_symndx;
@@ -7422,8 +7661,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
                  {
                    if (elf_section_data (sec)->relocs != relstart)
                      free (relstart);
-                   if (local_syms != NULL
-                       && symtab_hdr->contents != (bfd_byte *) local_syms)
+                   if (symtab_hdr->contents != (bfd_byte *) local_syms)
                      free (local_syms);
                    return FALSE;
                  }
@@ -7445,7 +7683,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
                        && !(r_type == R_PPC64_PLTCALL_NOTOC
                             && (((h ? h->other : sym->st_other)
                                  & STO_PPC64_LOCAL_MASK)
-                                != 1 << STO_PPC64_LOCAL_BIT)))
+                                > 1 << STO_PPC64_LOCAL_BIT)))
                      *tls_maskp &= ~PLT_KEEP;
                  }
              }
@@ -7466,16 +7704,19 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
   return TRUE;
 }
 
-/* Set htab->tls_get_addr and call the generic ELF tls_setup function.  */
+/* Set htab->tls_get_addr and various other info specific to TLS.
+   This needs to run before dynamic symbols are processed in
+   bfd_elf_size_dynamic_sections.  */
 
-asection *
+bfd_boolean
 ppc64_elf_tls_setup (struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
+  struct elf_link_hash_entry *tga, *tga_fd, *desc, *desc_fd;
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
-    return NULL;
+    return FALSE;
 
   if (abiversion (info->output_bfd) == 1)
     htab->opd_abi = 1;
@@ -7501,6 +7742,19 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
      --plt-localentry can cause trouble.  */
   if (htab->params->plt_localentry0 < 0)
     htab->params->plt_localentry0 = 0;
+  if (htab->params->plt_localentry0 && htab->has_power10_relocs)
+    {
+      /* The issue is that __glink_PLTresolve saves r2, which is done
+        because glibc ld.so _dl_runtime_resolve restores r2 to support
+        a glibc plt call optimisation where global entry code is
+        skipped on calls that resolve to the same binary.  The
+        __glink_PLTresolve save of r2 is incompatible with code
+        making tail calls, because the tail call might go via the
+        resolver and thus overwrite the proper saved r2.  */
+      _bfd_error_handler (_("warning: --plt-localentry is incompatible with "
+                           "power10 pc-relative code"));
+      htab->params->plt_localentry0 = 0;
+    }
   if (htab->params->plt_localentry0
       && elf_link_hash_lookup (&htab->elf, "GLIBC_2.26",
                               FALSE, FALSE, FALSE) == NULL)
@@ -7508,18 +7762,29 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
       (_("warning: --plt-localentry is especially dangerous without "
         "ld.so support to detect ABI violations"));
 
-  htab->tls_get_addr = ((struct ppc_link_hash_entry *)
-                       elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
-                                             FALSE, FALSE, TRUE));
+  tga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
+                             FALSE, FALSE, TRUE);
+  htab->tls_get_addr = ppc_elf_hash_entry (tga);
+
   /* 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 (tga != NULL)
+    func_desc_adjust (tga, info);
+  tga_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
+                                FALSE, FALSE, TRUE);
+  htab->tls_get_addr_fd = ppc_elf_hash_entry (tga_fd);
+
+  desc = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_desc",
+                              FALSE, FALSE, TRUE);
+  htab->tga_desc = ppc_elf_hash_entry (desc);
+  if (desc != NULL)
+    func_desc_adjust (desc, info);
+  desc_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_desc",
+                                 FALSE, FALSE, TRUE);
+  htab->tga_desc_fd = ppc_elf_hash_entry (desc_fd);
+
   if (htab->params->tls_get_addr_opt)
     {
-      struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd;
+      struct elf_link_hash_entry *opt, *opt_fd;
 
       opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt",
                                  FALSE, FALSE, TRUE);
@@ -7535,24 +7800,49 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
             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)
-                  || UNDEFWEAK_NO_DYNAMIC_RELOC (info, tga_fd)))
+         if (!(htab->elf.dynamic_sections_created
+               && tga_fd != NULL
+               && (tga_fd->type == STT_FUNC
+                   || tga_fd->needs_plt)
+               && !(SYMBOL_CALLS_LOCAL (info, tga_fd)
+                    || UNDEFWEAK_NO_DYNAMIC_RELOC (info, tga_fd))))
+           tga_fd = NULL;
+         if (!(htab->elf.dynamic_sections_created
+               && desc_fd != NULL
+               && (desc_fd->type == STT_FUNC
+                   || desc_fd->needs_plt)
+               && !(SYMBOL_CALLS_LOCAL (info, desc_fd)
+                    || UNDEFWEAK_NO_DYNAMIC_RELOC (info, desc_fd))))
+           desc_fd = NULL;
+
+         if (tga_fd != NULL || desc_fd != NULL)
            {
-             struct plt_entry *ent;
+             struct plt_entry *ent = NULL;
 
-             for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next)
-               if (ent->plt.refcount > 0)
-                 break;
+             if (tga_fd != NULL)
+               for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next)
+                 if (ent->plt.refcount > 0)
+                   break;
+             if (ent == NULL && desc_fd != NULL)
+               for (ent = desc_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 (tga_fd != NULL)
+                   {
+                     tga_fd->root.type = bfd_link_hash_indirect;
+                     tga_fd->root.u.i.link = &opt_fd->root;
+                     tga_fd->root.u.i.warning = NULL;
+                     ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd);
+                   }
+                 if (desc_fd != NULL)
+                   {
+                     desc_fd->root.type = bfd_link_hash_indirect;
+                     desc_fd->root.u.i.link = &opt_fd->root;
+                     desc_fd->root.u.i.warning = NULL;
+                     ppc64_elf_copy_indirect_symbol (info, opt_fd, desc_fd);
+                   }
                  opt_fd->mark = 1;
                  if (opt_fd->dynindx != -1)
                    {
@@ -7561,27 +7851,52 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
                      _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
                                              opt_fd->dynstr_index);
                      if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
-                       return NULL;
+                       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)
+                 if (tga_fd != NULL)
                    {
-                     tga->root.type = bfd_link_hash_indirect;
-                     tga->root.u.i.link = &opt->root;
-                     ppc64_elf_copy_indirect_symbol (info, opt, tga);
-                     opt->mark = 1;
-                     _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 = ppc_elf_hash_entry (opt_fd);
+                     tga = (struct elf_link_hash_entry *) htab->tls_get_addr;
+                     if (opt != NULL && tga != NULL)
+                       {
+                         tga->root.type = bfd_link_hash_indirect;
+                         tga->root.u.i.link = &opt->root;
+                         tga->root.u.i.warning = NULL;
+                         ppc64_elf_copy_indirect_symbol (info, opt, tga);
+                         opt->mark = 1;
+                         _bfd_elf_link_hash_hide_symbol (info, opt,
+                                                         tga->forced_local);
+                         htab->tls_get_addr = ppc_elf_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;
+                       }
                    }
-                 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)
+                 if (desc_fd != NULL)
                    {
-                     htab->tls_get_addr->oh = htab->tls_get_addr_fd;
-                     htab->tls_get_addr->is_func = 1;
+                     htab->tga_desc_fd = ppc_elf_hash_entry (opt_fd);
+                     if (opt != NULL && desc != NULL)
+                       {
+                         desc->root.type = bfd_link_hash_indirect;
+                         desc->root.u.i.link = &opt->root;
+                         desc->root.u.i.warning = NULL;
+                         ppc64_elf_copy_indirect_symbol (info, opt, desc);
+                         opt->mark = 1;
+                         _bfd_elf_link_hash_hide_symbol (info, opt,
+                                                         desc->forced_local);
+                         htab->tga_desc = ppc_elf_hash_entry (opt);
+                       }
+                     htab->tga_desc_fd->oh = htab->tga_desc;
+                     htab->tga_desc_fd->is_func_descriptor = 1;
+                     if (htab->tga_desc != NULL)
+                       {
+                         htab->tga_desc->oh = htab->tga_desc_fd;
+                         htab->tga_desc->is_func = 1;
+                       }
                    }
                }
            }
@@ -7589,17 +7904,25 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
       else if (htab->params->tls_get_addr_opt < 0)
        htab->params->tls_get_addr_opt = 0;
     }
-  return _bfd_elf_tls_setup (info->output_bfd, info);
+
+  if (htab->tga_desc_fd != NULL
+      && htab->params->tls_get_addr_opt
+      && htab->params->no_tls_get_addr_regsave == -1)
+    htab->params->no_tls_get_addr_regsave = 0;
+
+  return TRUE;
 }
 
 /* Return TRUE iff REL is a branch reloc with a global symbol matching
-   HASH1 or HASH2.  */
+   any of HASH1, HASH2, HASH3, or HASH4.  */
 
 static bfd_boolean
 branch_reloc_hash_match (const bfd *ibfd,
                         const Elf_Internal_Rela *rel,
                         const struct ppc_link_hash_entry *hash1,
-                        const struct ppc_link_hash_entry *hash2)
+                        const struct ppc_link_hash_entry *hash2,
+                        const struct ppc_link_hash_entry *hash3,
+                        const struct ppc_link_hash_entry *hash4)
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
   enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
@@ -7612,7 +7935,10 @@ branch_reloc_hash_match (const bfd *ibfd,
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       h = elf_follow_link (h);
-      if (h == &hash1->elf || h == &hash2->elf)
+      if (h == (struct elf_link_hash_entry *) hash1
+         || h == (struct elf_link_hash_entry *) hash2
+         || h == (struct elf_link_hash_entry *) hash3
+         || h == (struct elf_link_hash_entry *) hash4)
        return TRUE;
     }
   return FALSE;
@@ -7641,6 +7967,8 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
   if (htab == NULL)
     return FALSE;
 
+  htab->do_tls_opt = 1;
+
   /* Make two passes over the relocs.  On the first pass, mark toc
      entries involved with tls relocs, and check that tls relocs
      involved in setting up a tls_get_addr call are indeed followed by
@@ -7679,7 +8007,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  Elf_Internal_Sym *sym;
                  asection *sym_sec;
                  unsigned char *tls_mask;
-                 unsigned char tls_set, tls_clear, tls_type = 0;
+                 unsigned int tls_set, tls_clear, tls_type = 0;
                  bfd_vma value;
                  bfd_boolean ok_tprel, is_local;
                  long toc_ref_index = 0;
@@ -7693,11 +8021,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    err_free_rel:
                      if (elf_section_data (sec)->relocs != relstart)
                        free (relstart);
-                     if (toc_ref != NULL)
-                       free (toc_ref);
-                     if (locsyms != NULL
-                         && (elf_symtab_hdr (ibfd).contents
-                             != (unsigned char *) locsyms))
+                     free (toc_ref);
+                     if (elf_symtab_hdr (ibfd).contents
+                         != (unsigned char *) locsyms)
                        free (locsyms);
                      return ret;
                    }
@@ -7721,11 +8047,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    value = sym->st_value;
 
                  ok_tprel = FALSE;
-                 is_local = FALSE;
-                 if (h == NULL
-                     || !h->def_dynamic)
+                 is_local = SYMBOL_REFERENCES_LOCAL (info, h);
+                 if (is_local)
                    {
-                     is_local = TRUE;
                      if (h != NULL
                          && h->root.type == bfd_link_hash_undefweak)
                        ok_tprel = TRUE;
@@ -7734,9 +8058,14 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        {
                          value += sym_sec->output_offset;
                          value += sym_sec->output_section->vma;
-                         value -= htab->elf.tls_sec->vma;
-                         ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31)
-                                     < (bfd_vma) 1 << 32);
+                         value -= htab->elf.tls_sec->vma + TP_OFFSET;
+                         /* Note that even though the prefix insns
+                            allow a 1<<33 offset we use the same test
+                            as for addis;addi.  There may be a mix of
+                            pcrel and non-pcrel code and the decision
+                            to optimise is per symbol, not per TLS
+                            sequence.  */
+                         ok_tprel = value + 0x80008000ULL < 1ULL << 32;
                        }
                    }
 
@@ -7748,10 +8077,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                     setup insn.  If we don't find matching arg setup
                     relocs, don't do any tls optimization.  */
                  if (pass == 0
-                     && sec->has_tls_get_addr_call
+                     && sec->nomark_tls_get_addr
                      && h != NULL
-                     && (h == &htab->tls_get_addr->elf
-                         || h == &htab->tls_get_addr_fd->elf)
+                     && is_tls_get_addr (h, htab)
                      && !found_tls_get_addr_arg
                      && is_branch_reloc (r_type))
                    {
@@ -7767,6 +8095,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    {
                    case R_PPC64_GOT_TLSLD16:
                    case R_PPC64_GOT_TLSLD16_LO:
+                   case R_PPC64_GOT_TLSLD_PCREL34:
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
                      /* Fall through.  */
@@ -7787,6 +8116,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
 
                    case R_PPC64_GOT_TLSGD16:
                    case R_PPC64_GOT_TLSGD16_LO:
+                   case R_PPC64_GOT_TLSGD_PCREL34:
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
                      /* Fall through. */
@@ -7798,11 +8128,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        tls_set = 0;
                      else
                        /* GD -> IE */
-                       tls_set = TLS_TLS | TLS_TPRELGD;
+                       tls_set = TLS_TLS | TLS_GDIE;
                      tls_clear = TLS_GD;
                      tls_type = TLS_TLS | TLS_GD;
                      break;
 
+                   case R_PPC64_GOT_TPREL_PCREL34:
                    case R_PPC64_GOT_TPREL16_DS:
                    case R_PPC64_GOT_TPREL16_LO_DS:
                    case R_PPC64_GOT_TPREL16_HI:
@@ -7817,10 +8148,13 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                      continue;
 
-                   case R_PPC64_TLSGD:
                    case R_PPC64_TLSLD:
-                     if (rel + 1 < relend
-                         && is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+                     if (!is_local)
+                       continue;
+                     /* Fall through.  */
+                   case R_PPC64_TLSGD:
+                     if (rel + 1 < relend
+                         && is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
                        {
                          if (pass != 0
                              && (ELF64_R_TYPE (rel[1].r_info)
@@ -7925,7 +8259,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                            tls_set = TLS_EXPLICIT | TLS_GD;
                          else
                            /* GD -> IE */
-                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_TPRELGD;
+                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_GDIE;
                          tls_clear = TLS_GD;
                        }
                      else
@@ -7939,6 +8273,42 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                      break;
 
+                   case R_PPC64_TPREL16_HA:
+                     if (pass == 0)
+                       {
+                         unsigned char buf[4];
+                         unsigned int insn;
+                         bfd_vma off = rel->r_offset & ~3;
+                         if (!bfd_get_section_contents (ibfd, sec, buf,
+                                                        off, 4))
+                           goto err_free_rel;
+                         insn = bfd_get_32 (ibfd, buf);
+                         /* addis rt,13,imm */
+                         if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                             != ((15u << 26) | (13 << 16)))
+                           {
+                             /* xgettext:c-format */
+                             info->callbacks->minfo
+                               (_("%H: warning: %s unexpected insn %#x.\n"),
+                                ibfd, sec, off, "R_PPC64_TPREL16_HA", insn);
+                             htab->do_tls_opt = 0;
+                           }
+                       }
+                     continue;
+
+                   case R_PPC64_TPREL16_HI:
+                   case R_PPC64_TPREL16_HIGH:
+                   case R_PPC64_TPREL16_HIGHA:
+                   case R_PPC64_TPREL16_HIGHER:
+                   case R_PPC64_TPREL16_HIGHERA:
+                   case R_PPC64_TPREL16_HIGHEST:
+                   case R_PPC64_TPREL16_HIGHESTA:
+                     /* These can all be used in sequences along with
+                        TPREL16_LO or TPREL16_LO_DS in ways we aren't
+                        able to verify easily.  */
+                     htab->do_tls_opt = 0;
+                     continue;
+
                    default:
                      continue;
                    }
@@ -7946,13 +8316,15 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  if (pass == 0)
                    {
                      if (!expecting_tls_get_addr
-                         || !sec->has_tls_get_addr_call)
+                         || !sec->nomark_tls_get_addr)
                        continue;
 
                      if (rel + 1 < relend
                          && branch_reloc_hash_match (ibfd, rel + 1,
+                                                     htab->tls_get_addr_fd,
+                                                     htab->tga_desc_fd,
                                                      htab->tls_get_addr,
-                                                     htab->tls_get_addr_fd))
+                                                     htab->tga_desc))
                        {
                          if (expecting_tls_get_addr == 2)
                            {
@@ -7998,24 +8370,38 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                     Disable optimization in this case.  */
                  if ((tls_clear & (TLS_GD | TLS_LD)) != 0
                      && (tls_set & TLS_EXPLICIT) == 0
-                     && !sec->has_tls_get_addr_call
+                     && !sec->nomark_tls_get_addr
                      && ((*tls_mask & (TLS_TLS | TLS_MARK))
                          != (TLS_TLS | TLS_MARK)))
                    continue;
 
-                 if (expecting_tls_get_addr)
+                 if (expecting_tls_get_addr == 1 + !sec->nomark_tls_get_addr)
                    {
                      struct plt_entry *ent = NULL;
 
-                     if (htab->tls_get_addr != NULL)
+                     if (htab->tls_get_addr_fd != NULL)
+                       for (ent = htab->tls_get_addr_fd->elf.plt.plist;
+                            ent != NULL;
+                            ent = ent->next)
+                         if (ent->addend == 0)
+                           break;
+
+                     if (ent == NULL && htab->tga_desc_fd != NULL)
+                       for (ent = htab->tga_desc_fd->elf.plt.plist;
+                            ent != NULL;
+                            ent = ent->next)
+                         if (ent->addend == 0)
+                           break;
+
+                     if (ent == NULL && htab->tls_get_addr != NULL)
                        for (ent = htab->tls_get_addr->elf.plt.plist;
                             ent != NULL;
                             ent = ent->next)
                          if (ent->addend == 0)
                            break;
 
-                     if (ent == NULL && htab->tls_get_addr_fd != NULL)
-                       for (ent = htab->tls_get_addr_fd->elf.plt.plist;
+                     if (ent == NULL && htab->tga_desc != NULL)
+                       for (ent = htab->tga_desc->elf.plt.plist;
                             ent != NULL;
                             ent = ent->next)
                          if (ent->addend == 0)
@@ -8070,7 +8456,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                    }
 
-                 *tls_mask |= tls_set;
+                 *tls_mask |= tls_set & 0xff;
                  *tls_mask &= ~tls_clear;
                }
 
@@ -8088,9 +8474,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
          }
       }
 
-  if (toc_ref != NULL)
-    free (toc_ref);
-  htab->do_tls_opt = 1;
+  free (toc_ref);
   return TRUE;
 }
 
@@ -8120,7 +8504,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   if (eh->adjust_done)
     return TRUE;
 
@@ -8156,61 +8540,81 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 static bfd_boolean
 ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type)
 {
-  return ((insn & (0x3f << 26)) == 12u << 26 /* addic */
-         || (insn & (0x3f << 26)) == 14u << 26 /* addi */
-         || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
-         || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
-         || (insn & (0x3f << 26)) == 36u << 26 /* stw */
-         || (insn & (0x3f << 26)) == 38u << 26 /* stb */
-         || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
-         || (insn & (0x3f << 26)) == 42u << 26 /* lha */
-         || (insn & (0x3f << 26)) == 44u << 26 /* sth */
-         || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
-         || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
-         || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
-         || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
-         || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
-         || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
-         || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */
-         || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+  return ((insn & (0x3fu << 26)) == 12u << 26 /* addic */
+         || (insn & (0x3fu << 26)) == 14u << 26 /* addi */
+         || (insn & (0x3fu << 26)) == 32u << 26 /* lwz */
+         || (insn & (0x3fu << 26)) == 34u << 26 /* lbz */
+         || (insn & (0x3fu << 26)) == 36u << 26 /* stw */
+         || (insn & (0x3fu << 26)) == 38u << 26 /* stb */
+         || (insn & (0x3fu << 26)) == 40u << 26 /* lhz */
+         || (insn & (0x3fu << 26)) == 42u << 26 /* lha */
+         || (insn & (0x3fu << 26)) == 44u << 26 /* sth */
+         || (insn & (0x3fu << 26)) == 46u << 26 /* lmw */
+         || (insn & (0x3fu << 26)) == 47u << 26 /* stmw */
+         || (insn & (0x3fu << 26)) == 48u << 26 /* lfs */
+         || (insn & (0x3fu << 26)) == 50u << 26 /* lfd */
+         || (insn & (0x3fu << 26)) == 52u << 26 /* stfs */
+         || (insn & (0x3fu << 26)) == 54u << 26 /* stfd */
+         || (insn & (0x3fu << 26)) == 56u << 26 /* lq,lfq */
+         || ((insn & (0x3fu << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
              /* Exclude lfqu by testing reloc.  If relocs are ever
                 defined for the reduced D field in psq_lu then those
                 will need testing too.  */
              && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
-         || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */
+         || ((insn & (0x3fu << 26)) == 58u << 26 /* ld,lwa */
              && (insn & 1) == 0)
-         || (insn & (0x3f << 26)) == 60u << 26 /* stfq */
-         || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+         || (insn & (0x3fu << 26)) == 60u << 26 /* stfq */
+         || ((insn & (0x3fu << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
              /* Exclude stfqu.  psq_stu as above for psq_lu.  */
              && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
-         || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */
+         || ((insn & (0x3fu << 26)) == 62u << 26 /* std,stq */
              && (insn & 1) == 0));
 }
 
 /* PCREL_OPT in one instance flags to the linker that a pair of insns:
      pld ra,symbol@got@pcrel
-     load/store rt,0(ra)
+     load/store rt,off(ra)
    or
      pla ra,symbol@pcrel
-     load/store rt,0(ra)
+     load/store rt,off(ra)
    may be translated to
-     pload/pstore rt,symbol@pcrel
+     pload/pstore rt,symbol+off@pcrel
      nop.
    This function returns true if the optimization is possible, placing
-   the prefix insn in *PINSN1 and a NOP in *PINSN2.
+   the prefix insn in *PINSN1, a NOP in *PINSN2 and the offset in *POFF.
 
    On entry to this function, the linker has already determined that
    the pld can be replaced with pla: *PINSN1 is that pla insn,
    while *PINSN2 is the second instruction.  */
 
 static bfd_boolean
-xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2)
+xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2, bfd_signed_vma *poff)
 {
-  uint32_t insn2 = *pinsn2 >> 32;
-  uint64_t i1new;
+  uint64_t insn1 = *pinsn1;
+  uint64_t insn2 = *pinsn2;
+  bfd_signed_vma off;
+
+  if ((insn2 & (63ULL << 58)) == 1ULL << 58)
+    {
+      /* Check that regs match.  */
+      if (((insn2 >> 16) & 31) != ((insn1 >> 21) & 31))
+       return FALSE;
+
+      /* P8LS or PMLS form, non-pcrel.  */
+      if ((insn2 & (-1ULL << 50) & ~(1ULL << 56)) != (1ULL << 58))
+       return FALSE;
+
+      *pinsn1 = (insn2 & ~(31 << 16) & ~0x3ffff0000ffffULL) | (1ULL << 52);
+      *pinsn2 = PNOP;
+      off = ((insn2 >> 16) & 0x3ffff0000ULL) | (insn2 & 0xffff);
+      *poff = (off ^ 0x200000000ULL) - 0x200000000ULL;
+      return TRUE;
+    }
+
+  insn2 >>= 32;
 
   /* Check that regs match.  */
-  if (((insn2 >> 16) & 31) != ((*pinsn1 >> 21) & 31))
+  if (((insn2 >> 16) & 31) != ((insn1 >> 21) & 31))
     return FALSE;
 
   switch ((insn2 >> 26) & 63)
@@ -8230,27 +8634,28 @@ xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2)
     case 52: /* stfs */
     case 54: /* stfd */
       /* These are the PMLS cases, where we just need to tack a prefix
-        on the insn.  Check that the D field is zero.  */
-      if ((insn2 & 0xffff) != 0)
-       return FALSE;
-      i1new = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+        on the insn.  */
+      insn1 = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
               | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      off = insn2 & 0xffff;
       break;
 
     case 58: /* lwa, ld */
-      if ((insn2 & 0xfffd) != 0)
+      if ((insn2 & 1) != 0)
        return FALSE;
-      i1new = ((1ULL << 58) | (1ULL << 52)
+      insn1 = ((1ULL << 58) | (1ULL << 52)
               | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26)
               | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
       break;
 
     case 57: /* lxsd, lxssp */
-      if ((insn2 & 0xfffc) != 0 || (insn2 & 3) < 2)
+      if ((insn2 & 3) < 2)
        return FALSE;
-      i1new = ((1ULL << 58) | (1ULL << 52)
+      insn1 = ((1ULL << 58) | (1ULL << 52)
               | ((40ULL | (insn2 & 3)) << 26)
               | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
       break;
 
     case 61: /* stxsd, stxssp, lxv, stxv  */
@@ -8258,40 +8663,48 @@ xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2)
        return FALSE;
       else if ((insn2 & 3) >= 2)
        {
-         if ((insn2 & 0xfffc) != 0)
-           return FALSE;
-         i1new = ((1ULL << 58) | (1ULL << 52)
+         insn1 = ((1ULL << 58) | (1ULL << 52)
                   | ((44ULL | (insn2 & 3)) << 26)
                   | (insn2 & (31ULL << 21)));
+         off = insn2 & 0xfffc;
        }
       else
        {
-         if ((insn2 & 0xfff0) != 0)
-           return FALSE;
-         i1new = ((1ULL << 58) | (1ULL << 52)
+         insn1 = ((1ULL << 58) | (1ULL << 52)
                   | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26)
                   | (insn2 & (31ULL << 21)));
+         off = insn2 & 0xfff0;
        }
       break;
 
     case 56: /* lq */
-      if ((insn2 & 0xffff) != 0)
-       return FALSE;
-      i1new = ((1ULL << 58) | (1ULL << 52)
+      insn1 = ((1ULL << 58) | (1ULL << 52)
               | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      off = insn2 & 0xffff;
+      break;
+
+    case 6: /* lxvp, stxvp */
+      if ((insn2 & 0xe) != 0)
+       return FALSE;
+      insn1 = ((1ULL << 58) | (1ULL << 52)
+              | ((insn2 & 1) == 0 ? 58ULL << 26 : 62ULL << 26)
+              | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfff0;
       break;
 
     case 62: /* std, stq */
-      if ((insn2 & 0xfffd) != 0)
+      if ((insn2 & 1) != 0)
        return FALSE;
-      i1new = ((1ULL << 58) | (1ULL << 52)
+      insn1 = ((1ULL << 58) | (1ULL << 52)
               | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26)
               | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
       break;
     }
 
-  *pinsn1 = i1new;
+  *pinsn1 = insn1;
   *pinsn2 = (uint64_t) NOP << 32;
+  *poff = (off ^ 0x8000) - 0x8000;
   return TRUE;
 }
 
@@ -8500,18 +8913,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       if (used == NULL)
        {
        error_ret:
-         if (local_syms != NULL
-             && symtab_hdr->contents != (unsigned char *) local_syms)
+         if (symtab_hdr->contents != (unsigned char *) local_syms)
            free (local_syms);
          if (sec != NULL
-             && relstart != NULL
              && elf_section_data (sec)->relocs != relstart)
            free (relstart);
-         if (toc_relocs != NULL
-             && elf_section_data (toc)->relocs != toc_relocs)
+         if (elf_section_data (toc)->relocs != toc_relocs)
            free (toc_relocs);
-         if (skip != NULL)
-           free (skip);
+         free (skip);
          return FALSE;
        }
 
@@ -8554,65 +8963,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  struct elf_link_hash_entry *h;
                  Elf_Internal_Sym *sym;
                  bfd_vma val;
-                 enum {no_check, check_lo, check_ha} insn_check;
 
                  r_type = ELF64_R_TYPE (rel->r_info);
-                 switch (r_type)
-                   {
-                   default:
-                     insn_check = no_check;
-                     break;
-
-                   case R_PPC64_GOT_TLSLD16_HA:
-                   case R_PPC64_GOT_TLSGD16_HA:
-                   case R_PPC64_GOT_TPREL16_HA:
-                   case R_PPC64_GOT_DTPREL16_HA:
-                   case R_PPC64_GOT16_HA:
-                   case R_PPC64_TOC16_HA:
-                     insn_check = check_ha;
-                     break;
-
-                   case R_PPC64_GOT_TLSLD16_LO:
-                   case R_PPC64_GOT_TLSGD16_LO:
-                   case R_PPC64_GOT_TPREL16_LO_DS:
-                   case R_PPC64_GOT_DTPREL16_LO_DS:
-                   case R_PPC64_GOT16_LO:
-                   case R_PPC64_GOT16_LO_DS:
-                   case R_PPC64_TOC16_LO:
-                   case R_PPC64_TOC16_LO_DS:
-                     insn_check = check_lo;
-                     break;
-                   }
-
-                 if (insn_check != no_check)
-                   {
-                     bfd_vma off = rel->r_offset & ~3;
-                     unsigned char buf[4];
-                     unsigned int insn;
-
-                     if (!bfd_get_section_contents (ibfd, sec, buf, off, 4))
-                       {
-                         free (used);
-                         goto error_ret;
-                       }
-                     insn = bfd_get_32 (ibfd, buf);
-                     if (insn_check == check_lo
-                         ? !ok_lo_toc_insn (insn, r_type)
-                         : ((insn & ((0x3f << 26) | 0x1f << 16))
-                            != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
-                       {
-                         char str[12];
-
-                         ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1;
-                         sprintf (str, "%#08x", insn);
-                         info->callbacks->einfo
-                           /* xgettext:c-format */
-                           (_("%H: toc optimization is not supported for"
-                              " %s instruction\n"),
-                            ibfd, sec, rel->r_offset & ~3, str);
-                       }
-                   }
-
                  switch (r_type)
                    {
                    case R_PPC64_TOC16:
@@ -8935,8 +9287,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              rel_hdr->sh_size = toc->reloc_count * sz;
            }
        }
-      else if (toc_relocs != NULL
-              && elf_section_data (toc)->relocs != toc_relocs)
+      else if (elf_section_data (toc)->relocs != toc_relocs)
        free (toc_relocs);
 
       if (local_syms != NULL
@@ -8964,11 +9315,13 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       if (!is_ppc64_elf (ibfd))
        continue;
 
-      if (!ppc64_elf_tdata (ibfd)->has_gotrel)
+      if (!ppc64_elf_tdata (ibfd)->has_optrel)
        continue;
 
       sec = ppc64_elf_tdata (ibfd)->got;
-      got = sec->output_section->vma + sec->output_offset + 0x8000;
+      got = 0;
+      if (sec != NULL)
+       got = sec->output_section->vma + sec->output_offset + 0x8000;
 
       local_syms = NULL;
       symtab_hdr = &elf_symtab_hdr (ibfd);
@@ -8976,7 +9329,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       for (sec = ibfd->sections; sec != NULL; sec = sec->next)
        {
          if (sec->reloc_count == 0
-             || !ppc64_elf_section_data (sec)->has_gotrel
+             || !ppc64_elf_section_data (sec)->has_optrel
              || discarded_section (sec))
            continue;
 
@@ -8985,11 +9338,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
          if (relstart == NULL)
            {
            got_error_ret:
-             if (local_syms != NULL
-                 && symtab_hdr->contents != (unsigned char *) local_syms)
+             if (symtab_hdr->contents != (unsigned char *) local_syms)
                free (local_syms);
              if (sec != NULL
-                 && relstart != NULL
                  && elf_section_data (sec)->relocs != relstart)
                free (relstart);
              return FALSE;
@@ -9003,24 +9354,82 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              asection *sym_sec;
              struct elf_link_hash_entry *h;
              struct got_entry *ent;
-             bfd_vma sym_addend, val, pc;
+             bfd_vma val, pc;
              unsigned char buf[8];
              unsigned int insn;
+             enum {no_check, check_lo, check_ha} insn_check;
 
              r_type = ELF64_R_TYPE (rel->r_info);
              switch (r_type)
                {
                default:
-                 continue;
+                 insn_check = no_check;
+                 break;
 
-               case R_PPC64_GOT16_DS:
+               case R_PPC64_PLT16_HA:
+               case R_PPC64_GOT_TLSLD16_HA:
+               case R_PPC64_GOT_TLSGD16_HA:
+               case R_PPC64_GOT_TPREL16_HA:
+               case R_PPC64_GOT_DTPREL16_HA:
                case R_PPC64_GOT16_HA:
+               case R_PPC64_TOC16_HA:
+                 insn_check = check_ha;
+                 break;
+
+               case R_PPC64_PLT16_LO:
+               case R_PPC64_PLT16_LO_DS:
+               case R_PPC64_GOT_TLSLD16_LO:
+               case R_PPC64_GOT_TLSGD16_LO:
+               case R_PPC64_GOT_TPREL16_LO_DS:
+               case R_PPC64_GOT_DTPREL16_LO_DS:
+               case R_PPC64_GOT16_LO:
                case R_PPC64_GOT16_LO_DS:
-                 sym_addend = rel->r_addend;
+               case R_PPC64_TOC16_LO:
+               case R_PPC64_TOC16_LO_DS:
+                 insn_check = check_lo;
                  break;
+               }
+
+             if (insn_check != no_check)
+               {
+                 bfd_vma off = rel->r_offset & ~3;
+
+                 if (!bfd_get_section_contents (ibfd, sec, buf, off, 4))
+                   goto got_error_ret;
+
+                 insn = bfd_get_32 (ibfd, buf);
+                 if (insn_check == check_lo
+                     ? !ok_lo_toc_insn (insn, r_type)
+                     : ((insn & ((0x3fu << 26) | 0x1f << 16))
+                        != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+                   {
+                     char str[12];
+
+                     ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1;
+                     sprintf (str, "%#08x", insn);
+                     info->callbacks->einfo
+                       /* xgettext:c-format */
+                       (_("%H: got/toc optimization is not supported for"
+                          " %s instruction\n"),
+                        ibfd, sec, rel->r_offset & ~3, str);
+                     continue;
+                   }
+               }
 
+             switch (r_type)
+               {
+               /* Note that we don't delete GOT entries for
+                  R_PPC64_GOT16_DS since we'd need a lot more
+                  analysis.  For starters, the preliminary layout is
+                  before the GOT, PLT, dynamic sections and stubs are
+                  laid out.  Then we'd need to allow for changes in
+                  distance between sections caused by alignment.  */
+               default:
+                 continue;
+
+               case R_PPC64_GOT16_HA:
+               case R_PPC64_GOT16_LO_DS:
                case R_PPC64_GOT_PCREL34:
-                 sym_addend = 0;
                  break;
                }
 
@@ -9029,6 +9438,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                              r_symndx, ibfd))
                goto got_error_ret;
 
+             if (sym_sec == NULL
+                 || sym_sec->output_section == NULL
+                 || discarded_section (sym_sec))
+               continue;
+
+             if ((h ? h->type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+               continue;
+
              if (!SYMBOL_REFERENCES_LOCAL (info, h))
                continue;
 
@@ -9036,53 +9453,49 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                val = h->root.u.def.value;
              else
                val = sym->st_value;
-             val += sym_addend;
+             val += rel->r_addend;
              val += sym_sec->output_section->vma + sym_sec->output_offset;
 
+/* Fudge factor to allow for the fact that the preliminary layout
+   isn't exact.  Reduce limits by this factor.  */
+#define LIMIT_ADJUST(LIMIT) ((LIMIT) - (LIMIT) / 16)
+
              switch (r_type)
                {
                default:
                  continue;
 
-               case R_PPC64_GOT16_DS:
-                 if (val - got + 0x8000 >= 0x10000)
-                   continue;
-                 if (!bfd_get_section_contents (ibfd, sec, buf,
-                                                rel->r_offset & ~3, 4))
-                   goto got_error_ret;
-                 insn = bfd_get_32 (ibfd, buf);
-                 if ((insn & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */)
-                   continue;
-                 break;
-
                case R_PPC64_GOT16_HA:
-                 if (val - got + 0x80008000ULL >= 0x100000000ULL)
+                 if (val - got + LIMIT_ADJUST (0x80008000ULL)
+                     >= LIMIT_ADJUST (0x100000000ULL))
                    continue;
 
                  if (!bfd_get_section_contents (ibfd, sec, buf,
                                                 rel->r_offset & ~3, 4))
                    goto got_error_ret;
                  insn = bfd_get_32 (ibfd, buf);
-                 if (((insn & ((0x3f << 26) | 0x1f << 16))
+                 if (((insn & ((0x3fu << 26) | 0x1f << 16))
                       != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
                    continue;
                  break;
 
                case R_PPC64_GOT16_LO_DS:
-                 if (val - got + 0x80008000ULL >= 0x100000000ULL)
+                 if (val - got + LIMIT_ADJUST (0x80008000ULL)
+                     >= LIMIT_ADJUST (0x100000000ULL))
                    continue;
                  if (!bfd_get_section_contents (ibfd, sec, buf,
                                                 rel->r_offset & ~3, 4))
                    goto got_error_ret;
                  insn = bfd_get_32 (ibfd, buf);
-                 if ((insn & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */)
+                 if ((insn & (0x3fu << 26 | 0x3)) != 58u << 26 /* ld */)
                    continue;
                  break;
 
                case R_PPC64_GOT_PCREL34:
                  pc = rel->r_offset;
                  pc += sec->output_section->vma + sec->output_offset;
-                 if (val - pc + (1ULL << 33) >= 1ULL << 34)
+                 if (val - pc + LIMIT_ADJUST (1ULL << 33)
+                     >= LIMIT_ADJUST (1ULL << 34))
                    continue;
                  if (!bfd_get_section_contents (ibfd, sec, buf,
                                                 rel->r_offset & ~3, 8))
@@ -9091,10 +9504,11 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  if ((insn & (-1u << 18)) != ((1u << 26) | (1u << 20)))
                    continue;
                  insn = bfd_get_32 (ibfd, buf + 4);
-                 if ((insn & (0x3f << 26)) != 57u << 26)
+                 if ((insn & (0x3fu << 26)) != 57u << 26)
                    continue;
                  break;
                }
+#undef LIMIT_ADJUST
 
              if (h != NULL)
                ent = h->got.glist;
@@ -9104,7 +9518,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  ent = local_got_ents[r_symndx];
                }
              for (; ent != NULL; ent = ent->next)
-               if (ent->addend == sym_addend
+               if (ent->addend == rel->r_addend
                    && ent->owner == ibfd
                    && ent->tls_type == 0)
                  break;
@@ -9147,7 +9561,7 @@ allocate_got (struct elf_link_hash_entry *h,
              struct got_entry *gent)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
+  struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h);
   int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)
                 ? 16 : 8);
   int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD
@@ -9163,7 +9577,7 @@ allocate_got (struct elf_link_hash_entry *h,
       htab->got_reli_size += rentsize;
     }
   else if (((bfd_link_pic (info)
-            && !((gent->tls_type & TLS_TPREL) != 0
+            && !(gent->tls_type != 0
                  && bfd_link_executable (info)
                  && SYMBOL_REFERENCES_LOCAL (info, h)))
            || (htab->elf.dynamic_sections_created
@@ -9235,10 +9649,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (htab == NULL)
     return FALSE;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   /* Run through the TLS GD got entries first if we're changing them
      to TPREL.  */
-  if ((eh->tls_mask & (TLS_TLS | TLS_TPRELGD)) == (TLS_TLS | TLS_TPRELGD))
+  if ((eh->tls_mask & (TLS_TLS | TLS_GDIE)) == (TLS_TLS | TLS_GDIE))
     for (gent = h->got.glist; gent != NULL; gent = gent->next)
       if (gent->got.refcount > 0
          && (gent->tls_type & TLS_GD) != 0)
@@ -9269,7 +9683,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     if (gent->got.refcount > 0)
       {
        if ((gent->tls_type & TLS_LD) != 0
-           && !h->def_dynamic)
+           && SYMBOL_REFERENCES_LOCAL (info, h))
          {
            ppc64_tlsld_got (gent->owner)->got.refcount += 1;
            *pgent = gent->next;
@@ -9286,7 +9700,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   for (gent = h->got.glist; gent != NULL; gent = gent->next)
     if (!gent->is_indirect)
       {
-       /* Make sure this symbol is output as a dynamic symbol.  */
+       /* Ensure we catch all the cases where this symbol should
+          be made dynamic.  */
        if (!ensure_undef_dynamic (info, h))
          return FALSE;
 
@@ -9300,19 +9715,19 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
      IFUNCs which are handled even in static executables.  */
   if (!htab->elf.dynamic_sections_created
       && h->type != STT_GNU_IFUNC)
-    eh->dyn_relocs = NULL;
+    h->dyn_relocs = NULL;
 
   /* Discard relocs on undefined symbols that must be local.  */
   else if (h->root.type == bfd_link_hash_undefined
           && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-    eh->dyn_relocs = NULL;
+    h->dyn_relocs = NULL;
 
   /* Also discard relocs on undefined weak syms with non-default
      visibility, or when dynamic_undefined_weak says so.  */
   else if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
-    eh->dyn_relocs = NULL;
+    h->dyn_relocs = NULL;
 
-  if (eh->dyn_relocs != NULL)
+  if (h->dyn_relocs != NULL)
     {
       struct elf_dyn_relocs *p, **pp;
 
@@ -9321,7 +9736,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         be defined in regular objects.  For the normal shared case,
         discard space for relocs that have become local due to symbol
         visibility changes.  */
-
       if (bfd_link_pic (info))
        {
          /* Relocs that use pc_count are those that appear on a call
@@ -9333,7 +9747,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             avoid writing weird assembly.  */
          if (SYMBOL_CALLS_LOCAL (info, h))
            {
-             for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+             for (pp = &h->dyn_relocs; (p = *pp) != NULL; )
                {
                  p->count -= p->pc_count;
                  p->pc_count = 0;
@@ -9344,35 +9758,38 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                }
            }
 
-         if (eh->dyn_relocs != NULL)
+         if (h->dyn_relocs != NULL)
            {
-             /* Make sure this symbol is output as a dynamic symbol.  */
+             /* Ensure we catch all the cases where this symbol
+                should be made dynamic.  */
              if (!ensure_undef_dynamic (info, h))
                return FALSE;
            }
        }
-      else if (ELIMINATE_COPY_RELOCS && h->type != STT_GNU_IFUNC)
+
+      /* For a fixed position executable, discard space for
+        relocs against symbols which are not dynamic.  */
+      else if (h->type != STT_GNU_IFUNC)
        {
-         /* For the non-pic case, discard space for relocs against
-            symbols which turn out to need copy relocs or are not
-            dynamic.  */
          if (h->dynamic_adjusted
              && !h->def_regular
              && !ELF_COMMON_DEF_P (h))
            {
-             /* Make sure this symbol is output as a dynamic symbol.  */
+             /* Ensure we catch all the cases where this symbol
+                should be made dynamic.  */
              if (!ensure_undef_dynamic (info, h))
                return FALSE;
 
+             /* But if that didn't work out, discard dynamic relocs.  */
              if (h->dynindx == -1)
-               eh->dyn_relocs = NULL;
+               h->dyn_relocs = NULL;
            }
          else
-           eh->dyn_relocs = NULL;
+           h->dyn_relocs = NULL;
        }
 
       /* Finally, allocate space.  */
-      for (p = eh->dyn_relocs; p != NULL; p = p->next)
+      for (p = h->dyn_relocs; p != NULL; p = p->next)
        {
          asection *sreloc = elf_section_data (p->sec)->sreloc;
          if (eh->elf.type == STT_GNU_IFUNC)
@@ -9393,7 +9810,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          && h->def_regular
          && !htab->elf.dynamic_sections_created
          && !htab->can_convert_all_inline_plt
-         && (((struct ppc_link_hash_entry *) h)->tls_mask
+         && (ppc_elf_hash_entry (h)->tls_mask
              & (TLS_TLS | PLT_KEEP)) == PLT_KEEP))
     {
       struct plt_entry *pent;
@@ -9551,33 +9968,6 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
-/* Set DF_TEXTREL if we find any dynamic relocs that apply to
-   read-only sections.  */
-
-static bfd_boolean
-maybe_set_textrel (struct elf_link_hash_entry *h, void *inf)
-{
-  asection *sec;
-
-  if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
-
-  sec = readonly_dynrelocs (h);
-  if (sec != NULL)
-    {
-      struct bfd_link_info *info = (struct bfd_link_info *) inf;
-
-      info->flags |= DF_TEXTREL;
-      info->callbacks->minfo (_("%pB: dynamic relocation against `%pT'"
-                               " in read-only section `%pA'\n"),
-                             sec->owner, h->root.root.string, sec);
-
-      /* Not an error, just cut short the traversal.  */
-      return FALSE;
-    }
-  return TRUE;
-}
-
 /* Set the sizes of the dynamic sections.  */
 
 static bfd_boolean
@@ -9695,7 +10085,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
                        htab->got_reli_size += rel_size;
                      }
                    else if (bfd_link_pic (info)
-                            && !((ent->tls_type & TLS_TPREL) != 0
+                            && !(ent->tls_type != 0
                                  && bfd_link_executable (info)))
                      {
                        asection *srel = ppc64_elf_tdata (ibfd)->relgot;
@@ -9772,7 +10162,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
              ent->got.offset = s->size;
              ent->owner = ibfd;
              s->size += 16;
-             if (bfd_link_pic (info))
+             if (bfd_link_dll (info))
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
                  srel->size += sizeof (Elf64_External_Rela);
@@ -9933,8 +10323,10 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
        }
 
       tls_opt = (htab->params->tls_get_addr_opt
-                && htab->tls_get_addr_fd != NULL
-                && htab->tls_get_addr_fd->elf.plt.plist != NULL);
+                && ((htab->tls_get_addr_fd != NULL
+                     && htab->tls_get_addr_fd->elf.plt.plist != NULL)
+                    || (htab->tga_desc_fd != NULL
+                        && htab->tga_desc_fd->elf.plt.plist != NULL)));
       if (tls_opt || !htab->opd_abi)
        {
          if (!add_dynamic_entry (DT_PPC64_OPT, tls_opt ? PPC64_OPT_TLS : 0))
@@ -9951,7 +10343,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
          /* If any dynamic relocs apply to a read-only section,
             then we need a DT_TEXTREL entry.  */
          if ((info->flags & DF_TEXTREL) == 0)
-           elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info);
+           elf_link_hash_traverse (&htab->elf,
+                                   _bfd_elf_maybe_set_textrel, info);
 
          if ((info->flags & DF_TEXTREL) != 0)
            {
@@ -10258,7 +10651,7 @@ emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
 }
 
 static bfd_byte *
-build_powerxx_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
+build_power10_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
                      bfd_boolean load)
 {
   uint64_t insn;
@@ -10340,7 +10733,7 @@ build_powerxx_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
 }
 
 static unsigned int
-size_powerxx_offset (bfd_vma off, int odd)
+size_power10_offset (bfd_vma off, int odd)
 {
   if (off - odd + (1ULL << 33) < 1ULL << 34)
     return odd + 8;
@@ -10351,7 +10744,7 @@ size_powerxx_offset (bfd_vma off, int odd)
 }
 
 static unsigned int
-num_relocs_for_powerxx_offset (bfd_vma off, int odd)
+num_relocs_for_power10_offset (bfd_vma off, int odd)
 {
   if (off - odd + (1ULL << 33) < 1ULL << 34)
     return 1;
@@ -10362,7 +10755,7 @@ num_relocs_for_powerxx_offset (bfd_vma off, int odd)
 }
 
 static Elf_Internal_Rela *
-emit_relocs_for_powerxx_offset (struct bfd_link_info *info,
+emit_relocs_for_power10_offset (struct bfd_link_info *info,
                                Elf_Internal_Rela *r, bfd_vma roff,
                                bfd_vma targ, bfd_vma off, int odd)
 {
@@ -10468,55 +10861,61 @@ eh_advance_size (unsigned int delta)
 static inline unsigned int
 plt_stub_size (struct ppc_link_hash_table *htab,
               struct ppc_stub_hash_entry *stub_entry,
-              bfd_vma off)
+              bfd_vma off,
+              unsigned int odd)
 {
   unsigned size;
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
-      if (htab->powerxx_stubs)
-       {
-         bfd_vma start = (stub_entry->stub_offset
-                          + stub_entry->group->stub_sec->output_offset
-                          + stub_entry->group->stub_sec->output_section->vma);
-         if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
-           start += 4;
-         size = 8 + size_powerxx_offset (off, start & 4);
-       }
+      if (htab->params->power10_stubs != 0)
+       size = 8 + size_power10_offset (off, odd);
       else
        size = 8 + size_offset (off - 8);
       if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
        size += 4;
-      return size;
     }
-
-  size = 12;
-  if (ALWAYS_EMIT_R2SAVE
-      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-    size += 4;
-  if (PPC_HA (off) != 0)
-    size += 4;
-  if (htab->opd_abi)
+  else
     {
-      size += 4;
-      if (htab->params->plt_static_chain)
+      size = 12;
+      if (ALWAYS_EMIT_R2SAVE
+         || stub_entry->stub_type == ppc_stub_plt_call_r2save)
        size += 4;
-      if (htab->params->plt_thread_safe
-         && htab->elf.dynamic_sections_created
-         && stub_entry->h != NULL
-         && stub_entry->h->elf.dynindx != -1)
-       size += 8;
-      if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off))
+      if (PPC_HA (off) != 0)
        size += 4;
+      if (htab->opd_abi)
+       {
+         size += 4;
+         if (htab->params->plt_static_chain)
+           size += 4;
+         if (htab->params->plt_thread_safe
+             && htab->elf.dynamic_sections_created
+             && stub_entry->h != NULL
+             && stub_entry->h->elf.dynindx != -1)
+           size += 8;
+         if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain)
+             != 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)
+      && is_tls_get_addr (&stub_entry->h->elf, htab)
       && htab->params->tls_get_addr_opt)
     {
-      size += 7 * 4;
-      if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
-       size += 6 * 4;
+      if (!htab->params->no_tls_get_addr_regsave)
+       {
+         size += 30 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+             || stub_entry->stub_type == ppc_stub_plt_call_both)
+           size += 4;
+       }
+      else
+       {
+         size += 7 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+             || stub_entry->stub_type == ppc_stub_plt_call_both)
+           size += 6 * 4;
+       }
     }
   return size;
 }
@@ -10530,7 +10929,8 @@ plt_stub_size (struct ppc_link_hash_table *htab,
 static inline unsigned int
 plt_stub_pad (struct ppc_link_hash_table *htab,
              struct ppc_stub_hash_entry *stub_entry,
-             bfd_vma plt_off)
+             bfd_vma plt_off,
+             unsigned int odd)
 {
   int stub_align;
   unsigned stub_size;
@@ -10545,7 +10945,7 @@ plt_stub_pad (struct ppc_link_hash_table *htab,
     }
 
   stub_align = 1 << -htab->params->plt_stub_align;
-  stub_size = plt_stub_size (htab, stub_entry, plt_off);
+  stub_size = plt_stub_size (htab, stub_entry, plt_off, odd);
   if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
       > ((stub_size - 1) & -stub_align))
     return stub_align - (stub_off & (stub_align - 1));
@@ -10572,8 +10972,8 @@ build_plt_stub (struct ppc_link_hash_table *htab,
   if (!ALWAYS_USE_FAKE_DEP
       && plt_load_toc
       && plt_thread_safe
-      && !((stub_entry->h == htab->tls_get_addr_fd
-           || stub_entry->h == htab->tls_get_addr)
+      && !(stub_entry->h != NULL
+          && is_tls_get_addr (&stub_entry->h->elf, htab)
           && htab->params->tls_get_addr_opt))
     {
       bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
@@ -10732,70 +11132,149 @@ build_plt_stub (struct ppc_link_hash_table *htab,
 
 /* Build a special .plt call stub for __tls_get_addr.  */
 
-#define LD_R11_0R3     0xe9630000
+#define LD_R0_0R3      0xe8030000
 #define LD_R12_0R3     0xe9830000
 #define MR_R0_R3       0x7c601b78
-#define CMPDI_R11_0    0x2c2b0000
+#define CMPDI_R0_0     0x2c200000
 #define ADD_R3_R12_R13 0x7c6c6a14
 #define BEQLR          0x4d820020
 #define MR_R3_R0       0x7c030378
-#define STD_R11_0R1    0xf9610000
 #define BCTRL          0x4e800421
-#define LD_R11_0R1     0xe9610000
-#define MTLR_R11       0x7d6803a6
 
-static inline bfd_byte *
-build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
+static bfd_byte *
+build_tls_get_addr_head (struct ppc_link_hash_table *htab,
                         struct ppc_stub_hash_entry *stub_entry,
-                        bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
+                        bfd_byte *p)
 {
   bfd *obfd = htab->params->stub_bfd;
-  bfd_byte *loc = p;
 
-  bfd_put_32 (obfd, LD_R11_0R3 + 0, p),                p += 4;
+  bfd_put_32 (obfd, LD_R0_0R3 + 0, p),         p += 4;
   bfd_put_32 (obfd, LD_R12_0R3 + 8, p),                p += 4;
+  bfd_put_32 (obfd, CMPDI_R0_0, 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;
-  if (r != NULL)
-    r[0].r_offset += 7 * 4;
-  if (stub_entry->stub_type != ppc_stub_plt_call_r2save)
-    return build_plt_stub (htab, stub_entry, p, offset, r);
 
-  bfd_put_32 (obfd, MFLR_R11, p),              p += 4;
-  bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4;
+  if (!htab->params->no_tls_get_addr_regsave)
+    p = tls_get_addr_prologue (obfd, p, htab);
+  else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+    {
+      bfd_put_32 (obfd, MFLR_R0, p);
+      p += 4;
+      bfd_put_32 (obfd, STD_R0_0R1 + STK_LINKER (htab), p);
+      p += 4;
+    }
+  return p;
+}
+
+static bfd_byte *
+build_tls_get_addr_tail (struct ppc_link_hash_table *htab,
+                        struct ppc_stub_hash_entry *stub_entry,
+                        bfd_byte *p,
+                        bfd_byte *loc)
+{
+  bfd *obfd = htab->params->stub_bfd;
+
+  if (!htab->params->no_tls_get_addr_regsave)
+    {
+      bfd_put_32 (obfd, BCTRL, p - 4);
 
-  if (r != NULL)
-    r[0].r_offset += 2 * 4;
-  p = build_plt_stub (htab, stub_entry, p, offset, r);
-  bfd_put_32 (obfd, BCTRL, p - 4);
+      if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+         || stub_entry->stub_type == ppc_stub_plt_call_both)
+       {
+         bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
+       }
+      p = tls_get_addr_epilogue (obfd, p, htab);
+    }
+  else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+    {
+      bfd_put_32 (obfd, BCTRL, p - 4);
 
-  bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p),    p += 4;
-  bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p),        p += 4;
-  bfd_put_32 (obfd, MTLR_R11, p),              p += 4;
-  bfd_put_32 (obfd, BLR, p),                   p += 4;
+      bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
+      p += 4;
+      bfd_put_32 (obfd, LD_R0_0R1 + STK_LINKER (htab), p);
+      p += 4;
+      bfd_put_32 (obfd, MTLR_R0, p);
+      p += 4;
+      bfd_put_32 (obfd, BLR, p);
+      p += 4;
+    }
 
   if (htab->glink_eh_frame != NULL
       && htab->glink_eh_frame->size != 0)
     {
       bfd_byte *base, *eh;
-      unsigned int lr_used, delta;
 
       base = htab->glink_eh_frame->contents + stub_entry->group->eh_base + 17;
       eh = base + stub_entry->group->eh_size;
-      lr_used = stub_entry->stub_offset + (p - 20 - loc);
-      delta = lr_used - stub_entry->group->lr_restore;
-      stub_entry->group->lr_restore = lr_used + 16;
-      eh = eh_advance (htab->elf.dynobj, eh, delta);
-      *eh++ = DW_CFA_offset_extended_sf;
-      *eh++ = 65;
-      *eh++ = -(STK_LINKER (htab) / 8) & 0x7f;
-      *eh++ = DW_CFA_advance_loc + 4;
-      *eh++ = DW_CFA_restore_extended;
-      *eh++ = 65;
-      stub_entry->group->eh_size = eh - base;
+
+      if (!htab->params->no_tls_get_addr_regsave)
+       {
+         unsigned int cfa_updt, delta, i;
+
+         /* After the bctrl, lr has been modified so we need to emit
+            .eh_frame info saying the return address is on the stack.  In
+            fact we must put the EH info at or before the call rather
+            than after it, because the EH info for a call needs to be
+            specified by that point.
+            See libgcc/unwind-dw2.c execute_cfa_program.
+            Any stack pointer update must be described immediately after
+            the instruction making the change, and since the stdu occurs
+            after saving regs we put all the reg saves and the cfa
+            change there.  */
+         cfa_updt = stub_entry->stub_offset + 18 * 4;
+         delta = cfa_updt - stub_entry->group->lr_restore;
+         stub_entry->group->lr_restore
+           = stub_entry->stub_offset + (p - loc) - 4;
+         eh = eh_advance (htab->elf.dynobj, eh, delta);
+         *eh++ = DW_CFA_def_cfa_offset;
+         if (htab->opd_abi)
+           {
+             *eh++ = 128;
+             *eh++ = 1;
+           }
+         else
+           *eh++ = 96;
+         *eh++ = DW_CFA_offset_extended_sf;
+         *eh++ = 65;
+         *eh++ = (-16 / 8) & 0x7f;
+         for (i = 4; i < 12; i++)
+           {
+             *eh++ = DW_CFA_offset + i;
+             *eh++ = (htab->opd_abi ? 13 : 12) - i;
+           }
+         *eh++ = (DW_CFA_advance_loc
+                  + (stub_entry->group->lr_restore - 8 - cfa_updt) / 4);
+         *eh++ = DW_CFA_def_cfa_offset;
+         *eh++ = 0;
+         for (i = 4; i < 12; i++)
+           *eh++ = DW_CFA_restore + i;
+         *eh++ = DW_CFA_advance_loc + 2;
+         *eh++ = DW_CFA_restore_extended;
+         *eh++ = 65;
+         stub_entry->group->eh_size = eh - base;
+       }
+      else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+              || stub_entry->stub_type == ppc_stub_plt_call_both)
+       {
+         unsigned int lr_used, delta;
+
+         lr_used = stub_entry->stub_offset + (p - 20 - loc);
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->lr_restore = lr_used + 16;
+         eh = eh_advance (htab->elf.dynobj, eh, delta);
+         *eh++ = DW_CFA_offset_extended_sf;
+         *eh++ = 65;
+         *eh++ = -(STK_LINKER (htab) / 8) & 0x7f;
+         *eh++ = DW_CFA_advance_loc + 4;
+         *eh++ = DW_CFA_restore_extended;
+         *eh++ = 65;
+         stub_entry->group->eh_size = eh - base;
+       }
     }
   return p;
 }
@@ -10869,9 +11348,7 @@ use_global_in_relocs (struct ppc_link_hash_table *htab,
     h = ppc_follow_link (h->oh);
   BFD_ASSERT (h->elf.root.type == bfd_link_hash_defined
              || h->elf.root.type == bfd_link_hash_defweak);
-  symval = (h->elf.root.u.def.value
-           + h->elf.root.u.def.section->output_offset
-           + h->elf.root.u.def.section->output_section->vma);
+  symval = defined_sym_val (&h->elf);
   while (num_rel-- != 0)
     {
       r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info));
@@ -10931,6 +11408,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_branch_hash_entry *br_entry;
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
+  bfd *obfd;
   bfd_byte *loc;
   bfd_byte *p, *relp;
   bfd_vma targ, off;
@@ -10938,11 +11416,31 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   asection *plt;
   int num_rel;
   int odd;
+  bfd_boolean is_tga;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
   info = in_arg;
 
+  /* Fail if the target section could not be assigned to an output
+     section.  The user should fix his linker script.  */
+  if (stub_entry->target_section != NULL
+      && stub_entry->target_section->output_section == NULL
+      && info->non_contiguous_regions)
+    info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. "
+                             "Retry without --enable-non-contiguous-regions.\n"),
+                           stub_entry->target_section);
+
+  /* Same for the group.  */
+  if (stub_entry->group->stub_sec != NULL
+      && stub_entry->group->stub_sec->output_section == NULL
+      && info->non_contiguous_regions)
+    info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an "
+                             "output section. Retry without "
+                             "--enable-non-contiguous-regions.\n"),
+                           stub_entry->group->stub_sec,
+                           stub_entry->target_section);
+
   htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
@@ -10968,6 +11466,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       off = targ - off;
 
       p = loc;
+      obfd = htab->params->stub_bfd;
       if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
        {
          bfd_vma r2off = get_r2off (info, stub_entry);
@@ -10977,23 +11476,21 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              htab->stub_error = TRUE;
              return FALSE;
            }
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
          if (PPC_HA (r2off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+             bfd_put_32 (obfd, ADDIS_R2_R2 | PPC_HA (r2off), p);
              p += 4;
            }
          if (PPC_LO (r2off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDI_R2_R2 | PPC_LO (r2off), p);
+             bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p);
              p += 4;
            }
          off -= p - loc;
        }
-      bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p);
+      bfd_put_32 (obfd, B_DOT | (off & 0x3fffffc), p);
       p += 4;
 
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
@@ -11119,19 +11616,17 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
 
       p = loc;
+      obfd = htab->params->stub_bfd;
       if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
        {
          if (PPC_HA (off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R12_R2 | PPC_HA (off), p);
+             bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p);
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         LD_R12_0R12 | PPC_LO (off), p);
+             bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p);
            }
          else
-           bfd_put_32 (htab->params->stub_bfd,
-                       LD_R12_0R2 | PPC_LO (off), p);
+           bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p);
        }
       else
        {
@@ -11143,36 +11638,32 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              return FALSE;
            }
 
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
          if (PPC_HA (off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R12_R2 | PPC_HA (off), p);
+             bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p);
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         LD_R12_0R12 | PPC_LO (off), p);
+             bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p);
            }
          else
-           bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p);
+           bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p);
 
          if (PPC_HA (r2off) != 0)
            {
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+             bfd_put_32 (obfd, ADDIS_R2_R2 | PPC_HA (r2off), p);
            }
          if (PPC_LO (r2off) != 0)
            {
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDI_R2_R2 | PPC_LO (r2off), p);
+             bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p);
            }
        }
       p += 4;
-      bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+      bfd_put_32 (obfd, MTCTR_R12, p);
       p += 4;
-      bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+      bfd_put_32 (obfd, BCTR, p);
       p += 4;
       break;
 
@@ -11186,12 +11677,23 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       off = (stub_entry->stub_offset
             + stub_entry->group->stub_sec->output_offset
             + stub_entry->group->stub_sec->output_section->vma);
+      obfd = htab->params->stub_bfd;
+      is_tga = ((stub_entry->stub_type == ppc_stub_plt_call_notoc
+                || stub_entry->stub_type == ppc_stub_plt_call_both)
+               && stub_entry->h != NULL
+               && is_tls_get_addr (&stub_entry->h->elf, htab)
+               && htab->params->tls_get_addr_opt);
+      if (is_tga)
+       {
+         p = build_tls_get_addr_head (htab, stub_entry, p);
+         off += p - loc;
+       }
       if (stub_entry->stub_type == ppc_stub_long_branch_both
          || stub_entry->stub_type == ppc_stub_plt_branch_both
          || stub_entry->stub_type == ppc_stub_plt_call_both)
        {
          off += 4;
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
        }
       if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
@@ -11221,20 +11723,42 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       relp = p;
       num_rel = 0;
-      if (htab->powerxx_stubs)
+      if (htab->params->power10_stubs != 0)
        {
          bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc;
-         p = build_powerxx_offset (htab->params->stub_bfd, p, off, odd, load);
+         p = build_power10_offset (obfd, p, off, odd, load);
        }
       else
        {
+         if (htab->glink_eh_frame != NULL
+             && htab->glink_eh_frame->size != 0)
+           {
+             bfd_byte *base, *eh;
+             unsigned int lr_used, delta;
+
+             base = (htab->glink_eh_frame->contents
+                     + stub_entry->group->eh_base + 17);
+             eh = base + stub_entry->group->eh_size;
+             lr_used = stub_entry->stub_offset + (p - loc) + 8;
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->lr_restore = lr_used + 8;
+             eh = eh_advance (htab->elf.dynobj, eh, delta);
+             *eh++ = DW_CFA_register;
+             *eh++ = 65;
+             *eh++ = 12;
+             *eh++ = DW_CFA_advance_loc + 2;
+             *eh++ = DW_CFA_restore_extended;
+             *eh++ = 65;
+             stub_entry->group->eh_size = eh - base;
+           }
+
          /* The notoc stubs calculate their target (either a PLT entry or
             the global entry point of a function) relative to the PC
             returned by the "bcl" two instructions past the start of the
             sequence emitted by build_offset.  The offset is therefore 8
             less than calculated from the start of the sequence.  */
          off -= 8;
-         p = build_offset (htab->params->stub_bfd, p, off,
+         p = build_offset (obfd, p, off,
                            stub_entry->stub_type >= ppc_stub_plt_call_notoc);
        }
 
@@ -11246,22 +11770,24 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                  + stub_entry->group->stub_sec->output_offset
                  + stub_entry->group->stub_sec->output_section->vma
                  + (p - loc));
-         bfd_put_32 (htab->params->stub_bfd,
-                     B_DOT | ((targ - from) & 0x3fffffc), p);
+         bfd_put_32 (obfd, B_DOT | ((targ - from) & 0x3fffffc), p);
        }
       else
        {
-         bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+         bfd_put_32 (obfd, MTCTR_R12, p);
          p += 4;
-         bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+         bfd_put_32 (obfd, BCTR, p);
        }
       p += 4;
 
+      if (is_tga)
+       p = build_tls_get_addr_tail (htab, stub_entry, p, loc);
+
       if (info->emitrelocations)
        {
          bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
-         if (htab->powerxx_stubs)
-           num_rel += num_relocs_for_powerxx_offset (off, odd);
+         if (htab->params->power10_stubs != 0)
+           num_rel += num_relocs_for_power10_offset (off, odd);
          else
            {
              num_rel += num_relocs_for_offset (off);
@@ -11270,8 +11796,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r = get_relocs (stub_entry->group->stub_sec, num_rel);
          if (r == NULL)
            return FALSE;
-         if (htab->powerxx_stubs)
-           r = emit_relocs_for_powerxx_offset (info, r, roff, targ, off, odd);
+         if (htab->params->power10_stubs != 0)
+           r = emit_relocs_for_power10_offset (info, r, roff, targ, off, odd);
          else
            r = emit_relocs_for_offset (info, r, roff, targ, off);
          if (stub_entry->stub_type == ppc_stub_long_branch_notoc
@@ -11287,33 +11813,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                return FALSE;
            }
        }
-
-      if (!htab->powerxx_stubs
-         && htab->glink_eh_frame != NULL
-         && htab->glink_eh_frame->size != 0)
-       {
-         bfd_byte *base, *eh;
-         unsigned int lr_used, delta;
-
-         base = (htab->glink_eh_frame->contents
-                 + stub_entry->group->eh_base + 17);
-         eh = base + stub_entry->group->eh_size;
-         lr_used = stub_entry->stub_offset + 8;
-         if (stub_entry->stub_type == ppc_stub_long_branch_both
-             || stub_entry->stub_type == ppc_stub_plt_branch_both
-             || stub_entry->stub_type == ppc_stub_plt_call_both)
-           lr_used += 4;
-         delta = lr_used - stub_entry->group->lr_restore;
-         stub_entry->group->lr_restore = lr_used + 8;
-         eh = eh_advance (htab->elf.dynobj, eh, delta);
-         *eh++ = DW_CFA_register;
-         *eh++ = 65;
-         *eh++ = 12;
-         *eh++ = DW_CFA_advance_loc + 2;
-         *eh++ = DW_CFA_restore_extended;
-         *eh++ = 65;
-         stub_entry->group->eh_size = eh - base;
-       }
       break;
 
     case ppc_stub_plt_call:
@@ -11382,13 +11881,20 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            r[0].r_offset += 2;
          r[0].r_addend = targ;
        }
-      if (stub_entry->h != NULL
-         && (stub_entry->h == htab->tls_get_addr_fd
-             || stub_entry->h == htab->tls_get_addr)
-         && htab->params->tls_get_addr_opt)
-       p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
-      else
-       p = build_plt_stub (htab, stub_entry, loc, off, r);
+      p = loc;
+      obfd = htab->params->stub_bfd;
+      is_tga = (stub_entry->h != NULL
+               && is_tls_get_addr (&stub_entry->h->elf, htab)
+               && htab->params->tls_get_addr_opt);
+      if (is_tga)
+       {
+         p = build_tls_get_addr_head (htab, stub_entry, p);
+         if (r != NULL)
+           r[0].r_offset += p - loc;
+       }
+      p = build_plt_stub (htab, stub_entry, p, off, r);
+      if (is_tga)
+       p = build_tls_get_addr_tail (htab, stub_entry, p, loc);
       break;
 
     case ppc_stub_save_res:
@@ -11469,6 +11975,25 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   if (htab == NULL)
     return FALSE;
 
+  /* Fail if the target section could not be assigned to an output
+     section.  The user should fix his linker script.  */
+  if (stub_entry->target_section != NULL
+      && stub_entry->target_section->output_section == NULL
+      && info->non_contiguous_regions)
+    info->callbacks->einfo (_("%F%P: Could not assign %pA to an output section. "
+                             "Retry without --enable-non-contiguous-regions.\n"),
+                           stub_entry->target_section);
+
+  /* Same for the group.  */
+  if (stub_entry->group->stub_sec != NULL
+      && stub_entry->group->stub_sec->output_section == NULL
+      && info->non_contiguous_regions)
+    info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an "
+                             "output section. Retry without "
+                             "--enable-non-contiguous-regions.\n"),
+                           stub_entry->group->stub_sec,
+                           stub_entry->target_section);
+
   /* Make a note of the offset within the stubs for this entry.  */
   stub_entry->stub_offset = stub_entry->group->stub_sec->size;
 
@@ -11618,16 +12143,16 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (info->emitrelocations)
        {
          unsigned int num_rel;
-         if (htab->powerxx_stubs)
-           num_rel = num_relocs_for_powerxx_offset (off, odd);
+         if (htab->params->power10_stubs != 0)
+           num_rel = num_relocs_for_power10_offset (off, odd);
          else
            num_rel = num_relocs_for_offset (off - 8);
          stub_entry->group->stub_sec->reloc_count += num_rel;
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      if (htab->powerxx_stubs)
-       extra = size_powerxx_offset (off, odd);
+      if (htab->params->power10_stubs != 0)
+       extra = size_power10_offset (off, odd);
       else
        extra = size_offset (off - 8);
       /* Include branch insn plus those in the offset sequence.  */
@@ -11637,7 +12162,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
         calculated.  */
       off -= extra;
 
-      if (!htab->powerxx_stubs)
+      if (htab->params->power10_stubs == 0)
        {
          /* After the bcl, lr has been modified so we need to emit
             .eh_frame info saying the return address is in r12.  */
@@ -11665,11 +12190,19 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
     case ppc_stub_plt_call_notoc:
     case ppc_stub_plt_call_both:
-      off = (stub_entry->stub_offset
-            + stub_entry->group->stub_sec->output_offset
-            + stub_entry->group->stub_sec->output_section->vma);
+      lr_used = 0;
+      if (stub_entry->h != NULL
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
+         && htab->params->tls_get_addr_opt)
+       {
+         lr_used += 7 * 4;
+         if (!htab->params->no_tls_get_addr_regsave)
+           lr_used += 11 * 4;
+         else if (stub_entry->stub_type == ppc_stub_plt_call_both)
+           lr_used += 2 * 4;
+       }
       if (stub_entry->stub_type == ppc_stub_plt_call_both)
-       off += 4;
+       lr_used += 4;
       targ = stub_entry->plt_ent->plt.offset & ~1;
       if (targ >= (bfd_vma) -2)
        abort ();
@@ -11685,38 +12218,41 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            plt = htab->pltlocal;
        }
       targ += plt->output_offset + plt->output_section->vma;
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma
+            + lr_used);
       odd = off & 4;
       off = targ - off;
 
       if (htab->params->plt_stub_align != 0)
        {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+         unsigned pad = plt_stub_pad (htab, stub_entry, off, odd);
 
          stub_entry->group->stub_sec->size += pad;
          stub_entry->stub_offset = stub_entry->group->stub_sec->size;
          off -= pad;
+         odd ^= pad & 4;
        }
 
       if (info->emitrelocations)
        {
          unsigned int num_rel;
-         if (htab->powerxx_stubs)
-           num_rel = num_relocs_for_powerxx_offset (off, odd);
+         if (htab->params->power10_stubs != 0)
+           num_rel = num_relocs_for_power10_offset (off, odd);
          else
            num_rel = num_relocs_for_offset (off - 8);
          stub_entry->group->stub_sec->reloc_count += num_rel;
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      size = plt_stub_size (htab, stub_entry, off);
+      size = plt_stub_size (htab, stub_entry, off, odd);
 
-      if (!htab->powerxx_stubs)
+      if (htab->params->power10_stubs == 0)
        {
          /* After the bcl, lr has been modified so we need to emit
             .eh_frame info saying the return address is in r12.  */
-         lr_used = stub_entry->stub_offset + 8;
-         if (stub_entry->stub_type == ppc_stub_plt_call_both)
-           lr_used += 4;
+         lr_used += stub_entry->stub_offset + 8;
          /* The eh_frame info will consist of a DW_CFA_advance_loc or
             variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
             DW_CFA_restore_extended 65.  */
@@ -11724,8 +12260,32 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->eh_size += eh_advance_size (delta) + 6;
          stub_entry->group->lr_restore = lr_used + 8;
        }
-      break;
-
+      if ((stub_entry->stub_type == ppc_stub_plt_call_notoc
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+         && stub_entry->h != NULL
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
+         && htab->params->tls_get_addr_opt)
+       {
+         if (!htab->params->no_tls_get_addr_regsave)
+           {
+             unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+             delta = cfa_updt - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta);
+             stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
+             stub_entry->group->lr_restore
+               = stub_entry->stub_offset + size - 4;
+           }
+         else if (stub_entry->stub_type == ppc_stub_plt_call_both)
+           {
+             lr_used = stub_entry->stub_offset + size - 20;
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+             stub_entry->group->lr_restore
+               = stub_entry->stub_offset + size - 4;
+           }
+       }
+      break;
+
     case ppc_stub_plt_call:
     case ppc_stub_plt_call_r2save:
       targ = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
@@ -11749,7 +12309,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (htab->params->plt_stub_align != 0)
        {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+         unsigned pad = plt_stub_pad (htab, stub_entry, off, 0);
 
          stub_entry->group->stub_sec->size += pad;
          stub_entry->stub_offset = stub_entry->group->stub_sec->size;
@@ -11766,28 +12326,31 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      size = plt_stub_size (htab, stub_entry, off);
+      size = plt_stub_size (htab, stub_entry, off, 0);
 
       if (stub_entry->h != NULL
-         && (stub_entry->h == htab->tls_get_addr_fd
-             || stub_entry->h == htab->tls_get_addr)
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
          && htab->params->tls_get_addr_opt
          && stub_entry->stub_type == ppc_stub_plt_call_r2save)
        {
-         /* After the bctrl, lr has been modified so we need to
-            emit .eh_frame info saying the return address is
-            on the stack.  In fact we put the EH info specifying
-            that the return address is on the stack *at* the
-            call rather than after it, because the EH info for a
-            call needs to be specified by that point.
-            See libgcc/unwind-dw2.c execute_cfa_program.  */
-         lr_used = stub_entry->stub_offset + size - 20;
-         /* The eh_frame info will consist of a DW_CFA_advance_loc
-            or variant, DW_CFA_offset_externed_sf, 65, -stackoff,
-            DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65.  */
-         delta = lr_used - stub_entry->group->lr_restore;
-         stub_entry->group->eh_size += eh_advance_size (delta) + 6;
-         stub_entry->group->lr_restore = size - 4;
+         if (!htab->params->no_tls_get_addr_regsave)
+           {
+             /* Adjustments to r1 need to be described.  */
+             unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+             delta = cfa_updt - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta);
+             stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
+           }
+         else
+           {
+             lr_used = stub_entry->stub_offset + size - 20;
+             /* The eh_frame info will consist of a DW_CFA_advance_loc
+                or variant, DW_CFA_offset_externed_sf, 65, -stackoff,
+                DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65.  */
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+           }
+         stub_entry->group->lr_restore = stub_entry->stub_offset + size - 4;
        }
       break;
 
@@ -11808,7 +12371,7 @@ int
 ppc64_elf_setup_section_lists (struct bfd_link_info *info)
 {
   unsigned int id;
-  bfd_size_type amt;
+  size_t amt;
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
   if (htab == NULL)
@@ -12067,7 +12630,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
                  htab->got_reli_size += rel_size;
                }
              else if (bfd_link_pic (info)
-                      && !((ent->tls_type & TLS_TPREL) != 0
+                      && !(ent->tls_type != 0
                            && bfd_link_executable (info)))
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
@@ -12093,7 +12656,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
          asection *s = ppc64_elf_tdata (ibfd)->got;
          ent->got.offset = s->size;
          s->size += 16;
-         if (bfd_link_pic (info))
+         if (bfd_link_dll (info))
            {
              asection *srel = ppc64_elf_tdata (ibfd)->relgot;
              srel->size += sizeof (Elf64_External_Rela);
@@ -12218,7 +12781,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 
          /* Calls to dynamic lib functions go through a plt call stub
             that uses r2.  */
-         eh = (struct ppc_link_hash_entry *) h;
+         eh = ppc_elf_hash_entry (h);
          if (eh != NULL
              && (eh->elf.plt.plist != NULL
                  || (eh->oh != NULL
@@ -12329,9 +12892,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
            }
        }
 
-      if (local_syms != NULL
-         && (elf_symtab_hdr (isec->owner).contents
-             != (unsigned char *) local_syms))
+      if (elf_symtab_hdr (isec->owner).contents
+         != (unsigned char *) local_syms)
        free (local_syms);
       if (elf_section_data (isec)->relocs != relstart)
        free (relstart);
@@ -12633,6 +13195,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (htab == NULL)
     return FALSE;
 
+  if (htab->params->power10_stubs == -1 && !htab->has_power10_relocs)
+    htab->params->power10_stubs = 0;
+
   if (htab->params->plt_thread_safe == -1 && !bfd_link_executable (info))
     htab->params->plt_thread_safe = 1;
   if (!htab->opd_abi)
@@ -12686,6 +13251,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (!group_sections (info, stub_group_size, stubs_always_before_branch))
     return FALSE;
 
+  htab->tga_group = NULL;
+  if (!htab->params->no_tls_get_addr_regsave
+      && htab->tga_desc_fd != NULL
+      && (htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefined
+         || htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefweak)
+      && htab->tls_get_addr_fd != NULL
+      && is_static_defined (&htab->tls_get_addr_fd->elf))
+    {
+      asection *sym_sec, *code_sec, *stub_sec;
+      bfd_vma sym_value;
+      struct _opd_sec_data *opd;
+
+      sym_sec = htab->tls_get_addr_fd->elf.root.u.def.section;
+      sym_value = defined_sym_val (&htab->tls_get_addr_fd->elf);
+      code_sec = sym_sec;
+      opd = get_opd_info (sym_sec);
+      if (opd != NULL)
+       opd_entry_value (sym_sec, sym_value, &code_sec, NULL, FALSE);
+      htab->tga_group = htab->sec_info[code_sec->id].u.group;
+      stub_sec = (*htab->params->add_stub_section) (".tga_desc.stub",
+                                                   htab->tga_group->link_sec);
+      if (stub_sec == NULL)
+       return FALSE;
+      htab->tga_group->stub_sec = stub_sec;
+
+      htab->tga_desc_fd->elf.root.type = bfd_link_hash_defined;
+      htab->tga_desc_fd->elf.root.u.def.section = stub_sec;
+      htab->tga_desc_fd->elf.root.u.def.value = 0;
+      htab->tga_desc_fd->elf.type = STT_FUNC;
+      htab->tga_desc_fd->elf.def_regular = 1;
+      htab->tga_desc_fd->elf.non_elf = 0;
+      _bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, TRUE);
+    }
+
 #define STUB_SHRINK_ITER 20
   /* Loop until no stubs added.  After iteration 20 of this loop we may
      exit on a stub section shrinking.  This is to break out of a
@@ -12791,7 +13390,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
                                  r_indx, input_bfd))
                    goto error_ret_free_internal;
-                 hash = (struct ppc_link_hash_entry *) h;
+                 hash = ppc_elf_hash_entry (h);
 
                  ok_dest = FALSE;
                  fdh = NULL;
@@ -12897,7 +13496,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                                   && code_sec->output_section != NULL
                                   && (((hash ? hash->elf.other : sym->st_other)
                                        & STO_PPC64_LOCAL_MASK)
-                                      != 1 << STO_PPC64_LOCAL_BIT)))
+                                      > 1 << STO_PPC64_LOCAL_BIT)))
                        stub_type = ppc_stub_long_branch_notoc;
                    }
                  else if (stub_type != ppc_stub_plt_call)
@@ -12910,10 +13509,10 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                         fact a call needing a TOC adjustment.  */
                      if ((code_sec != NULL
                           && code_sec->output_section != NULL
-                          && (htab->sec_info[code_sec->id].toc_off
-                              != htab->sec_info[section->id].toc_off)
                           && (code_sec->has_toc_reloc
-                              || code_sec->makes_toc_func_call))
+                              || code_sec->makes_toc_func_call)
+                          && (htab->sec_info[code_sec->id].toc_off
+                              != htab->sec_info[section->id].toc_off))
                          || (((hash ? hash->elf.other : sym->st_other)
                               & STO_PPC64_LOCAL_MASK)
                              == 1 << STO_PPC64_LOCAL_BIT))
@@ -12927,8 +13526,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  if (stub_type != ppc_stub_plt_call
                      && stub_type != ppc_stub_plt_call_notoc
                      && hash != NULL
-                     && (hash == htab->tls_get_addr
-                         || hash == htab->tls_get_addr_fd)
+                     && is_tls_get_addr (&hash->elf, htab)
                      && section->has_tls_reloc
                      && irela != internal_relocs)
                    {
@@ -12938,7 +13536,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                      if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms,
                                         irela - 1, input_bfd))
                        goto error_ret_free_internal;
-                     if ((*tls_mask & TLS_TLS) != 0)
+                     if ((*tls_mask & TLS_TLS) != 0
+                         && (*tls_mask & (TLS_GD | TLS_LD)) == 0)
                        continue;
                    }
 
@@ -12974,6 +13573,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  if (stub_entry != NULL)
                    {
                      enum ppc_stub_type old_type;
+
                      /* A stub has already been created, but it may
                         not be the required type.  We shouldn't be
                         transitioning from plt_call to long_branch
@@ -12981,6 +13581,39 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                         upgrading from plt_call to plt_call_r2save or
                         from long_branch to long_branch_r2off.  */
                      free (stub_name);
+                     if (htab->params->power10_stubs == -1)
+                       {
+                         /* For --power10-stubs=auto, don't merge _notoc
+                            and other varieties of stubs.  (The _both
+                            variety won't be created.)  */
+                         bfd_boolean notoc = r_type == R_PPC64_REL24_NOTOC;
+                         struct ppc_stub_hash_entry *alt_stub
+                           = select_alt_stub (stub_entry, notoc);
+
+                         if (alt_stub == NULL)
+                           {
+                             alt_stub = (struct ppc_stub_hash_entry *)
+                               stub_hash_newfunc (NULL,
+                                                  &htab->stub_hash_table,
+                                                  stub_entry->root.string);
+                             if (alt_stub == NULL)
+                               {
+                                 /* xgettext:c-format */
+                                 _bfd_error_handler
+                                   (_("%pB: cannot create stub entry %s"),
+                                    section->owner, stub_entry->root.string);
+                                 goto error_ret_free_internal;
+                               }
+                             *alt_stub = *stub_entry;
+                             stub_entry->root.next = &alt_stub->root;
+                             if (notoc)
+                               /* Sort notoc stubs first, for no good
+                                  reason.  */
+                               alt_stub = stub_entry;
+                             alt_stub->stub_type = stub_type;
+                           }
+                         stub_entry = alt_stub;
+                       }
                      old_type = stub_entry->stub_type;
                      switch (old_type)
                        {
@@ -13050,9 +13683,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                      if (elf_section_data (section)->relocs == NULL)
                        free (internal_relocs);
                    error_ret_free_local:
-                     if (local_syms != NULL
-                         && (symtab_hdr->contents
-                             != (unsigned char *) local_syms))
+                     if (symtab_hdr->contents
+                         != (unsigned char *) local_syms)
                        free (local_syms);
                      return FALSE;
                    }
@@ -13115,6 +13747,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
              stub_sec->flags &= ~SEC_RELOC;
            }
        }
+      if (htab->tga_group != NULL)
+       {
+         /* See emit_tga_desc and emit_tga_desc_eh_frame.  */
+         htab->tga_group->eh_size
+           = 1 + 2 + (htab->opd_abi != 0) + 3 + 8 * 2 + 3 + 8 + 3;
+         htab->tga_group->lr_restore = 23 * 4;
+         htab->tga_group->stub_sec->size = 24 * 4;
+       }
 
       if (htab->stub_iteration <= STUB_SHRINK_ITER
          || htab->brlt->rawsize < htab->brlt->size)
@@ -13178,7 +13818,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
              || (htab->stub_iteration > STUB_SHRINK_ITER
                  && htab->brlt->rawsize > htab->brlt->size))
          && (htab->glink_eh_frame == NULL
-             || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
+             || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
+         && (htab->tga_group == NULL
+             || htab->stub_iteration > 1))
        break;
 
       /* Ask the linker to do its stuff.  */
@@ -13254,11 +13896,11 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
          /* Augmentation.  */
          p += 1;
 
-         *p++ = DW_CFA_advance_loc + 1;
+         *p++ = DW_CFA_advance_loc + (htab->has_plt_localentry0 ? 3 : 2);
          *p++ = DW_CFA_register;
          *p++ = 65;
          *p++ = htab->opd_abi ? 12 : 0;
-         *p++ = DW_CFA_advance_loc + (htab->opd_abi ? 5 : 7);
+         *p++ = DW_CFA_advance_loc + (htab->opd_abi ? 4 : 2);
          *p++ = DW_CFA_restore_extended;
          *p++ = 65;
          p += ((24 + align - 1) & -align) - 24;
@@ -13274,6 +13916,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
     }
 
   maybe_strip_output (info, htab->brlt);
+  if (htab->relbrlt != NULL)
+    maybe_strip_output (info, htab->relbrlt);
   if (htab->glink_eh_frame != NULL)
     maybe_strip_output (info, htab->glink_eh_frame);
 
@@ -13309,9 +13953,7 @@ ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
          && (!is_elf_hash_table (htab)
              || h->def_regular))
        {
-         TOCstart = (h->root.u.def.value - TOC_BASE_OFF
-                     + h->root.u.def.section->output_offset
-                     + h->root.u.def.section->output_section->vma);
+         TOCstart = defined_sym_val (h) - TOC_BASE_OFF;
          _bfd_set_gp_value (obfd, TOCstart);
          return TOCstart;
        }
@@ -13430,7 +14072,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
              {
                plt = htab->elf.iplt;
                relplt = htab->elf.irelplt;
-               htab->local_ifunc_resolver = 1;
+               htab->elf.ifunc_resolvers = TRUE;
                if (htab->opd_abi)
                  rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
                else
@@ -13450,10 +14092,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
                else
                  relplt = NULL;
              }
-           rela.r_addend = (h->root.u.def.value
-                            + h->root.u.def.section->output_offset
-                            + h->root.u.def.section->output_section->vma
-                            + ent->addend);
+           rela.r_addend = defined_sym_val (h) + ent->addend;
 
            if (relplt == NULL)
              {
@@ -13487,7 +14126,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
                   + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
                      / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
            if (h->type == STT_GNU_IFUNC && is_static_defined (h))
-             htab->maybe_local_ifunc_resolver = 1;
+             htab->elf.ifunc_resolvers = TRUE;
            bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
          }
       }
@@ -13616,21 +14255,18 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
              if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms,
                              lplt - local_plt, ibfd))
                {
-                 if (local_syms != NULL
-                     && symtab_hdr->contents != (unsigned char *) local_syms)
+                 if (symtab_hdr->contents != (unsigned char *) local_syms)
                    free (local_syms);
                  return FALSE;
                }
 
              val = sym->st_value + ent->addend;
-             if (ELF_ST_TYPE (sym->st_info) != STT_GNU_IFUNC)
-               val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
              if (sym_sec != NULL && sym_sec->output_section != NULL)
                val += sym_sec->output_offset + sym_sec->output_section->vma;
 
              if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                {
-                 htab->local_ifunc_resolver = 1;
+                 htab->elf.ifunc_resolvers = TRUE;
                  plt = htab->elf.iplt;
                  relplt = htab->elf.irelplt;
                }
@@ -13689,6 +14325,74 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Emit the static wrapper function preserving registers around a
+   __tls_get_addr_opt call.  */
+
+static bfd_boolean
+emit_tga_desc (struct ppc_link_hash_table *htab)
+{
+  asection *stub_sec = htab->tga_group->stub_sec;
+  unsigned int cfa_updt = 11 * 4;
+  bfd_byte *p;
+  bfd_vma to, from, delta;
+
+  BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined
+             && htab->tga_desc_fd->elf.root.u.def.section == stub_sec
+             && htab->tga_desc_fd->elf.root.u.def.value == 0);
+  to = defined_sym_val (&htab->tls_get_addr_fd->elf);
+  from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt;
+  delta = to - from;
+  if (delta + (1 << 25) >= 1 << 26)
+    {
+      _bfd_error_handler (_("__tls_get_addr call offset overflow"));
+      htab->stub_error = TRUE;
+      return FALSE;
+    }
+
+  p = stub_sec->contents;
+  p = tls_get_addr_prologue (htab->elf.dynobj, p, htab);
+  bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p);
+  p += 4;
+  p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab);
+  return stub_sec->size == (bfd_size_type) (p - stub_sec->contents);
+}
+
+/* Emit eh_frame describing the static wrapper function.  */
+
+static bfd_byte *
+emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p)
+{
+  unsigned int cfa_updt = 11 * 4;
+  unsigned int i;
+
+  *p++ = DW_CFA_advance_loc + cfa_updt / 4;
+  *p++ = DW_CFA_def_cfa_offset;
+  if (htab->opd_abi)
+    {
+      *p++ = 128;
+      *p++ = 1;
+    }
+  else
+    *p++ = 96;
+  *p++ = DW_CFA_offset_extended_sf;
+  *p++ = 65;
+  *p++ = (-16 / 8) & 0x7f;
+  for (i = 4; i < 12; i++)
+    {
+      *p++ = DW_CFA_offset + i;
+      *p++ = (htab->opd_abi ? 13 : 12) - i;
+    }
+  *p++ = DW_CFA_advance_loc + 10;
+  *p++ = DW_CFA_def_cfa_offset;
+  *p++ = 0;
+  for (i = 4; i < 12; i++)
+    *p++ = DW_CFA_restore + i;
+  *p++ = DW_CFA_advance_loc + 2;
+  *p++ = DW_CFA_restore_extended;
+  *p++ = 65;
+  return p;
+}
+
 /* Build all the stubs associated with the current output file.
    The stubs are kept in a hash table attached to the main linker
    hash table.  This function is called via gldelf64ppc_finish.  */
@@ -13790,23 +14494,60 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
        }
       else
        {
+         unsigned int insn;
+
+         /* 0:
+            .  .quad plt0-1f           # plt0 entry relative to 1:
+            #
+            # We get here with r12 initially @ a glink branch
+            # Load the address of _dl_runtime_resolve from plt0 and
+            # jump to it, with r0 set to the index of the PLT entry
+            # to be resolved and r11 the link map.
+            __glink_PLTresolve:
+            .  std %r2,24(%r1)         # optional
+            .  mflr %r0
+            .  bcl 20,31,1f
+            1:
+            .  mflr %r11
+            .  mtlr %r0
+            .  ld %r0,(0b-1b)(%r11)
+            .  sub %r12,%r12,%r11
+            .  add %r11,%r0,%r11
+            .  addi %r0,%r12,1b-2f
+            .  ld %r12,0(%r11)
+            .  srdi %r0,%r0,2
+            .  mtctr %r12
+            .  ld %r11,8(%r11)
+            .  bctr
+            2:
+            .  b __glink_PLTresolve
+            .  ...
+            .  b __glink_PLTresolve  */
+
+         if (htab->has_plt_localentry0)
+           {
+             bfd_put_32 (htab->glink->owner, STD_R2_0R1 + 24, p);
+             p += 4;
+           }
          bfd_put_32 (htab->glink->owner, MFLR_R0, p);
          p += 4;
          bfd_put_32 (htab->glink->owner, BCL_20_31, p);
          p += 4;
          bfd_put_32 (htab->glink->owner, MFLR_R11, p);
          p += 4;
-         bfd_put_32 (htab->glink->owner, STD_R2_0R1 + 24, p);
-         p += 4;
-         bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
-         p += 4;
          bfd_put_32 (htab->glink->owner, MTLR_R0, p);
          p += 4;
+         if (htab->has_plt_localentry0)
+           insn = LD_R0_0R11 | (-20 & 0xfffc);
+         else
+           insn = LD_R0_0R11 | (-16 & 0xfffc);
+         bfd_put_32 (htab->glink->owner, insn, p);
+         p += 4;
          bfd_put_32 (htab->glink->owner, SUB_R12_R12_R11, p);
          p += 4;
-         bfd_put_32 (htab->glink->owner, ADD_R11_R2_R11, p);
+         bfd_put_32 (htab->glink->owner, ADD_R11_R0_R11, p);
          p += 4;
-         bfd_put_32 (htab->glink->owner, ADDI_R0_R12 | (-48 & 0xffff), p);
+         bfd_put_32 (htab->glink->owner, ADDI_R0_R12 | (-44 & 0xffff), p);
          p += 4;
          bfd_put_32 (htab->glink->owner, LD_R12_0R11, p);
          p += 4;
@@ -13848,6 +14589,24 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
        }
     }
 
+  if (htab->tga_group != NULL)
+    {
+      htab->tga_group->lr_restore = 23 * 4;
+      htab->tga_group->stub_sec->size = 24 * 4;
+      if (!emit_tga_desc (htab))
+       return FALSE;
+      if (htab->glink_eh_frame != NULL
+         && htab->glink_eh_frame->size != 0)
+       {
+         size_t align = 4;
+
+         p = htab->glink_eh_frame->contents;
+         p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+         p += 17;
+         htab->tga_group->eh_size = emit_tga_desc_eh_frame (htab, p) - p;
+       }
+    }
+
   /* Build .glink global entry stubs, and PLT relocs for globals.  */
   elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
 
@@ -13973,42 +14732,46 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
 
   if (stats != NULL)
     {
-      size_t len;
-      *stats = bfd_malloc (500);
-      if (*stats == NULL)
-       return FALSE;
-
-      len = sprintf (*stats,
-                    ngettext ("linker stubs in %u group\n",
-                              "linker stubs in %u groups\n",
-                              stub_sec_count),
-                    stub_sec_count);
-      sprintf (*stats + len, _("  branch         %lu\n"
-                              "  branch toc adj %lu\n"
-                              "  branch notoc   %lu\n"
-                              "  branch both    %lu\n"
-                              "  long branch    %lu\n"
-                              "  long toc adj   %lu\n"
-                              "  long notoc     %lu\n"
-                              "  long both      %lu\n"
-                              "  plt call       %lu\n"
-                              "  plt call save  %lu\n"
-                              "  plt call notoc %lu\n"
-                              "  plt call both  %lu\n"
-                              "  global entry   %lu"),
-              htab->stub_count[ppc_stub_long_branch - 1],
-              htab->stub_count[ppc_stub_long_branch_r2off - 1],
-              htab->stub_count[ppc_stub_long_branch_notoc - 1],
-              htab->stub_count[ppc_stub_long_branch_both - 1],
-              htab->stub_count[ppc_stub_plt_branch - 1],
-              htab->stub_count[ppc_stub_plt_branch_r2off - 1],
-              htab->stub_count[ppc_stub_plt_branch_notoc - 1],
-              htab->stub_count[ppc_stub_plt_branch_both - 1],
-              htab->stub_count[ppc_stub_plt_call - 1],
-              htab->stub_count[ppc_stub_plt_call_r2save - 1],
-              htab->stub_count[ppc_stub_plt_call_notoc - 1],
-              htab->stub_count[ppc_stub_plt_call_both - 1],
-              htab->stub_count[ppc_stub_global_entry - 1]);
+      char *groupmsg;
+      if (asprintf (&groupmsg,
+                   ngettext ("linker stubs in %u group\n",
+                             "linker stubs in %u groups\n",
+                             stub_sec_count),
+                   stub_sec_count) < 0)
+       *stats = NULL;
+      else
+       {
+         if (asprintf (stats, _("%s"
+                                "  branch         %lu\n"
+                                "  branch toc adj %lu\n"
+                                "  branch notoc   %lu\n"
+                                "  branch both    %lu\n"
+                                "  long branch    %lu\n"
+                                "  long toc adj   %lu\n"
+                                "  long notoc     %lu\n"
+                                "  long both      %lu\n"
+                                "  plt call       %lu\n"
+                                "  plt call save  %lu\n"
+                                "  plt call notoc %lu\n"
+                                "  plt call both  %lu\n"
+                                "  global entry   %lu"),
+                       groupmsg,
+                       htab->stub_count[ppc_stub_long_branch - 1],
+                       htab->stub_count[ppc_stub_long_branch_r2off - 1],
+                       htab->stub_count[ppc_stub_long_branch_notoc - 1],
+                       htab->stub_count[ppc_stub_long_branch_both - 1],
+                       htab->stub_count[ppc_stub_plt_branch - 1],
+                       htab->stub_count[ppc_stub_plt_branch_r2off - 1],
+                       htab->stub_count[ppc_stub_plt_branch_notoc - 1],
+                       htab->stub_count[ppc_stub_plt_branch_both - 1],
+                       htab->stub_count[ppc_stub_plt_call - 1],
+                       htab->stub_count[ppc_stub_plt_call_r2save - 1],
+                       htab->stub_count[ppc_stub_plt_call_notoc - 1],
+                       htab->stub_count[ppc_stub_plt_call_both - 1],
+                       htab->stub_count[ppc_stub_global_entry - 1]) < 0)
+           *stats = NULL;
+         free (groupmsg);
+       }
     }
   return TRUE;
 }
@@ -14031,6 +14794,66 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (sec);
 }
 
+/* These are the dynamic relocations supported by glibc.  */
+
+static bfd_boolean
+ppc64_glibc_dynamic_reloc (enum elf_ppc64_reloc_type r_type)
+{
+  switch (r_type)
+    {
+    case R_PPC64_RELATIVE:
+    case R_PPC64_NONE:
+    case R_PPC64_ADDR64:
+    case R_PPC64_GLOB_DAT:
+    case R_PPC64_IRELATIVE:
+    case R_PPC64_JMP_IREL:
+    case R_PPC64_JMP_SLOT:
+    case R_PPC64_DTPMOD64:
+    case R_PPC64_DTPREL64:
+    case R_PPC64_TPREL64:
+    case R_PPC64_TPREL16_LO_DS:
+    case R_PPC64_TPREL16_DS:
+    case R_PPC64_TPREL16:
+    case R_PPC64_TPREL16_LO:
+    case R_PPC64_TPREL16_HI:
+    case R_PPC64_TPREL16_HIGH:
+    case R_PPC64_TPREL16_HA:
+    case R_PPC64_TPREL16_HIGHA:
+    case R_PPC64_TPREL16_HIGHER:
+    case R_PPC64_TPREL16_HIGHEST:
+    case R_PPC64_TPREL16_HIGHERA:
+    case R_PPC64_TPREL16_HIGHESTA:
+    case R_PPC64_ADDR16_LO_DS:
+    case R_PPC64_ADDR16_LO:
+    case R_PPC64_ADDR16_HI:
+    case R_PPC64_ADDR16_HIGH:
+    case R_PPC64_ADDR16_HA:
+    case R_PPC64_ADDR16_HIGHA:
+    case R_PPC64_REL30:
+    case R_PPC64_COPY:
+    case R_PPC64_UADDR64:
+    case R_PPC64_UADDR32:
+    case R_PPC64_ADDR32:
+    case R_PPC64_ADDR24:
+    case R_PPC64_ADDR16:
+    case R_PPC64_UADDR16:
+    case R_PPC64_ADDR16_DS:
+    case R_PPC64_ADDR16_HIGHER:
+    case R_PPC64_ADDR16_HIGHEST:
+    case R_PPC64_ADDR16_HIGHERA:
+    case R_PPC64_ADDR16_HIGHESTA:
+    case R_PPC64_ADDR14:
+    case R_PPC64_ADDR14_BRTAKEN:
+    case R_PPC64_ADDR14_BRNTAKEN:
+    case R_PPC64_REL32:
+    case R_PPC64_REL64:
+      return TRUE;
+
+    default:
+      return FALSE;
+    }
+}
+
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -14084,6 +14907,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   bfd_boolean is_opd;
   /* Assume 'at' branch hints.  */
   bfd_boolean is_isa_v2 = TRUE;
+  bfd_boolean warned_dynamic = FALSE;
   bfd_vma d_offset = (bfd_big_endian (input_bfd) ? 2 : 0);
 
   /* Initialize howto table if needed.  */
@@ -14233,7 +15057,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  }
            }
        }
-      h = (struct ppc_link_hash_entry *) h_elf;
+      h = ppc_elf_hash_entry (h_elf);
 
       if (sec != NULL && discarded_section (sec))
        {
@@ -14305,10 +15129,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          && (h == NULL
              || h->elf.root.type == bfd_link_hash_defined
              || h->elf.root.type == bfd_link_hash_defweak)
-         && (IS_PPC64_TLS_RELOC (r_type)
-             != (sym_type == STT_TLS
-                 || (sym_type == STT_SECTION
-                     && (sec->flags & SEC_THREAD_LOCAL) != 0))))
+         && IS_PPC64_TLS_RELOC (r_type) != (sym_type == STT_TLS))
        {
          if ((tls_mask & TLS_TLS) != 0
              && (r_type == R_PPC64_TLS
@@ -14348,7 +15169,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_LO_DS_OPT:
          insn = bfd_get_32 (input_bfd, contents + rel->r_offset - d_offset);
-         if ((insn & (0x3f << 26)) != 58u << 26)
+         if ((insn & (0x3fu << 26)) != 58u << 26)
            abort ();
          insn += (14u << 26) - (58u << 26);
          bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset);
@@ -14386,7 +15207,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                       doing a GD->IE transition.  */
                    if (retval == 2)
                      {
-                       tls_gd = TLS_TPRELGD;
+                       tls_gd = TLS_GDIE;
                        if ((tls_mask & TLS_TLS) != 0
                            && (tls_mask & TLS_GD) == 0)
                          goto tls_ldgd_opt;
@@ -14440,35 +15261,78 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
+       case R_PPC64_GOT_TPREL_PCREL34:
+         if ((tls_mask & TLS_TLS) != 0
+             && (tls_mask & TLS_TPREL) == 0)
+           {
+             /* pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel  */
+             pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             pinsn <<= 32;
+             pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             pinsn += ((2ULL << 56) + (-1ULL << 52)
+                       + (14ULL << 26) - (57ULL << 26) + (13ULL << 16));
+             bfd_put_32 (input_bfd, pinsn >> 32,
+                         contents + rel->r_offset);
+             bfd_put_32 (input_bfd, pinsn & 0xffffffff,
+                         contents + rel->r_offset + 4);
+             r_type = R_PPC64_TPREL34;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+           }
+         break;
+
        case R_PPC64_TLS:
          if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
-             insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
              insn = _bfd_elf_ppc_at_tls_transform (insn, 13);
              if (insn == 0)
-               abort ();
-             bfd_put_32 (input_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.  */
-             rel->r_offset += d_offset;
-             r_type = R_PPC64_TPREL16_LO;
-             if (toc_symndx != 0)
+               break;
+             if ((rel->r_offset & 3) == 0)
                {
-                 rel->r_info = ELF64_R_INFO (toc_symndx, r_type);
-                 rel->r_addend = toc_addend;
-                 /* We changed the symbol.  Start over in order to
-                    get h, sym, sec etc. right.  */
-                 goto again;
+                 bfd_put_32 (input_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.  */
+                 rel->r_offset += d_offset;
+                 r_type = R_PPC64_TPREL16_LO;
+                 if (toc_symndx != 0)
+                   {
+                     rel->r_info = ELF64_R_INFO (toc_symndx, r_type);
+                     rel->r_addend = toc_addend;
+                     /* We changed the symbol.  Start over in order to
+                        get h, sym, sec etc. right.  */
+                     goto again;
+                   }
+                 else
+                   rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+             else if ((rel->r_offset & 3) == 1)
+               {
+                 /* For pcrel IE to LE we already have the full
+                    offset and thus don't need an addi here.  A nop
+                    or mr will do.  */
+                 if ((insn & (0x3fu << 26)) == 14 << 26)
+                   {
+                     /* Extract regs from addi rt,ra,si.  */
+                     unsigned int rt = (insn >> 21) & 0x1f;
+                     unsigned int ra = (insn >> 16) & 0x1f;
+                     if (rt == ra)
+                       insn = NOP;
+                     else
+                       {
+                         /* Build or ra,rs,rb with rb==rs, ie. mr ra,rs.  */
+                         insn = (rt << 16) | (ra << 21) | (ra << 11);
+                         insn |= (31u << 26) | (444u << 1);
+                       }
+                   }
+                 bfd_put_32 (input_bfd, insn, contents + rel->r_offset - 1);
                }
-             else
-               rel->r_info = ELF64_R_INFO (r_symndx, r_type);
            }
          break;
 
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
-         tls_gd = TLS_TPRELGD;
+         tls_gd = TLS_GDIE;
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_gdld_hi;
          break;
@@ -14493,7 +15357,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT_TLSGD16:
        case R_PPC64_GOT_TLSGD16_LO:
-         tls_gd = TLS_TPRELGD;
+         tls_gd = TLS_GDIE;
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_ldgd_opt;
          break;
@@ -14511,11 +15375,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                 stays with its arg setup insns, ie. that the next
                 reloc is the __tls_get_addr call associated with
                 the current reloc.  Edit both insns.  */
-             if (input_section->has_tls_get_addr_call
+             if (input_section->nomark_tls_get_addr
                  && rel + 1 < relend
                  && branch_reloc_hash_match (input_bfd, rel + 1,
+                                             htab->tls_get_addr_fd,
+                                             htab->tga_desc_fd,
                                              htab->tls_get_addr,
-                                             htab->tls_get_addr_fd))
+                                             htab->tga_desc))
                offset = rel[1].r_offset;
              /* We read the low GOT_TLS (or TOC16) insn because we
                 need to keep the destination reg.  It may be
@@ -14527,15 +15393,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                {
                  /* IE */
                  insn1 &= (0x1f << 21) | (0x1f << 16);
-                 insn1 |= 58 << 26;    /* ld */
+                 insn1 |= 58u << 26;   /* ld */
                  insn2 = 0x7c636a14;   /* add 3,3,13 */
                  if (offset != (bfd_vma) -1)
                    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);
-                 else
+                 if (r_type == R_PPC64_TOC16
+                     || r_type == R_PPC64_TOC16_LO)
                    r_type += R_PPC64_TOC16_DS - R_PPC64_TOC16;
+                 else
+                   r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 1)) & 1)
+                             + R_PPC64_GOT_TPREL16_DS);
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                }
              else
@@ -14547,20 +15414,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  if (tls_gd == 0)
                    {
                      /* Was an LD reloc.  */
-                     if (toc_symndx)
-                       sec = local_sections[toc_symndx];
-                     for (r_symndx = 0;
-                          r_symndx < symtab_hdr->sh_info;
-                          r_symndx++)
-                       if (local_sections[r_symndx] == sec)
-                         break;
-                     if (r_symndx >= symtab_hdr->sh_info)
-                       r_symndx = STN_UNDEF;
+                     r_symndx = STN_UNDEF;
                      rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-                     if (r_symndx != STN_UNDEF)
-                       rel->r_addend -= (local_syms[r_symndx].st_value
-                                         + sec->output_offset
-                                         + sec->output_section->vma);
                    }
                  else if (toc_symndx != 0)
                    {
@@ -14599,6 +15454,51 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
+       case R_PPC64_GOT_TLSGD_PCREL34:
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
+           {
+             pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             pinsn <<= 32;
+             pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             if ((tls_mask & TLS_GDIE) != 0)
+               {
+                 /* IE, pla -> pld  */
+                 pinsn += (-2ULL << 56) + (57ULL << 26) - (14ULL << 26);
+                 r_type = R_PPC64_GOT_TPREL_PCREL34;
+               }
+             else
+               {
+                 /* LE, pla pcrel -> paddi r13  */
+                 pinsn += (-1ULL << 52) + (13ULL << 16);
+                 r_type = R_PPC64_TPREL34;
+               }
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+             bfd_put_32 (input_bfd, pinsn >> 32,
+                         contents + rel->r_offset);
+             bfd_put_32 (input_bfd, pinsn & 0xffffffff,
+                         contents + rel->r_offset + 4);
+           }
+         break;
+
+       case R_PPC64_GOT_TLSLD_PCREL34:
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
+           {
+             pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             pinsn <<= 32;
+             pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             pinsn += (-1ULL << 52) + (13ULL << 16);
+             bfd_put_32 (input_bfd, pinsn >> 32,
+                         contents + rel->r_offset);
+             bfd_put_32 (input_bfd, pinsn & 0xffffffff,
+                         contents + rel->r_offset + 4);
+             rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
+             r_symndx = STN_UNDEF;
+             r_type = R_PPC64_TPREL34;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+             goto again;
+           }
+         break;
+
        case R_PPC64_TLSGD:
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
              && rel + 1 < relend)
@@ -14620,7 +15520,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
                bfd_put_32 (output_bfd, NOP, contents + offset + 4);
 
-             if ((tls_mask & TLS_TPRELGD) != 0)
+             if ((tls_mask & TLS_GDIE) != 0)
                {
                  /* IE */
                  r_type = R_PPC64_NONE;
@@ -14634,16 +15534,27 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      r_symndx = toc_symndx;
                      rel->r_addend = toc_addend;
                    }
-                 r_type = R_PPC64_TPREL16_LO;
-                 rel->r_offset = offset + d_offset;
-                 insn2 = 0x38630000;   /* addi 3,3,0 */
+                 if (r_type1 == R_PPC64_REL24_NOTOC
+                     || r_type1 == R_PPC64_PLTCALL_NOTOC)
+                   {
+                     r_type = R_PPC64_NONE;
+                     insn2 = NOP;
+                   }
+                 else
+                   {
+                     rel->r_offset = offset + d_offset;
+                     r_type = R_PPC64_TPREL16_LO;
+                     insn2 = 0x38630000;       /* addi 3,3,0 */
+                   }
                }
              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 (STN_UNDEF, R_PPC64_NONE);
              bfd_put_32 (input_bfd, insn2, contents + offset);
-             if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
+             if ((tls_mask & TLS_GDIE) == 0
+                 && toc_symndx != 0
+                 && r_type != R_PPC64_NONE)
                goto again;
            }
          break;
@@ -14669,30 +15580,27 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
                bfd_put_32 (output_bfd, NOP, contents + offset + 4);
 
-             if (toc_symndx)
-               sec = local_sections[toc_symndx];
-             for (r_symndx = 0;
-                  r_symndx < symtab_hdr->sh_info;
-                  r_symndx++)
-               if (local_sections[r_symndx] == sec)
-                 break;
-             if (r_symndx >= symtab_hdr->sh_info)
-               r_symndx = STN_UNDEF;
-             rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-             if (r_symndx != STN_UNDEF)
-               rel->r_addend -= (local_syms[r_symndx].st_value
-                                 + sec->output_offset
-                                 + sec->output_section->vma);
-
-             r_type = R_PPC64_TPREL16_LO;
+             if (r_type1 == R_PPC64_REL24_NOTOC
+                 || r_type1 == R_PPC64_PLTCALL_NOTOC)
+               {
+                 r_type = R_PPC64_NONE;
+                 insn2 = NOP;
+               }
+             else
+               {
+                 rel->r_offset = offset + d_offset;
+                 r_symndx = STN_UNDEF;
+                 r_type = R_PPC64_TPREL16_LO;
+                 rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
+                 insn2 = 0x38630000;   /* addi 3,3,0 */
+               }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
-             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 (STN_UNDEF, R_PPC64_NONE);
-             insn2 = 0x38630000;       /* addi 3,3,0 */
              bfd_put_32 (input_bfd, insn2, contents + offset);
-             goto again;
+             if (r_type != R_PPC64_NONE)
+               goto again;
            }
          break;
 
@@ -14704,7 +15612,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if ((tls_mask & TLS_GD) == 0)
                {
                  rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE);
-                 if ((tls_mask & TLS_TPRELGD) != 0)
+                 if ((tls_mask & TLS_GDIE) != 0)
                    r_type = R_PPC64_TPREL64;
                  else
                    {
@@ -14896,6 +15804,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (stub_entry->stub_type == ppc_stub_plt_call
                  && !htab->opd_abi
                  && htab->params->plt_localentry0 != 0
+                 && h != NULL
                  && is_elfv2_localentry0 (&h->elf))
                {
                  /* The function doesn't use or change r2.  */
@@ -14929,8 +15838,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                               || nop == CROR_313131)
                        {
                          if (h != NULL
-                             && (h == htab->tls_get_addr_fd
-                                 || h == htab->tls_get_addr)
+                             && is_tls_get_addr (&h->elf, htab)
                              && htab->params->tls_get_addr_opt)
                            {
                              /* Special stub used, leave nop alone.  */
@@ -15056,7 +15964,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  || stub_entry->stub_type == ppc_stub_plt_branch_both)
              && (r_type != R_PPC64_REL24_NOTOC
                  || ((fdh ? fdh->elf.other : sym->st_other)
-                     & STO_PPC64_LOCAL_MASK) == 1 << STO_PPC64_LOCAL_BIT)
+                     & STO_PPC64_LOCAL_MASK) <= 1 << STO_PPC64_LOCAL_BIT)
              && (relocation + addend - from + max_br_offset
                  < 2 * max_br_offset))
            stub_entry = NULL;
@@ -15090,23 +15998,25 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              addend = 0;
              reloc_dest = DEST_STUB;
 
-             if (((stub_entry->stub_type == ppc_stub_plt_call
-                   && ALWAYS_EMIT_R2SAVE)
-                  || stub_entry->stub_type == ppc_stub_plt_call_r2save
-                  || stub_entry->stub_type == ppc_stub_plt_call_both)
-                 && !(h != NULL
-                      && (h == htab->tls_get_addr_fd
-                          || h == htab->tls_get_addr)
-                      && htab->params->tls_get_addr_opt)
-                 && rel + 1 < relend
-                 && rel[1].r_offset == rel->r_offset + 4
-                 && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
-               relocation += 4;
-             else if ((stub_entry->stub_type == ppc_stub_long_branch_both
-                       || stub_entry->stub_type == ppc_stub_plt_branch_both
-                       || stub_entry->stub_type == ppc_stub_plt_call_both)
-                      && r_type == R_PPC64_REL24_NOTOC)
-               relocation += 4;
+             if ((((stub_entry->stub_type == ppc_stub_plt_call
+                    && ALWAYS_EMIT_R2SAVE)
+                   || stub_entry->stub_type == ppc_stub_plt_call_r2save
+                   || stub_entry->stub_type == ppc_stub_plt_call_both)
+                  && rel + 1 < relend
+                  && rel[1].r_offset == rel->r_offset + 4
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
+                 || ((stub_entry->stub_type == ppc_stub_long_branch_both
+                      || stub_entry->stub_type == ppc_stub_plt_branch_both
+                      || stub_entry->stub_type == ppc_stub_plt_call_both)
+                     && r_type == R_PPC64_REL24_NOTOC))
+               {
+                 /* Skip over the r2 store at the start of the stub.  */
+                 if (!(stub_entry->stub_type >= ppc_stub_plt_call
+                       && htab->params->tls_get_addr_opt
+                       && h != NULL
+                       && is_tls_get_addr (&h->elf, htab)))
+                   relocation += 4;
+               }
 
              if (r_type == R_PPC64_REL24_NOTOC
                  && (stub_entry->stub_type == ppc_stub_plt_call_notoc
@@ -15155,12 +16065,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_GOT16_DS:
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
+           break;
          from = TOCstart + htab->sec_info[input_section->id].toc_off;
          if (relocation + addend - from + 0x8000 < 0x10000
-             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+             && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
            {
              insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
-             if ((insn & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */)
+             if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */)
                {
                  insn += (14u << 26) - (58u << 26);
                  bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3));
@@ -15172,19 +16085,24 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT16_LO_DS:
        case R_PPC64_GOT16_HA:
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
+           break;
          from = TOCstart + htab->sec_info[input_section->id].toc_off;
          if (relocation + addend - from + 0x80008000ULL < 0x100000000ULL
-             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+             && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
            {
              insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
-             if ((insn & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */)
+             if (r_type == R_PPC64_GOT16_LO_DS
+                 && (insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */)
                {
                  insn += (14u << 26) - (58u << 26);
                  bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3));
                  r_type = R_PPC64_TOC16_LO;
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                }
-             else if ((insn & (0x3f << 26)) == 15u << 26 /* addis */)
+             else if (r_type == R_PPC64_GOT16_HA
+                      && (insn & (0x3fu << 26)) == 15u << 26 /* addis */)
                {
                  r_type = R_PPC64_TOC16_HA;
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
@@ -15193,32 +16111,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_GOT_PCREL34:
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
+           break;
          from = (rel->r_offset
                  + input_section->output_section->vma
                  + input_section->output_offset);
-         if (relocation - from + (1ULL << 33) < 1ULL << 34
-             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
-           {
-             offset = rel->r_offset;
-             pinsn = bfd_get_32 (input_bfd, contents + offset);
-             pinsn <<= 32;
-             pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
-             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
-                  == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
-               {
-                 /* Replace with paddi.  */
-                 pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
-                 r_type = R_PPC64_PCREL34;
-                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
-                 bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
-                 bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
-                 goto pcrelopt;
-               }
-           }
-         break;
+         if (!(relocation - from + (1ULL << 33) < 1ULL << 34
+               && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
+           break;
+
+         offset = rel->r_offset;
+         pinsn = bfd_get_32 (input_bfd, contents + offset);
+         pinsn <<= 32;
+         pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
+         if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+             != ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+           break;
+
+         /* Replace with paddi.  */
+         pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+         r_type = R_PPC64_PCREL34;
+         rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+         bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
+         bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
+         /* Fall through.  */
 
        case R_PPC64_PCREL34:
-         if (SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+         if (!htab->params->no_pcrel_opt
+             && rel + 1 < relend
+             && rel[1].r_offset == rel->r_offset
+             && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT)
+             && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
            {
              offset = rel->r_offset;
              pinsn = bfd_get_32 (input_bfd, contents + offset);
@@ -15228,32 +16152,37 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   == ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
                       | (14ULL << 26) /* paddi */))
                {
-               pcrelopt:
-                 if (rel + 1 < relend
-                     && rel[1].r_offset == offset
-                     && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT))
+                 bfd_vma off2 = rel[1].r_addend;
+                 if (off2 == 0)
+                   /* zero means next insn.  */
+                   off2 = 8;
+                 off2 += offset;
+                 if (off2 + 4 <= input_section->size)
                    {
-                     bfd_vma off2 = rel[1].r_addend;
-                     if (off2 == 0)
-                       /* zero means next insn.  */
-                       off2 = 8;
-                     off2 += offset;
-                     if (off2 + 4 <= input_section->size)
+                     uint64_t pinsn2;
+                     bfd_signed_vma addend_off;
+                     pinsn2 = bfd_get_32 (input_bfd, contents + off2);
+                     pinsn2 <<= 32;
+                     if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
                        {
-                         uint64_t pinsn2;
-                         pinsn2 = bfd_get_32 (input_bfd, contents + off2);
-                         pinsn2 <<= 32;
-                         if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+                         if (off2 + 8 > input_section->size)
                            break;
-                         if (xlate_pcrel_opt (&pinsn, &pinsn2))
-                           {
-                             bfd_put_32 (input_bfd, pinsn >> 32,
-                                         contents + offset);
-                             bfd_put_32 (input_bfd, pinsn,
-                                         contents + offset + 4);
-                             bfd_put_32 (input_bfd, pinsn2 >> 32,
-                                         contents + off2);
-                           }
+                         pinsn2 |= bfd_get_32 (input_bfd,
+                                               contents + off2 + 4);
+                       }
+                     if (xlate_pcrel_opt (&pinsn, &pinsn2, &addend_off))
+                       {
+                         addend += addend_off;
+                         rel->r_addend = addend;
+                         bfd_put_32 (input_bfd, pinsn >> 32,
+                                     contents + offset);
+                         bfd_put_32 (input_bfd, pinsn,
+                                     contents + offset + 4);
+                         bfd_put_32 (input_bfd, pinsn2 >> 32,
+                                     contents + off2);
+                         if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+                           bfd_put_32 (input_bfd, pinsn2,
+                                       contents + off2 + 4);
                        }
                    }
                }
@@ -15261,7 +16190,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
        }
 
-      /* Set `addend'.  */
       tls_type = 0;
       save_unresolved_reloc = unresolved_reloc;
       switch (r_type)
@@ -15294,6 +16222,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TLSGD16_LO:
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TLSGD_PCREL34:
          tls_type = TLS_TLS | TLS_GD;
          goto dogot;
 
@@ -15301,6 +16230,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TLSLD16_LO:
        case R_PPC64_GOT_TLSLD16_HI:
        case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TLSLD_PCREL34:
          tls_type = TLS_TLS | TLS_LD;
          goto dogot;
 
@@ -15308,6 +16238,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TPREL16_LO_DS:
        case R_PPC64_GOT_TPREL16_HI:
        case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_TPREL_PCREL34:
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogot;
 
@@ -15315,6 +16246,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_DTPREL16_LO_DS:
        case R_PPC64_GOT_DTPREL16_HI:
        case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_GOT_DTPREL_PCREL34:
          tls_type = TLS_TLS | TLS_DTPREL;
          goto dogot;
 
@@ -15334,14 +16266,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            bfd_vma off;
            unsigned long indx = 0;
            struct got_entry *ent;
-           bfd_vma sym_addend = orig_rel.r_addend;
-
-           if (r_type == R_PPC64_GOT_PCREL34)
-             sym_addend = 0;
 
            if (tls_type == (TLS_TLS | TLS_LD)
-               && (h == NULL
-                   || !h->elf.def_dynamic))
+               && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
              ent = ppc64_tlsld_got (input_bfd);
            else
              {
@@ -15371,7 +16298,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  }
 
                for (; ent != NULL; ent = ent->next)
-                 if (ent->addend == sym_addend
+                 if (ent->addend == orig_rel.r_addend
                      && ent->owner == input_bfd
                      && ent->tls_type == tls_type)
                    break;
@@ -15408,27 +16335,25 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (ifunc)
                  {
                    relgot = htab->elf.irelplt;
-                   if (indx == 0)
-                     htab->local_ifunc_resolver = 1;
-                   else if (is_static_defined (&h->elf))
-                     htab->maybe_local_ifunc_resolver = 1;
+                   if (indx == 0 || is_static_defined (&h->elf))
+                     htab->elf.ifunc_resolvers = TRUE;
                  }
                else if (indx != 0
                         || (bfd_link_pic (info)
                             && (h == NULL
-                                || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf)
-                                || (tls_type == (TLS_TLS | TLS_LD)
-                                    && !h->elf.def_dynamic))
-                            && !(tls_type == (TLS_TLS | TLS_TPREL)
+                                || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf))
+                            && !(tls_type != 0
                                  && bfd_link_executable (info)
-                                 && SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
+                                 && (h == NULL
+                                     || SYMBOL_REFERENCES_LOCAL (info,
+                                                                 &h->elf)))))
                  relgot = ppc64_elf_tdata (ent->owner)->relgot;
                if (relgot != NULL)
                  {
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
                                       + off);
-                   outrel.r_addend = sym_addend;
+                   outrel.r_addend = orig_rel.r_addend;
                    if (tls_type & (TLS_LD | TLS_GD))
                      {
                        outrel.r_addend = 0;
@@ -15441,7 +16366,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                            bfd_elf64_swap_reloca_out (output_bfd,
                                                       &outrel, loc);
                            outrel.r_offset += 8;
-                           outrel.r_addend = sym_addend;
+                           outrel.r_addend = orig_rel.r_addend;
                            outrel.r_info
                              = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
                          }
@@ -15487,7 +16412,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   emitting a reloc.  */
                else
                  {
-                   relocation += sym_addend;
+                   relocation += orig_rel.r_addend;
                    if (tls_type != 0)
                      {
                        if (htab->elf.tls_sec == NULL)
@@ -15518,7 +16443,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              abort ();
 
            relocation = got->output_section->vma + got->output_offset + off;
-           if (r_type != R_PPC64_GOT_PCREL34)
+           addend = 0;
+           if (!(r_type == R_PPC64_GOT_PCREL34
+                 || r_type == R_PPC64_GOT_TLSGD_PCREL34
+                 || r_type == R_PPC64_GOT_TLSLD_PCREL34
+                 || r_type == R_PPC64_GOT_TPREL_PCREL34
+                 || r_type == R_PPC64_GOT_DTPREL_PCREL34))
              addend = -(TOCstart + htab->sec_info[input_section->id].toc_off);
          }
          break;
@@ -15551,15 +16481,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            if (plt_list)
              {
                struct plt_entry *ent;
-               bfd_vma sym_addend = orig_rel.r_addend;
-
-               if (r_type == R_PPC64_PLT_PCREL34
-                   || r_type == R_PPC64_PLT_PCREL34_NOTOC)
-                 sym_addend = 0;
 
                for (ent = *plt_list; ent != NULL; ent = ent->next)
                  if (ent->plt.offset != (bfd_vma) -1
-                     && ent->addend == sym_addend)
+                     && ent->addend == orig_rel.r_addend)
                    {
                      asection *plt;
                      bfd_vma got;
@@ -15588,9 +16513,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                                 + htab->sec_info[input_section->id].toc_off);
                          relocation -= got;
                        }
-                     if (r_type != R_PPC64_PLT_PCREL34
-                         && r_type != R_PPC64_PLT_PCREL34_NOTOC)
-                       addend = 0;
+                     addend = 0;
                      unresolved_reloc = FALSE;
                      break;
                    }
@@ -15622,6 +16545,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TOC16_LO_DS:
        case R_PPC64_TOC16_HA:
          addend -= TOCstart + htab->sec_info[input_section->id].toc_off;
+         if (h != NULL)
+           goto dodyn;
          break;
 
          /* Relocate against the beginning of the section.  */
@@ -15671,6 +16596,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TPREL16_HIGHERA:
        case R_PPC64_TPREL16_HIGHEST:
        case R_PPC64_TPREL16_HIGHESTA:
+       case R_PPC64_TPREL34:
          if (h != NULL
              && h->elf.root.type == bfd_link_hash_undefweak
              && h->elf.dynindx == -1)
@@ -15706,6 +16632,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_DTPREL16_HIGHERA:
        case R_PPC64_DTPREL16_HIGHEST:
        case R_PPC64_DTPREL16_HIGHESTA:
+       case R_PPC64_DTPREL34:
          if (htab->elf.tls_sec != NULL)
            addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
          break;
@@ -15775,11 +16702,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          if (bfd_link_pic (info)
              ? ((h == NULL
-                 || h->dyn_relocs != NULL)
+                 || h->elf.dyn_relocs != NULL)
                 && ((h != NULL && pc_dynrelocs (h))
                     || must_be_dyn_reloc (info, r_type)))
              : (h != NULL
-                ? h->dyn_relocs != NULL
+                ? h->elf.dyn_relocs != NULL
                 : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
            {
              bfd_boolean skip, relocate;
@@ -15818,7 +16745,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
+             else if (h != NULL
+                      && !SYMBOL_REFERENCES_LOCAL (info, &h->elf)
                       && !is_opd
                       && r_type != R_PPC64_TOC)
                {
@@ -15889,21 +16817,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        }
                      else
                        {
-                         asection *osec;
+                         asection *osec = sec->output_section;
 
-                         osec = sec->output_section;
-                         indx = elf_section_data (osec)->dynindx;
-
-                         if (indx == 0)
+                         if ((osec->flags & SEC_THREAD_LOCAL) != 0)
+                           {
+                             /* TLS symbol values are relative to the
+                                TLS segment.  Dynamic relocations for
+                                local TLS symbols therefore can't be
+                                reduced to a relocation against their
+                                section symbol because it holds the
+                                address of the section, not a value
+                                relative to the TLS segment.  We could
+                                change the .tdata dynamic section symbol
+                                to be zero value but STN_UNDEF works
+                                and is used elsewhere, eg. for TPREL64
+                                GOT relocs against local TLS symbols.  */
+                             osec = htab->elf.tls_sec;
+                             indx = 0;
+                           }
+                         else
                            {
-                             if ((osec->flags & SEC_READONLY) == 0
-                                 && htab->elf.data_index_section != NULL)
-                               osec = htab->elf.data_index_section;
-                             else
-                               osec = htab->elf.text_index_section;
                              indx = elf_section_data (osec)->dynindx;
+                             if (indx == 0)
+                               {
+                                 if ((osec->flags & SEC_READONLY) == 0
+                                     && htab->elf.data_index_section != NULL)
+                                   osec = htab->elf.data_index_section;
+                                 else
+                                   osec = htab->elf.text_index_section;
+                                 indx = elf_section_data (osec)->dynindx;
+                               }
+                             BFD_ASSERT (indx != 0);
                            }
-                         BFD_ASSERT (indx != 0);
 
                          /* We are turning this relocation into one
                             against a section symbol, so subtract out
@@ -15923,10 +16868,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                {
                  sreloc = htab->elf.irelplt;
-                 if (indx == 0)
-                   htab->local_ifunc_resolver = 1;
-                 else if (is_static_defined (&h->elf))
-                   htab->maybe_local_ifunc_resolver = 1;
+                 if (indx == 0 || is_static_defined (&h->elf))
+                   htab->elf.ifunc_resolvers = TRUE;
                }
              if (sreloc == NULL)
                abort ();
@@ -15938,6 +16881,19 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
              bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
 
+             if (!warned_dynamic
+                 && !ppc64_glibc_dynamic_reloc (ELF64_R_TYPE (outrel.r_info)))
+               {
+                 info->callbacks->einfo
+                   /* xgettext:c-format */
+                   (_("%X%P: %pB: %s against %pT "
+                      "is not supported by glibc as a dynamic relocation\n"),
+                    input_bfd,
+                    ppc64_elf_howto_table[ELF64_R_TYPE (outrel.r_info)]->name,
+                    sym_name);
+                 warned_dynamic = TRUE;
+               }
+
              /* If this reloc is against an external symbol, it will
                 be computed at runtime, so there's no need to do
                 anything now.  However, for the sake of prelink ensure
@@ -16105,10 +17061,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              insn = bfd_get_32 (input_bfd, p);
-             if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
+             if ((insn & (0x3fu << 26)) == 12u << 26 /* addic */)
                {
                  /* Transform addic to addi when we change reg.  */
-                 insn &= ~((0x3f << 26) | (0x1f << 16));
+                 insn &= ~((0x3fu << 26) | (0x1f << 16));
                  insn |= (14u << 26) | (2 << 16);
                }
              else
@@ -16124,19 +17080,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
-             insn = bfd_get_32 (input_bfd, p);
-             if ((insn & ((0x3f << 26) | 0x1f << 16))
-                 != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */)
-               /* xgettext:c-format */
-               info->callbacks->minfo
-                 (_("%H: warning: %s unexpected insn %#x.\n"),
-                  input_bfd, input_section, rel->r_offset,
-                  ppc64_elf_howto_table[r_type]->name, insn);
-             else
-               {
-                 bfd_put_32 (input_bfd, NOP, p);
-                 goto copy_reloc;
-               }
+             bfd_put_32 (input_bfd, NOP, p);
+             goto copy_reloc;
            }
          break;
 
@@ -16234,8 +17179,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             forms of all the _DS relocs bloats all reloc switches in
             this file.  It doesn't make much sense to use these
             relocs in data, so testing the insn should be safe.  */
-         if ((insn & (0x3f << 26)) == (56u << 26)
-             || ((insn & (0x3f << 26)) == (61u << 26) && (insn & 3) == 1))
+         if ((insn & (0x3fu << 26)) == (56u << 26)
+             || ((insn & (0x3fu << 26)) == (61u << 26) && (insn & 3) == 1))
            mask = 15;
          relocation += addend;
          addend = insn & (mask ^ 3);
@@ -16284,15 +17229,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          enum complain_overflow complain = complain_overflow_signed;
 
          insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
-         if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+         if ((insn & (0x3fu << 26)) == 10u << 26 /* cmpli */)
            complain = complain_overflow_bitfield;
          else if (howto->rightshift == 0
-                  ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
-                     || (insn & (0x3f << 26)) == 24u << 26 /* ori */
-                     || (insn & (0x3f << 26)) == 26u << 26 /* xori */)
-                  : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
-                     || (insn & (0x3f << 26)) == 25u << 26 /* oris */
-                     || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
+                  ? ((insn & (0x3fu << 26)) == 28u << 26 /* andi */
+                     || (insn & (0x3fu << 26)) == 24u << 26 /* ori */
+                     || (insn & (0x3fu << 26)) == 26u << 26 /* xori */)
+                  : ((insn & (0x3fu << 26)) == 29u << 26 /* andis */
+                     || (insn & (0x3fu << 26)) == 25u << 26 /* oris */
+                     || (insn & (0x3fu << 26)) == 27u << 26 /* xoris */))
            complain = complain_overflow_unsigned;
          if (howto->complain_on_overflow != complain)
            {
@@ -16311,6 +17256,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_D34_HA30:
        case R_PPC64_PCREL34:
        case R_PPC64_GOT_PCREL34:
+       case R_PPC64_TPREL34:
+       case R_PPC64_DTPREL34:
+       case R_PPC64_GOT_TLSGD_PCREL34:
+       case R_PPC64_GOT_TLSLD_PCREL34:
+       case R_PPC64_GOT_TPREL_PCREL34:
+       case R_PPC64_GOT_DTPREL_PCREL34:
        case R_PPC64_PLT_PCREL34:
        case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_D28:
@@ -16396,11 +17347,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                           && (h->elf.root.type == bfd_link_hash_undefweak
                               || h->elf.root.type == bfd_link_hash_undefined)
                           && is_branch_reloc (r_type))))
-               info->callbacks->reloc_overflow (info, &h->elf.root,
-                                                sym_name, reloc_name,
-                                                orig_rel.r_addend,
-                                                input_bfd, input_section,
-                                                rel->r_offset);
+               info->callbacks->reloc_overflow
+                 (info, (struct bfd_link_hash_entry *) h, sym_name,
+                  reloc_name, orig_rel.r_addend, input_bfd, input_section,
+                  rel->r_offset);
            }
          else
            {
@@ -16411,8 +17361,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                 reloc_name, sym_name, (int) r);
              ret = FALSE;
            }
-         if (more_info != NULL)
-           free (more_info);
+         free (more_info);
        }
     copy_reloc:
       if (wrel != rel)
@@ -16530,23 +17479,21 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
          break;
        }
 
-  if (h->needs_copy)
+  if (h->needs_copy
+      && (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+      && (h->root.u.def.section == htab->elf.sdynbss
+         || h->root.u.def.section == htab->elf.sdynrelro))
     {
       /* This symbol needs a copy reloc.  Set it up.  */
       Elf_Internal_Rela rela;
       asection *srel;
       bfd_byte *loc;
 
-      if (h->dynindx == -1
-         || (h->root.type != bfd_link_hash_defined
-             && h->root.type != bfd_link_hash_defweak)
-         || htab->elf.srelbss == NULL
-         || htab->elf.sreldynrelro == NULL)
+      if (h->dynindx == -1)
        abort ();
 
-      rela.r_offset = (h->root.u.def.value
-                      + h->root.u.def.section->output_section->vma
-                      + h->root.u.def.section->output_offset);
+      rela.r_offset = defined_sym_val (h);
       rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_COPY);
       rela.r_addend = 0;
       if (h->root.u.def.section == htab->elf.sdynrelro)
@@ -16674,11 +17621,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_TEXTREL:
-             if (htab->local_ifunc_resolver)
-               info->callbacks->einfo
-                 (_("%X%P: text relocations and GNU indirect "
-                    "functions will result in a segfault at runtime\n"));
-             else if (htab->maybe_local_ifunc_resolver)
+             if (htab->elf.ifunc_resolvers)
                info->callbacks->einfo
                  (_("%P: warning: text relocations and GNU indirect "
                     "functions may result in a segfault at runtime\n"));
@@ -16776,7 +17719,9 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 /* FreeBSD support */
 
 #undef  TARGET_LITTLE_SYM
+#define TARGET_LITTLE_SYM powerpc_elf64_fbsd_le_vec
 #undef  TARGET_LITTLE_NAME
+#define TARGET_LITTLE_NAME "elf64-powerpcle-freebsd"
 
 #undef  TARGET_BIG_SYM
 #define TARGET_BIG_SYM powerpc_elf64_fbsd_vec