gas/hash.c: add new functions
[binutils-gdb.git] / bfd / elf64-ppc.c
index 7c3534ac654c67404c7ba9a2669da89cb27df461..5cbf9acfcd1cb077805fa88ec59d29b57f544d21 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright (C) 1999-2018 Free Software Foundation, Inc.
+   Copyright (C) 1999-2020 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.
    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"
 
 #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
 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
@@ -51,6 +54,8 @@ static bfd_reloc_status_type ppc64_elf_toc_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_toc64_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_toc64_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type ppc64_elf_prefix_reloc
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_unhandled_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_vma opd_entry_value
 static bfd_reloc_status_type ppc64_elf_unhandled_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_vma opd_entry_value
@@ -120,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_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
 #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
@@ -185,18 +191,25 @@ static bfd_vma opd_entry_value
 #define LD_R2_0R12     0xe84c0000      /* ld    %r2,0(%r12)     */
 #define ADD_R2_R2_R12  0x7c426214      /* add   %r2,%r2,%r12    */
 
 #define LD_R2_0R12     0xe84c0000      /* ld    %r2,0(%r12)     */
 #define ADD_R2_R2_R12  0x7c426214      /* add   %r2,%r2,%r12    */
 
+#define LI_R11_0       0x39600000      /* li    %r11,0         */
 #define LIS_R2         0x3c400000      /* lis %r2,xxx@ha         */
 #define LIS_R2         0x3c400000      /* lis %r2,xxx@ha         */
+#define LIS_R11                0x3d600000      /* lis %r11,xxx@ha        */
 #define LIS_R12                0x3d800000      /* lis %r12,xxx@ha        */
 #define ADDIS_R2_R12   0x3c4c0000      /* addis %r2,%r12,xxx@ha  */
 #define ADDIS_R12_R2   0x3d820000      /* addis %r12,%r2,xxx@ha  */
 #define ADDIS_R12_R11  0x3d8b0000      /* addis %r12,%r11,xxx@ha */
 #define ADDIS_R12_R12  0x3d8c0000      /* addis %r12,%r12,xxx@ha */
 #define ORIS_R12_R12_0 0x658c0000      /* oris  %r12,%r12,xxx@hi */
 #define LIS_R12                0x3d800000      /* lis %r12,xxx@ha        */
 #define ADDIS_R2_R12   0x3c4c0000      /* addis %r2,%r12,xxx@ha  */
 #define ADDIS_R12_R2   0x3d820000      /* addis %r12,%r2,xxx@ha  */
 #define ADDIS_R12_R11  0x3d8b0000      /* addis %r12,%r11,xxx@ha */
 #define ADDIS_R12_R12  0x3d8c0000      /* addis %r12,%r12,xxx@ha */
 #define ORIS_R12_R12_0 0x658c0000      /* oris  %r12,%r12,xxx@hi */
+#define ORI_R11_R11_0  0x616b0000      /* ori   %r11,%r11,xxx@l  */
 #define ORI_R12_R12_0  0x618c0000      /* ori   %r12,%r12,xxx@l  */
 #define LD_R12_0R12    0xe98c0000      /* ld    %r12,xxx@l(%r12) */
 #define ORI_R12_R12_0  0x618c0000      /* ori   %r12,%r12,xxx@l  */
 #define LD_R12_0R12    0xe98c0000      /* ld    %r12,xxx@l(%r12) */
+#define SLDI_R11_R11_34        0x796b1746      /* sldi  %r11,%r11,34     */
 #define SLDI_R12_R12_32        0x799c07c6      /* sldi  %r12,%r12,32     */
 #define LDX_R12_R11_R12 0x7d8b602a     /* ldx   %r12,%r11,%r12   */
 #define ADD_R12_R11_R12 0x7d8b6214     /* add   %r12,%r11,%r12   */
 #define SLDI_R12_R12_32        0x799c07c6      /* sldi  %r12,%r12,32     */
 #define LDX_R12_R11_R12 0x7d8b602a     /* ldx   %r12,%r11,%r12   */
 #define ADD_R12_R11_R12 0x7d8b6214     /* add   %r12,%r11,%r12   */
+#define PADDI_R12_PC   0x0610000039800000ULL
+#define PLD_R12_PC     0x04100000e5800000ULL
+#define PNOP           0x0700000000000000ULL
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)                    \
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)                    \
@@ -878,6 +891,87 @@ static reloc_howto_type ppc64_elf_howto_raw[] =
   HOW (R_PPC64_ADDR64_LOCAL, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
        bfd_elf_generic_reloc),
 
   HOW (R_PPC64_ADDR64_LOCAL, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
        bfd_elf_generic_reloc),
 
+  HOW (R_PPC64_PLTSEQ_NOTOC, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_PLTCALL_NOTOC, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_PCREL_OPT, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_D34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_LO, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_HI30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_HA30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_GOT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_PLT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  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),
+
+  HOW (R_PPC64_ADDR16_HIGHERA34, 1, 16, 0xffff, 34, FALSE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHEST34, 1, 16, 0xffff, 50, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHESTA34, 1, 16, 0xffff, 50, FALSE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHER34, 1, 16, 0xffff, 34, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHERA34, 1, 16, 0xffff, 34, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHEST34, 1, 16, 0xffff, 50, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHESTA34, 1, 16, 0xffff, 50, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_D28, 4, 28, 0xfff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_PCREL28, 4, 28, 0xfff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_prefix_reloc),
+
   /* GNU extension to record C++ vtable hierarchy.  */
   HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, FALSE, dont,
        NULL),
   /* GNU extension to record C++ vtable hierarchy.  */
   HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, FALSE, dont,
        NULL),
@@ -905,8 +999,7 @@ ppc_howto_init (void)
 }
 
 static reloc_howto_type *
 }
 
 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;
 
 {
   enum elf_ppc64_reloc_type r = R_PPC64_NONE;
 
@@ -1047,6 +1140,7 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC64_PLTGOT16_LO_DS:       r = R_PPC64_PLTGOT16_LO_DS;
       break;
       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;
     case BFD_RELOC_PPC_TLS:                    r = R_PPC64_TLS;
       break;
     case BFD_RELOC_PPC_TLSGD:                  r = R_PPC64_TLSGD;
@@ -1167,6 +1261,52 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC64_ADDR64_LOCAL:         r = R_PPC64_ADDR64_LOCAL;
       break;
       break;
     case BFD_RELOC_PPC64_ADDR64_LOCAL:         r = R_PPC64_ADDR64_LOCAL;
       break;
+    case BFD_RELOC_PPC64_D34:                  r = R_PPC64_D34;
+      break;
+    case BFD_RELOC_PPC64_D34_LO:               r = R_PPC64_D34_LO;
+      break;
+    case BFD_RELOC_PPC64_D34_HI30:             r = R_PPC64_D34_HI30;
+      break;
+    case BFD_RELOC_PPC64_D34_HA30:             r = R_PPC64_D34_HA30;
+      break;
+    case BFD_RELOC_PPC64_PCREL34:              r = R_PPC64_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_PCREL34:          r = R_PPC64_GOT_PCREL34;
+      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;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHEST34:     r = R_PPC64_ADDR16_HIGHEST34;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:    r = R_PPC64_ADDR16_HIGHESTA34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHER34:       r = R_PPC64_REL16_HIGHER34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHERA34:      r = R_PPC64_REL16_HIGHERA34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHEST34:      r = R_PPC64_REL16_HIGHEST34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHESTA34:     r = R_PPC64_REL16_HIGHESTA34;
+      break;
+    case BFD_RELOC_PPC64_D28:                  r = R_PPC64_D28;
+      break;
+    case BFD_RELOC_PPC64_PCREL28:              r = R_PPC64_PCREL28;
+      break;
     case BFD_RELOC_VTABLE_INHERIT:             r = R_PPC64_GNU_VTINHERIT;
       break;
     case BFD_RELOC_VTABLE_ENTRY:               r = R_PPC64_GNU_VTENTRY;
     case BFD_RELOC_VTABLE_INHERIT:             r = R_PPC64_GNU_VTINHERIT;
       break;
     case BFD_RELOC_VTABLE_ENTRY:               r = R_PPC64_GNU_VTENTRY;
@@ -1177,16 +1317,33 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
 };
 
 static reloc_howto_type *
 };
 
 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;
 {
   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];
 
 
   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;
 }
 
   return NULL;
 }
 
@@ -1243,11 +1400,17 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
     return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
                                  input_section, output_bfd, error_message);
 
     return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
                                  input_section, output_bfd, error_message);
 
-  /* Adjust the addend for sign extension of the low 16 bits.
-     We won't actually be using the low 16 bits, so trashing them
+  /* Adjust the addend for sign extension of the low 16 (or 34) bits.
+     We won't actually be using the low bits, so trashing them
      doesn't matter.  */
      doesn't matter.  */
-  reloc_entry->addend += 0x8000;
   r_type = reloc_entry->howto->type;
   r_type = reloc_entry->howto->type;
+  if (r_type == R_PPC64_ADDR16_HIGHERA34
+      || r_type == R_PPC64_ADDR16_HIGHESTA34
+      || r_type == R_PPC64_REL16_HIGHERA34
+      || r_type == R_PPC64_REL16_HIGHESTA34)
+    reloc_entry->addend += 1ULL << 33;
+  else
+    reloc_entry->addend += 1U << 15;
   if (r_type != R_PPC64_REL16DX_HA)
     return bfd_reloc_continue;
 
   if (r_type != R_PPC64_REL16DX_HA)
     return bfd_reloc_continue;
 
@@ -1262,7 +1425,7 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
            + input_section->output_section->vma);
   value = (bfd_signed_vma) value >> 16;
 
            + 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);
   insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
   insn &= ~0x1fffc1;
   insn |= (value & 0xffc1) | ((value & 0x3e) << 15);
@@ -1337,7 +1500,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);
 
     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;
   insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
   insn &= ~(0x01 << 21);
   r_type = reloc_entry->howto->type;
@@ -1487,11 +1650,53 @@ 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);
 
   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;
 }
 
   bfd_put_64 (abfd, TOCstart + TOC_BASE_OFF, (bfd_byte *) data + octets);
   return bfd_reloc_ok;
 }
 
+static bfd_reloc_status_type
+ppc64_elf_prefix_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+                       void *data, asection *input_section,
+                       bfd *output_bfd, char **error_message)
+{
+  uint64_t insn;
+  bfd_vma targ;
+
+  if (output_bfd != NULL)
+    return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+                                 input_section, output_bfd, error_message);
+
+  insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+  insn <<= 32;
+  insn |= bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address + 4);
+
+  targ = (symbol->section->output_section->vma
+         + symbol->section->output_offset
+         + reloc_entry->addend);
+  if (!bfd_is_com_section (symbol->section))
+    targ += symbol->value;
+  if (reloc_entry->howto->type == R_PPC64_D34_HA30)
+    targ += 1ULL << 33;
+  if (reloc_entry->howto->pc_relative)
+    {
+      bfd_vma from = (reloc_entry->address
+                     + input_section->output_offset
+                     + input_section->output_section->vma);
+      targ -=from;
+    }
+  targ >>= reloc_entry->howto->rightshift;
+  insn &= ~reloc_entry->howto->dst_mask;
+  insn |= ((targ << 16) | (targ & 0xffff)) & reloc_entry->howto->dst_mask;
+  bfd_put_32 (abfd, insn >> 32, (bfd_byte *) data + reloc_entry->address);
+  bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address + 4);
+  if (reloc_entry->howto->complain_on_overflow == complain_overflow_signed
+      && (targ + (1ULL << (reloc_entry->howto->bitsize - 1))
+         >= 1ULL << reloc_entry->howto->bitsize))
+    return bfd_reloc_overflow;
+  return bfd_reloc_ok;
+}
+
 static bfd_reloc_status_type
 ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
                           void *data, asection *input_section,
 static bfd_reloc_status_type
 ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
                           void *data, asection *input_section,
@@ -1594,6 +1799,10 @@ struct ppc64_elf_obj_tdata
   /* Set if toc/got ha relocs detected not using r2, or lo reloc
      instruction not one we handle.  */
   unsigned int unexpected_toc_insn : 1;
   /* Set if toc/got ha relocs detected not using r2, or lo reloc
      instruction not one we handle.  */
   unsigned int unexpected_toc_insn : 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) \
 };
 
 #define ppc64_elf_tdata(bfd) \
@@ -1793,6 +2002,10 @@ struct _ppc64_elf_section_data
 
   /* Flag set when PLTCALL relocs are detected.  */
   unsigned int has_pltcall:1;
 
   /* Flag set when PLTCALL relocs are detected.  */
   unsigned int has_pltcall: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) \
 };
 
 #define ppc64_elf_section_data(sec) \
@@ -1804,7 +2017,7 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   if (!sec->used_by_bfd)
     {
       struct _ppc64_elf_section_data *sdata;
   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)
 
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
@@ -1815,6 +2028,18 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   return _bfd_elf_new_section_hook (abfd, 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)
 {
 static struct _opd_sec_data *
 get_opd_info (asection * sec)
 {
@@ -1908,15 +2133,26 @@ compare_symbols (const void *ap, const void *bp)
   if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0)
     return 1;
 
   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 *
 }
 
 /* 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)
     {
 
   if (id == (unsigned) -1)
     {
@@ -2202,8 +2438,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
        free_contents_and_exit_err:
          count = -1;
        free_contents_and_exit:
        free_contents_and_exit_err:
          count = -1;
        free_contents_and_exit:
-         if (contents)
-           free (contents);
+         free (contents);
          goto done;
        }
 
          goto done;
        }
 
@@ -2552,6 +2787,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_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:
       return 0;
 
     case R_PPC64_TPREL16:
@@ -2567,6 +2808,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_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);
       /* These relocations are relative but in a shared library the
         linker doesn't know the thread pointer base.  */
       return bfd_link_dll (info);
@@ -2574,20 +2816,20 @@ must_be_dyn_reloc (struct bfd_link_info *info,
 }
 
 /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
 }
 
 /* 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
    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
 #define ELIMINATE_COPY_RELOCS 1
 
 /* Section name for stubs is the associated section name plus this
@@ -2603,8 +2845,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.
    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
 
    .   mtctr   %r12
    .   bctr
 
@@ -2630,8 +2872,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)
 
    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
    .   addis   %r2,%r2,off@ha
    .   addi    %r2,%r2,off@l
    .   mtctr   %r12
@@ -2687,8 +2929,34 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    .   mtctr   %r12
    .   bctr
 
    .   mtctr   %r12
    .   bctr
 
+   There are also ELFv1 power10 variants of these stubs.
+   ppc_stub_long_branch_notoc:
+   .   pla     %r12,dest@pcrel
+   .   b       dest
+   ppc_stub_plt_branch_notoc:
+   .   lis     %r11,(dest-1f)@highesta34
+   .   ori     %r11,%r11,(dest-1f)@highera34
+   .   sldi    %r11,%r11,34
+   . 1: pla    %r12,dest@pcrel
+   .   add     %r12,%r11,%r12
+   .   mtctr   %r12
+   .   bctr
+   ppc_stub_plt_call_notoc:
+   .   lis     %r11,(xxx-1f)@highesta34
+   .   ori     %r11,%r11,(xxx-1f)@highera34
+   .   sldi    %r11,%r11,34
+   . 1: pla    %r12,xxx@pcrel
+   .   ldx     %r12,%r11,%r12
+   .   mtctr   %r12
+   .   bctr
+
    In cases where the high instructions would add zero, they are
    omitted and following instructions modified in some cases.
    In cases where the high instructions would add zero, they are
    omitted and following instructions modified in some cases.
+   For example, a power10 ppc_stub_plt_call_notoc might simplify down
+   to
+   .   pld     %r12,xxx@pcrel
+   .   mtctr   %r12
+   .   bctr
 
    For a given stub group (a set of sections all using the same toc
    pointer value) there will be just one stub type used for any
 
    For a given stub group (a set of sections all using the same toc
    pointer value) there will be just one stub type used for any
@@ -2823,9 +3091,6 @@ struct ppc_link_hash_entry
     struct ppc_link_hash_entry *next_dot_sym;
   } u;
 
     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;
 
   /* Link between function code and descriptor symbols.  */
   struct ppc_link_hash_entry *oh;
 
@@ -2853,9 +3118,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
      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. */
      These flags are also kept for local symbols.  */
 #define TLS_TLS                 1      /* Any TLS reloc.  */
 #define TLS_GD          2      /* GD reloc. */
@@ -2863,8 +3126,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_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
   unsigned char tls_mask;
 
   /* The above field is also used to mark function symbols.  In which
@@ -2874,6 +3137,12 @@ struct ppc_link_hash_entry
 #define NON_GOT        256     /* local symbol plt, not stored.  */
 };
 
 #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
 /* ppc64 ELF linker hash table.  */
 
 struct ppc_link_hash_table
@@ -2936,6 +3205,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;
   /* 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;
 
   /* The size of reliplt used by got entry relocs.  */
   bfd_size_type got_reli_size;
@@ -2967,19 +3239,17 @@ struct ppc_link_hash_table
   /* Whether func_desc_adjust needs to be run over symbols.  */
   unsigned int need_func_desc_adj:1;
 
   /* 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 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 any code linked seems to be Power10.  */
+  unsigned int has_power10_relocs:1;
+
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
   /* 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
 };
 
 /* Rename some of the generic section flags to better document how they
@@ -2988,8 +3258,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 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
 
 /* Nonzero if this section has any toc or got relocs.  */
 #define has_toc_reloc sec_flg2
@@ -3181,7 +3452,7 @@ static struct bfd_link_hash_table *
 ppc64_elf_link_hash_table_create (bfd *abfd)
 {
   struct ppc_link_hash_table *htab;
 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)
 
   htab = bfd_zmalloc (amt);
   if (htab == NULL)
@@ -3257,7 +3528,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
       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;
     }
 
        return FALSE;
     }
 
@@ -3268,7 +3539,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
   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
     return FALSE;
 
   /* The part of .glink used by global entry stubs, separate so that
@@ -3276,7 +3547,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
   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)
     return FALSE;
 
   if (!info->no_ld_generated_unwind_info)
@@ -3287,14 +3558,14 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
                                                                 ".eh_frame",
                                                                 flags);
       if (htab->glink_eh_frame == NULL
                                                                 ".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
        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
     return FALSE;
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
@@ -3302,7 +3573,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
   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.  */
     return FALSE;
 
   /* Create branch lookup table for plt_branch stubs.  */
@@ -3311,7 +3582,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
   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
     return FALSE;
 
   /* Local plt entries, put in .branch_lt but a separate section for
@@ -3319,7 +3590,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
   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))
     return FALSE;
 
   if (!bfd_link_pic (info))
@@ -3330,13 +3601,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
   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
     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;
     return FALSE;
 
   return TRUE;
@@ -3408,6 +3679,37 @@ ppc_stub_name (const asection *input_section,
   return stub_name;
 }
 
   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.  */
 
 /* Look up an entry in the stub hash.  Stub entries are cached because
    creating the stub name takes a bit of time.  */
 
@@ -3452,6 +3754,13 @@ ppc_get_stub_entry (const asection *input_section,
       free (stub_name);
     }
 
       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;
 }
 
   return stub_entry;
 }
 
@@ -3532,13 +3841,13 @@ create_got_section (bfd *abfd, struct bfd_link_info *info)
 
   got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
   if (!got
 
   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
     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;
     return FALSE;
 
   ppc64_elf_tdata (abfd)->got = got;
@@ -3566,7 +3875,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)
 {
 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.  */
 }
 
 /* Merge PLT info on FROM with that on TO.  */
@@ -3613,8 +3922,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
 {
   struct ppc_link_hash_entry *edir, *eind;
 
 {
   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;
 
   edir->is_func |= eind->is_func;
   edir->is_func_descriptor |= eind->is_func_descriptor;
@@ -3640,20 +3949,20 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
     return;
 
   /* Copy over any dynamic relocs we may have on the indirect sym.  */
     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.  */
        {
          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;
 
            {
              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;
                if (q->sec == p->sec)
                  {
                    q->pc_count += p->pc_count;
@@ -3664,11 +3973,11 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
              if (q == NULL)
                pp = &p->next;
            }
              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
     }
 
   /* Copy over got entries that we may have already seen to the
@@ -3730,8 +4039,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;
 
     {
       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;
 
       if (fdh == NULL)
        return fdh;
 
@@ -3859,9 +4168,9 @@ ppc64_elf_merge_symbol (struct elf_link_hash_entry *h,
                        bfd *oldbfd ATTRIBUTE_UNUSED,
                        const asection *oldsec ATTRIBUTE_UNUSED)
 {
                        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)
   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;
 }
 
   return TRUE;
 }
 
@@ -3883,7 +4192,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.  */
   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] == '.')
     return h;
 
   if (name[0] == '.')
@@ -3897,6 +4206,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);
   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;
 }
 
   return h;
 }
 
@@ -4066,7 +4380,8 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
              Elf_Internal_Sym *isym;
              asection *s;
 
              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)
              if (isym == NULL)
                {
                  if (elf_section_data (opd)->relocs != relocs)
@@ -4172,7 +4487,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
          break;
       if (ent == NULL)
        {
          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 = bfd_alloc (abfd, amt);
          if (ent == NULL)
            return FALSE;
@@ -4204,7 +4519,7 @@ update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend)
       break;
   if (ent == NULL)
     {
       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 = bfd_alloc (abfd, amt);
       if (ent == NULL)
        return FALSE;
@@ -4229,7 +4544,8 @@ is_branch_reloc (enum elf_ppc64_reloc_type r_type)
          || r_type == R_PPC64_ADDR14
          || r_type == R_PPC64_ADDR14_BRTAKEN
          || r_type == R_PPC64_ADDR14_BRNTAKEN
          || r_type == R_PPC64_ADDR14
          || r_type == R_PPC64_ADDR14_BRTAKEN
          || r_type == R_PPC64_ADDR14_BRNTAKEN
-         || r_type == R_PPC64_PLTCALL);
+         || r_type == R_PPC64_PLTCALL
+         || r_type == R_PPC64_PLTCALL_NOTOC);
 }
 
 /* Relocs on inline plt call sequence insns prior to the call.  */
 }
 
 /* Relocs on inline plt call sequence insns prior to the call.  */
@@ -4241,7 +4557,10 @@ is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type)
          || r_type == R_PPC64_PLT16_HI
          || r_type == R_PPC64_PLT16_LO
          || r_type == R_PPC64_PLT16_LO_DS
          || r_type == R_PPC64_PLT16_HI
          || r_type == R_PPC64_PLT16_LO
          || r_type == R_PPC64_PLT16_LO_DS
-         || r_type == R_PPC64_PLTSEQ);
+         || r_type == R_PPC64_PLT_PCREL34
+         || r_type == R_PPC64_PLT_PCREL34_NOTOC
+         || r_type == R_PPC64_PLTSEQ
+         || r_type == R_PPC64_PLTSEQ_NOTOC);
 }
 
 /* Look through the relocs for a section during the first phase, and
 }
 
 /* Look through the relocs for a section during the first phase, and
@@ -4264,15 +4583,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   if (bfd_link_relocatable (info))
     return TRUE;
 
   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);
   BFD_ASSERT (is_ppc64_elf (abfd));
 
   htab = ppc_hash_table (info);
@@ -4309,7 +4619,58 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            sec->has_toc_reloc = 1;
        }
 
            sec->has_toc_reloc = 1;
        }
 
-      tls_type = 0;
+      r_type = ELF64_R_TYPE (rel->r_info);
+      switch (r_type)
+       {
+       case R_PPC64_D34:
+       case R_PPC64_D34_LO:
+       case R_PPC64_D34_HI30:
+       case R_PPC64_D34_HA30:
+       case R_PPC64_D28:
+       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->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)
        {
       ifunc = NULL;
       if (h != NULL)
        {
@@ -4321,7 +4682,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
       else
        {
        }
       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;
                                                          abfd, r_symndx);
          if (isym == NULL)
            return FALSE;
@@ -4336,7 +4697,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
        }
 
            }
        }
 
-      r_type = ELF64_R_TYPE (rel->r_info);
+      tls_type = 0;
       switch (r_type)
        {
        case R_PPC64_TLSGD:
       switch (r_type)
        {
        case R_PPC64_TLSGD:
@@ -4344,7 +4705,7 @@ 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)
          /* 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,
                                        rel->r_addend,
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                        rel->r_addend,
@@ -4357,6 +4718,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_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;
 
          tls_type = TLS_TLS | TLS_LD;
          goto dogottls;
 
@@ -4364,6 +4726,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_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;
 
          tls_type = TLS_TLS | TLS_GD;
          goto dogottls;
 
@@ -4371,6 +4734,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_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;
          if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          tls_type = TLS_TLS | TLS_TPREL;
@@ -4380,17 +4744,20 @@ 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_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;
          tls_type = TLS_TLS | TLS_DTPREL;
        dogottls:
          sec->has_tls_reloc = 1;
-         /* Fall through */
+         goto dogot;
 
        case R_PPC64_GOT16:
 
        case R_PPC64_GOT16:
-       case R_PPC64_GOT16_DS:
-       case R_PPC64_GOT16_HA:
-       case R_PPC64_GOT16_HI:
        case R_PPC64_GOT16_LO:
        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_GOT16_LO_DS:
+       case R_PPC64_GOT_PCREL34:
+       dogot:
          /* This symbol requires a global offset table entry.  */
          sec->has_toc_reloc = 1;
          if (r_type == R_PPC64_GOT_TLSLD16
          /* This symbol requires a global offset table entry.  */
          sec->has_toc_reloc = 1;
          if (r_type == R_PPC64_GOT_TLSLD16
@@ -4413,7 +4780,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              struct ppc_link_hash_entry *eh;
              struct got_entry *ent;
 
              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 == rel->r_addend
                    && ent->owner == abfd
              for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
                if (ent->addend == rel->r_addend
                    && ent->owner == abfd
@@ -4421,7 +4788,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  break;
              if (ent == NULL)
                {
                  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 = bfd_alloc (abfd, amt);
                  if (ent == NULL)
                    return FALSE;
@@ -4441,20 +4808,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                        rel->r_addend, tls_type))
              return FALSE;
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                        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, rel->r_addend))
-               return FALSE;
-           }
          break;
 
        case R_PPC64_PLT16_HA:
        case R_PPC64_PLT16_HI:
        case R_PPC64_PLT16_LO:
        case R_PPC64_PLT16_LO_DS:
          break;
 
        case R_PPC64_PLT16_HA:
        case R_PPC64_PLT16_HI:
        case R_PPC64_PLT16_LO:
        case R_PPC64_PLT16_LO_DS:
+       case R_PPC64_PLT_PCREL34:
+       case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_PLT32:
        case R_PPC64_PLT64:
          /* This symbol requires a procedure linkage table entry.  */
        case R_PPC64_PLT32:
        case R_PPC64_PLT64:
          /* This symbol requires a procedure linkage table entry.  */
@@ -4464,8 +4825,8 @@ 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')
              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 = &h->plt.plist;
            }
          if (plt_list == NULL)
@@ -4510,6 +4871,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_REL16_HIGHERA:
        case R_PPC64_REL16_HIGHEST:
        case R_PPC64_REL16_HIGHESTA:
        case R_PPC64_REL16_HIGHERA:
        case R_PPC64_REL16_HIGHEST:
        case R_PPC64_REL16_HIGHESTA:
+       case R_PPC64_REL16_HIGHER34:
+       case R_PPC64_REL16_HIGHERA34:
+       case R_PPC64_REL16_HIGHEST34:
+       case R_PPC64_REL16_HIGHESTA34:
        case R_PPC64_REL16DX_HA:
          break;
 
        case R_PPC64_REL16DX_HA:
          break;
 
@@ -4539,6 +4904,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;
        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.  */
          break;
 
          /* Marker reloc.  */
@@ -4555,9 +4930,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* This relocation describes which C++ vtable entries are actually
             used.  Record for later use during GC.  */
        case R_PPC64_GNU_VTENTRY:
          /* This relocation describes which C++ vtable entries are actually
             used.  Record for later use during GC.  */
        case R_PPC64_GNU_VTENTRY:
-         BFD_ASSERT (h != NULL);
-         if (h != NULL
-             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
            return FALSE;
          break;
 
            return FALSE;
          break;
 
@@ -4580,7 +4953,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              {
                Elf_Internal_Sym *isym;
 
              {
                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;
                                              abfd, r_symndx);
                if (isym == NULL)
                  return FALSE;
@@ -4594,6 +4967,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          goto rel24;
 
        case R_PPC64_PLTCALL:
          goto rel24;
 
        case R_PPC64_PLTCALL:
+       case R_PPC64_PLTCALL_NOTOC:
          ppc64_elf_section_data (sec)->has_pltcall = 1;
          /* Fall through.  */
 
          ppc64_elf_section_data (sec)->has_pltcall = 1;
          /* Fall through.  */
 
@@ -4606,7 +4980,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')
              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)
                {
 
              if (h == tga || h == dottga)
                {
@@ -4619,7 +4993,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    ;
                  else
                    /* Mark this section as having an old-style call.  */
                    ;
                  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;
            }
                }
              plt_list = &h->plt.plist;
            }
@@ -4664,11 +5038,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        dotlstoc:
          sec->has_tls_reloc = 1;
          if (h != NULL)
        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,
                                        rel->r_addend, tls_type))
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                        rel->r_addend, tls_type))
@@ -4715,6 +5085,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_TPREL16_HIGHERA:
        case R_PPC64_TPREL16_HIGHEST:
        case R_PPC64_TPREL16_HIGHESTA:
        case R_PPC64_TPREL16_HIGHERA:
        case R_PPC64_TPREL16_HIGHEST:
        case R_PPC64_TPREL16_HIGHESTA:
+       case R_PPC64_TPREL34:
          if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
          if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
@@ -4725,7 +5096,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)
              && 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.  */
 
            }
          /* Fall through.  */
 
@@ -4741,12 +5112,21 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_ADDR16_HIGHESTA:
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_ADDR16_HIGHESTA:
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
+       case R_PPC64_D34:
+       case R_PPC64_D34_LO:
+       case R_PPC64_D34_HI30:
+       case R_PPC64_D34_HA30:
+       case R_PPC64_ADDR16_HIGHER34:
+       case R_PPC64_ADDR16_HIGHERA34:
+       case R_PPC64_ADDR16_HIGHEST34:
+       case R_PPC64_ADDR16_HIGHESTA34:
+       case R_PPC64_D28:
          if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1
              && rel->r_addend == 0)
            {
              /* We may need a .plt entry if this reloc refers to a
                 function in a shared lib.  */
          if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1
              && rel->r_addend == 0)
            {
              /* We may need a .plt entry if this reloc refers to a
                 function in a shared lib.  */
-             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+             if (!update_plt_info (abfd, &h->plt.plist, 0))
                return FALSE;
              h->pointer_equality_needed = 1;
            }
                return FALSE;
              h->pointer_equality_needed = 1;
            }
@@ -4760,7 +5140,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
        case R_PPC64_TOC:
        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;
 
            /* We may need a copy reloc.  */
            h->non_got_ref = 1;
 
@@ -4790,17 +5170,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:
             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))
            {
              || (!bfd_link_pic (info)
                  && ifunc != NULL))
            {
@@ -4823,7 +5200,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  struct elf_dyn_relocs *p;
                  struct elf_dyn_relocs **head;
 
                  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)
                    {
                  p = *head;
                  if (p == NULL || p->sec != sec)
                    {
@@ -4852,7 +5229,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  void *vpp;
                  Elf_Internal_Sym *isym;
 
                  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;
                                                abfd, r_symndx);
                  if (isym == NULL)
                    return FALSE;
@@ -5204,7 +5581,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
          && 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);
 }
          && is_ppc64_elf (h->root.u.def.section->owner)
          && abiversion (h->root.u.def.section->owner) >= 2);
 }
@@ -5253,6 +5630,27 @@ defined_func_desc (struct ppc_link_hash_entry *fh)
   return NULL;
 }
 
   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 == &htab->tls_get_addr_fd->elf || h == &htab->tga_desc_fd->elf
+         || h == &htab->tls_get_addr->elf || h == &htab->tga_desc->elf);
+}
+
 static bfd_boolean func_desc_adjust (struct elf_link_hash_entry *, void *);
 
 /* Garbage collect sections, after first dealing with dot-symbols.  */
 static bfd_boolean func_desc_adjust (struct elf_link_hash_entry *, void *);
 
 /* Garbage collect sections, after first dealing with dot-symbols.  */
@@ -5286,8 +5684,8 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
       struct ppc_link_hash_entry *eh, *fh;
       asection *sec;
 
       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
       if (eh == NULL)
        continue;
       if (eh->elf.root.type != bfd_link_hash_defined
@@ -5319,7 +5717,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;
 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;
 
   struct ppc_link_hash_entry *fdh;
   struct bfd_elf_dynamic_list *d = info->dynamic_list;
 
@@ -5403,7 +5801,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
            {
            case bfd_link_hash_defined:
            case bfd_link_hash_defweak:
            {
            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)
                {
              fdh = defined_func_desc (eh);
              if (fdh != NULL)
                {
@@ -5499,8 +5897,8 @@ sfpr_define (struct bfd_link_info *info,
 
       sym[len + 0] = i / 10 + '0';
       sym[len + 1] = i % 10 + '0';
 
       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
       if (stub_sec != NULL)
        {
          if (h != NULL
@@ -5513,9 +5911,7 @@ sfpr_define (struct bfd_link_info *info,
              s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE);
              if (s == NULL)
                return FALSE;
              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;
                {
                  s->root.type = bfd_link_hash_defined;
                  s->root.u.def.section = stub_sec;
@@ -5729,32 +6125,110 @@ restvr_tail (bfd *abfd, bfd_byte *p, int r)
   return p + 4;
 }
 
   return p + 4;
 }
 
-/* Called via elf_link_hash_traverse to transfer dynamic linking
-   information on function code symbol entries to their corresponding
-   function descriptor symbol entries.  */
+#define STDU_R1_0R1    0xf8210001
+#define ADDI_R1_R1     0x38210000
 
 
-static bfd_boolean
-func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
+/* 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)
 {
 {
-  struct bfd_link_info *info;
-  struct ppc_link_hash_table *htab;
-  struct ppc_link_hash_entry *fh;
-  struct ppc_link_hash_entry *fdh;
-  bfd_boolean force_local;
+  unsigned int i;
 
 
-  fh = (struct ppc_link_hash_entry *) h;
-  if (fh->elf.root.type == bfd_link_hash_indirect)
-    return TRUE;
+  bfd_put_32 (obfd, MFLR_R0, p);
+  p += 4;
+  bfd_put_32 (obfd, STD_R0_0R1 + 16, p);
+  p += 4;
 
 
-  if (!fh->is_func)
-    return TRUE;
+  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;
+}
 
 
-  if (fh->elf.root.root.string[0] != '.'
-      || fh->elf.root.root.string[1] == '\0')
-    return TRUE;
+/* Emit epilogue of wrapper preserving regs around a call to
+   __tls_get_addr_opt.  */
 
 
-  info = inf;
-  htab = ppc_hash_table (info);
+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.  */
+
+static bfd_boolean
+func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
+{
+  struct bfd_link_info *info;
+  struct ppc_link_hash_table *htab;
+  struct ppc_link_hash_entry *fh;
+  struct ppc_link_hash_entry *fdh;
+  bfd_boolean force_local;
+
+  fh = ppc_elf_hash_entry (h);
+  if (fh->elf.root.type == bfd_link_hash_indirect)
+    return TRUE;
+
+  if (!fh->is_func)
+    return TRUE;
+
+  if (fh->elf.root.root.string[0] != '.'
+      || fh->elf.root.root.string[1] == '\0')
+    return TRUE;
+
+  info = inf;
+  htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
   if (htab == NULL)
     return FALSE;
 
@@ -5920,25 +6394,6 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
   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.  */
 /* 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.  */
@@ -5946,14 +6401,12 @@ readonly_dynrelocs (struct elf_link_hash_entry *h)
 static bfd_boolean
 alias_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
     {
   do
     {
-      if (readonly_dynrelocs (&eh->elf))
+      if (_bfd_elf_readonly_dynrelocs (&eh->elf))
        return TRUE;
        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);
 
     }
   while (eh != NULL && &eh->elf != h);
 
@@ -5967,7 +6420,7 @@ pc_dynrelocs (struct ppc_link_hash_entry *eh)
 {
   struct elf_dyn_relocs *p;
 
 {
   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;
     if (p->pc_count != 0)
       return TRUE;
   return FALSE;
@@ -6015,7 +6468,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       || h->type == STT_GNU_IFUNC
       || h->needs_plt)
     {
       || 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
                           || SYMBOL_CALLS_LOCAL (info, h)
                           || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
       /* Discard dyn_relocs when non-pic if we've decided that a
@@ -6030,7 +6483,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       if (!bfd_link_pic (info)
          && h->type != STT_GNU_IFUNC
          && local)
       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.  */
 
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
@@ -6042,7 +6495,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          || (h->type != STT_GNU_IFUNC
              && local
              && (htab->can_convert_all_inline_plt
          || (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;
                      & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)))
        {
          h->plt.plist = NULL;
@@ -6060,7 +6513,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))
            {
             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
                {
                  h->pointer_equality_needed = 0;
                  /* If we haven't seen a branch reloc and the symbol
@@ -6071,14 +6524,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.  */
              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
            }
 
          /* 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.  */
        {
          /* If we haven't seen a branch reloc and the symbol isn't an
             ifunc then we don't need a plt entry.  */
@@ -6101,7 +6554,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)
       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;
     }
 
       return TRUE;
     }
 
@@ -6109,7 +6562,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.  */
      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
     return TRUE;
 
   /* If there are no references to this symbol that do not use the
@@ -6125,7 +6578,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.  */
 
       /* 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
 
       /* Protected variables do not work with .dynbss.  The copy in
         .dynbss won't be used by the shared library with the protected
@@ -6134,13 +6589,23 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       || h->protected_def)
     return TRUE;
 
       || 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"),
       info->callbacks->einfo
        (_("%P: copy reloc against `%pT' requires lazy plt linking; "
           "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"),
@@ -6179,7 +6644,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
     }
 
   /* We no longer want dyn_relocs.  */
     }
 
   /* 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);
 }
 
   return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
@@ -6193,7 +6658,10 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
   struct ppc_link_hash_entry *eh;
   _bfd_elf_link_hash_hide_symbol (info, h, force_local);
 
   struct ppc_link_hash_entry *eh;
   _bfd_elf_link_hash_hide_symbol (info, h, force_local);
 
-  eh = (struct ppc_link_hash_entry *) h;
+  if (ppc_hash_table (info) == NULL)
+    return;
+
+  eh = ppc_elf_hash_entry (h);
   if (eh->is_func_descriptor)
     {
       struct ppc_link_hash_entry *fh = eh->oh;
   if (eh->is_func_descriptor)
     {
       struct ppc_link_hash_entry *fh = eh->oh;
@@ -6216,8 +6684,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
          p = eh->elf.root.root.string - 1;
          save = *p;
          *(char *) p = '.';
          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
          *(char *) p = save;
 
          /* Unfortunately, if it so happens that the string we were
@@ -6230,8 +6698,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 == '.')
              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)
            {
            }
          if (fh != NULL)
            {
@@ -6279,12 +6747,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
        }
 
       if (tls_maskp != NULL)
        }
 
       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
     {
     }
   else
     {
@@ -6455,7 +6918,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
       && 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;
 
   if (eh->adjust_done)
     return TRUE;
 
@@ -6510,6 +6973,16 @@ dec_dynrel_count (bfd_vma r_info,
     default:
       return TRUE;
 
     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:
     case R_PPC64_TPREL16:
     case R_PPC64_TPREL16_LO:
     case R_PPC64_TPREL16_HI:
@@ -6523,6 +6996,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_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:
     case R_PPC64_DTPMOD64:
     case R_PPC64_DTPREL64:
     case R_PPC64_ADDR64:
@@ -6550,6 +7024,15 @@ dec_dynrel_count (bfd_vma r_info,
     case R_PPC64_UADDR32:
     case R_PPC64_UADDR64:
     case R_PPC64_TOC:
     case R_PPC64_UADDR32:
     case R_PPC64_UADDR64:
     case R_PPC64_TOC:
+    case R_PPC64_D34:
+    case R_PPC64_D34_LO:
+    case R_PPC64_D34_HI30:
+    case R_PPC64_D34_HA30:
+    case R_PPC64_ADDR16_HIGHER34:
+    case R_PPC64_ADDR16_HIGHERA34:
+    case R_PPC64_ADDR16_HIGHEST34:
+    case R_PPC64_ADDR16_HIGHESTA34:
+    case R_PPC64_D28:
       break;
     }
 
       break;
     }
 
@@ -6563,17 +7046,18 @@ dec_dynrel_count (bfd_vma r_info,
        return FALSE;
     }
 
        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;
     ;
   else
     return TRUE;
@@ -6582,7 +7066,7 @@ dec_dynrel_count (bfd_vma r_info,
     {
       struct elf_dyn_relocs *p;
       struct elf_dyn_relocs **pp;
     {
       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
 
       /* elf_gc_sweep may have already removed all dyn relocs associated
         with local syms for a given section.  Also, symbol flags are
@@ -6835,11 +7319,9 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
              bfd_byte *loc;
              if (!bfd_malloc_and_get_section (ibfd, sec, &loc))
                {
              bfd_byte *loc;
              if (!bfd_malloc_and_get_section (ibfd, sec, &loc))
                {
-                 if (loc != NULL)
-                   free (loc);
+                 free (loc);
                error_ret:
                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);
                    free (local_syms);
                  if (elf_section_data (sec)->relocs != relstart)
                    free (relstart);
@@ -6899,7 +7381,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
              if (h != NULL
                  && h->root.root.string[0] == '.')
                {
              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);
                  if (fdh != NULL)
                    {
                      fdh = ppc_follow_link (fdh);
@@ -7145,7 +7627,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
              return FALSE;
 
            relend = relstart + sec->reloc_count;
              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;
              {
                enum elf_ppc64_reloc_type r_type;
                unsigned long r_symndx;
@@ -7155,7 +7637,8 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
                unsigned char *tls_maskp;
 
                r_type = ELF64_R_TYPE (rel->r_info);
                unsigned char *tls_maskp;
 
                r_type = ELF64_R_TYPE (rel->r_info);
-               if (r_type != R_PPC64_PLTCALL)
+               if (r_type != R_PPC64_PLTCALL
+                   && r_type != R_PPC64_PLTCALL_NOTOC)
                  continue;
 
                r_symndx = ELF64_R_SYM (rel->r_info);
                  continue;
 
                r_symndx = ELF64_R_SYM (rel->r_info);
@@ -7164,8 +7647,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
                  {
                    if (elf_section_data (sec)->relocs != relstart)
                      free (relstart);
                  {
                    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;
                  }
                      free (local_syms);
                    return FALSE;
                  }
@@ -7183,7 +7665,11 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
                    from = (rel->r_offset
                            + sec->output_offset
                            + sec->output_section->vma);
                    from = (rel->r_offset
                            + sec->output_offset
                            + sec->output_section->vma);
-                   if (to - from + limit < 2 * limit)
+                   if (to - from + limit < 2 * limit
+                       && !(r_type == R_PPC64_PLTCALL_NOTOC
+                            && (((h ? h->other : sym->st_other)
+                                 & STO_PPC64_LOCAL_MASK)
+                                > 1 << STO_PPC64_LOCAL_BIT)))
                      *tls_maskp &= ~PLT_KEEP;
                  }
              }
                      *tls_maskp &= ~PLT_KEEP;
                  }
              }
@@ -7210,6 +7696,7 @@ asection *
 ppc64_elf_tls_setup (struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
 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)
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
@@ -7246,18 +7733,29 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
       (_("warning: --plt-localentry is especially dangerous without "
         "ld.so support to detect ABI violations"));
 
       (_("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.  */
   /* 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)
     {
   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);
 
       opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt",
                                  FALSE, FALSE, TRUE);
@@ -7273,24 +7771,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.  */
             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)))
-           {
-             struct plt_entry *ent;
-
-             for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next)
-               if (ent->plt.refcount > 0)
-                 break;
+         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 = NULL;
+
+             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)
                {
              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)
                    {
                  opt_fd->mark = 1;
                  if (opt_fd->dynindx != -1)
                    {
@@ -7301,25 +7824,50 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
                      if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
                        return NULL;
                    }
                      if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
                        return NULL;
                    }
-                 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 = &htab->tls_get_addr->elf;
+                     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;
+                       }
                    }
                }
            }
                    }
                }
            }
@@ -7327,17 +7875,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;
     }
       else if (htab->params->tls_get_addr_opt < 0)
        htab->params->tls_get_addr_opt = 0;
     }
+
+  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 _bfd_elf_tls_setup (info->output_bfd, info);
 }
 
 /* Return TRUE iff REL is a branch reloc with a global symbol matching
   return _bfd_elf_tls_setup (info->output_bfd, info);
 }
 
 /* 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,
 
 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);
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
   enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
@@ -7350,7 +7906,8 @@ branch_reloc_hash_match (const bfd *ibfd,
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       h = elf_follow_link (h);
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       h = elf_follow_link (h);
-      if (h == &hash1->elf || h == &hash2->elf)
+      if (h == &hash1->elf || h == &hash2->elf
+         || h == &hash3->elf || h == &hash4->elf)
        return TRUE;
     }
   return FALSE;
        return TRUE;
     }
   return FALSE;
@@ -7417,7 +7974,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  Elf_Internal_Sym *sym;
                  asection *sym_sec;
                  unsigned char *tls_mask;
                  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;
                  bfd_vma value;
                  bfd_boolean ok_tprel, is_local;
                  long toc_ref_index = 0;
@@ -7431,11 +7988,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    err_free_rel:
                      if (elf_section_data (sec)->relocs != relstart)
                        free (relstart);
                    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;
                    }
                        free (locsyms);
                      return ret;
                    }
@@ -7459,11 +8014,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    value = sym->st_value;
 
                  ok_tprel = FALSE;
                    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;
                      if (h != NULL
                          && h->root.type == bfd_link_hash_undefweak)
                        ok_tprel = TRUE;
@@ -7472,9 +8025,14 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        {
                          value += sym_sec->output_offset;
                          value += sym_sec->output_section->vma;
                        {
                          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;
                        }
                    }
 
                        }
                    }
 
@@ -7486,10 +8044,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
                     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 != 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))
                    {
                      && !found_tls_get_addr_arg
                      && is_branch_reloc (r_type))
                    {
@@ -7505,6 +8062,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_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.  */
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
                      /* Fall through.  */
@@ -7525,6 +8083,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_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. */
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
                      /* Fall through. */
@@ -7536,11 +8095,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        tls_set = 0;
                      else
                        /* GD -> IE */
                        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;
 
                      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:
                    case R_PPC64_GOT_TPREL16_DS:
                    case R_PPC64_GOT_TPREL16_LO_DS:
                    case R_PPC64_GOT_TPREL16_HI:
@@ -7555,14 +8115,19 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                      continue;
 
                        }
                      continue;
 
-                   case R_PPC64_TLSGD:
                    case R_PPC64_TLSLD:
                    case R_PPC64_TLSLD:
+                     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)
                      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)
-                                 != R_PPC64_PLTSEQ))
+                                 != R_PPC64_PLTSEQ)
+                             && (ELF64_R_TYPE (rel[1].r_info)
+                                 != R_PPC64_PLTSEQ_NOTOC))
                            {
                              r_symndx = ELF64_R_SYM (rel[1].r_info);
                              if (!get_sym_h (&h, NULL, NULL, NULL, &locsyms,
                            {
                              r_symndx = ELF64_R_SYM (rel[1].r_info);
                              if (!get_sym_h (&h, NULL, NULL, NULL, &locsyms,
@@ -7661,7 +8226,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;
                          else
                            /* GD -> IE */
-                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_TPRELGD;
+                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_GDIE;
                          tls_clear = TLS_GD;
                        }
                      else
                          tls_clear = TLS_GD;
                        }
                      else
@@ -7682,13 +8247,15 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  if (pass == 0)
                    {
                      if (!expecting_tls_get_addr
                  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,
                        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,
-                                                     htab->tls_get_addr_fd))
+                                                     htab->tga_desc))
                        {
                          if (expecting_tls_get_addr == 2)
                            {
                        {
                          if (expecting_tls_get_addr == 2)
                            {
@@ -7734,24 +8301,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
                     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;
 
                      && ((*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;
 
                    {
                      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;
 
                        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)
                             ent != NULL;
                             ent = ent->next)
                          if (ent->addend == 0)
@@ -7806,7 +8387,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                    }
 
                        }
                    }
 
-                 *tls_mask |= tls_set;
+                 *tls_mask |= tls_set & 0xff;
                  *tls_mask &= ~tls_clear;
                }
 
                  *tls_mask &= ~tls_clear;
                }
 
@@ -7824,8 +8405,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
          }
       }
 
          }
       }
 
-  if (toc_ref != NULL)
-    free (toc_ref);
+  free (toc_ref);
   htab->do_tls_opt = 1;
   return TRUE;
 }
   htab->do_tls_opt = 1;
   return TRUE;
 }
@@ -7856,7 +8436,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
       && 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;
 
   if (eh->adjust_done)
     return TRUE;
 
@@ -7892,37 +8472,174 @@ 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)
 {
 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)
              /* 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 & 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)
              /* 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));
 }
 
              && (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,off(ra)
+   or
+     pla ra,symbol@pcrel
+     load/store rt,off(ra)
+   may be translated to
+     pload/pstore rt,symbol+off@pcrel
+     nop.
+   This function returns true if the optimization is possible, placing
+   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, bfd_signed_vma *poff)
+{
+  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) != ((insn1 >> 21) & 31))
+    return FALSE;
+
+  switch ((insn2 >> 26) & 63)
+    {
+    default:
+      return FALSE;
+
+    case 32: /* lwz */
+    case 34: /* lbz */
+    case 36: /* stw */
+    case 38: /* stb */
+    case 40: /* lhz */
+    case 42: /* lha */
+    case 44: /* sth */
+    case 48: /* lfs */
+    case 50: /* lfd */
+    case 52: /* stfs */
+    case 54: /* stfd */
+      /* These are the PMLS cases, where we just need to tack a prefix
+        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 & 1) != 0)
+       return FALSE;
+      insn1 = ((1ULL << 58) | (1ULL << 52)
+              | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26)
+              | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
+      break;
+
+    case 57: /* lxsd, lxssp */
+      if ((insn2 & 3) < 2)
+       return FALSE;
+      insn1 = ((1ULL << 58) | (1ULL << 52)
+              | ((40ULL | (insn2 & 3)) << 26)
+              | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
+      break;
+
+    case 61: /* stxsd, stxssp, lxv, stxv  */
+      if ((insn2 & 3) == 0)
+       return FALSE;
+      else if ((insn2 & 3) >= 2)
+       {
+         insn1 = ((1ULL << 58) | (1ULL << 52)
+                  | ((44ULL | (insn2 & 3)) << 26)
+                  | (insn2 & (31ULL << 21)));
+         off = insn2 & 0xfffc;
+       }
+      else
+       {
+         insn1 = ((1ULL << 58) | (1ULL << 52)
+                  | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26)
+                  | (insn2 & (31ULL << 21)));
+         off = insn2 & 0xfff0;
+       }
+      break;
+
+    case 56: /* lq */
+      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 & 1) != 0)
+       return FALSE;
+      insn1 = ((1ULL << 58) | (1ULL << 52)
+              | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26)
+              | (insn2 & (31ULL << 21)));
+      off = insn2 & 0xfffc;
+      break;
+    }
+
+  *pinsn1 = insn1;
+  *pinsn2 = (uint64_t) NOP << 32;
+  *poff = (off ^ 0x8000) - 0x8000;
+  return TRUE;
+}
+
 /* Examine all relocs referencing .toc sections in order to remove
    unused .toc entries.  */
 
 /* Examine all relocs referencing .toc sections in order to remove
    unused .toc entries.  */
 
@@ -8128,18 +8845,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       if (used == NULL)
        {
        error_ret:
       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
            free (local_syms);
          if (sec != NULL
-             && relstart != NULL
              && elf_section_data (sec)->relocs != relstart)
            free (relstart);
              && 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);
            free (toc_relocs);
-         if (skip != NULL)
-           free (skip);
+         free (skip);
          return FALSE;
        }
 
          return FALSE;
        }
 
@@ -8182,65 +8895,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  struct elf_link_hash_entry *h;
                  Elf_Internal_Sym *sym;
                  bfd_vma val;
                  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);
 
                  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:
                  switch (r_type)
                    {
                    case R_PPC64_TOC16:
@@ -8563,8 +9219,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              rel_hdr->sh_size = toc->reloc_count * sz;
            }
        }
              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
        free (toc_relocs);
 
       if (local_syms != NULL
@@ -8578,52 +9233,291 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       free (skip);
     }
 
       free (skip);
     }
 
-  return TRUE;
-}
+  /* Look for cases where we can change an indirect GOT access to
+     a GOT relative or PC relative access, possibly reducing the
+     number of GOT entries.  */
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *sec;
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Sym *local_syms;
+      Elf_Internal_Rela *relstart, *rel;
+      bfd_vma got;
 
 
-/* Return true iff input section I references the TOC using
-   instructions limited to +/-32k offsets.  */
+      if (!is_ppc64_elf (ibfd))
+       continue;
 
 
-bfd_boolean
-ppc64_elf_has_small_toc_reloc (asection *i)
-{
-  return (is_ppc64_elf (i->owner)
-         && ppc64_elf_tdata (i->owner)->has_small_toc_reloc);
-}
+      if (!ppc64_elf_tdata (ibfd)->has_optrel)
+       continue;
 
 
-/* Allocate space for one GOT entry.  */
+      sec = ppc64_elf_tdata (ibfd)->got;
+      got = 0;
+      if (sec != NULL)
+       got = sec->output_section->vma + sec->output_offset + 0x8000;
 
 
-static void
-allocate_got (struct elf_link_hash_entry *h,
-             struct bfd_link_info *info,
-             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;
-  int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)
-                ? 16 : 8);
-  int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD
-                 ? 2 : 1) * sizeof (Elf64_External_Rela);
-  asection *got = ppc64_elf_tdata (gent->owner)->got;
+      local_syms = NULL;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
 
 
-  gent->got.offset = got->size;
-  got->size += entsize;
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       {
+         if (sec->reloc_count == 0
+             || !ppc64_elf_section_data (sec)->has_optrel
+             || discarded_section (sec))
+           continue;
 
 
-  if (h->type == STT_GNU_IFUNC)
-    {
-      htab->elf.irelplt->size += rentsize;
-      htab->got_reli_size += rentsize;
-    }
-  else if (((bfd_link_pic (info)
-            && !((gent->tls_type & TLS_TPREL) != 0
-                 && bfd_link_executable (info)
-                 && SYMBOL_REFERENCES_LOCAL (info, h)))
-           || (htab->elf.dynamic_sections_created
-               && h->dynindx != -1
-               && !SYMBOL_REFERENCES_LOCAL (info, h)))
-          && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
-    {
-      asection *relgot = ppc64_elf_tdata (gent->owner)->relgot;
+         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                               info->keep_memory);
+         if (relstart == NULL)
+           {
+           got_error_ret:
+             if (symtab_hdr->contents != (unsigned char *) local_syms)
+               free (local_syms);
+             if (sec != NULL
+                 && elf_section_data (sec)->relocs != relstart)
+               free (relstart);
+             return FALSE;
+           }
+
+         for (rel = relstart; rel < relstart + sec->reloc_count; ++rel)
+           {
+             enum elf_ppc64_reloc_type r_type;
+             unsigned long r_symndx;
+             Elf_Internal_Sym *sym;
+             asection *sym_sec;
+             struct elf_link_hash_entry *h;
+             struct got_entry *ent;
+             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:
+                 insn_check = no_check;
+                 break;
+
+               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:
+               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:
+                 break;
+               }
+
+             r_symndx = ELF64_R_SYM (rel->r_info);
+             if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                             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;
+
+             if (h != NULL)
+               val = h->root.u.def.value;
+             else
+               val = sym->st_value;
+             val += rel->r_addend;
+             val += sym_sec->output_section->vma + sym_sec->output_offset;
+
+/* 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_HA:
+                 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 & ((0x3fu << 26) | 0x1f << 16))
+                      != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+                   continue;
+                 break;
+
+               case R_PPC64_GOT16_LO_DS:
+                 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 & (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 + LIMIT_ADJUST (1ULL << 33)
+                     >= LIMIT_ADJUST (1ULL << 34))
+                   continue;
+                 if (!bfd_get_section_contents (ibfd, sec, buf,
+                                                rel->r_offset & ~3, 8))
+                   goto got_error_ret;
+                 insn = bfd_get_32 (ibfd, buf);
+                 if ((insn & (-1u << 18)) != ((1u << 26) | (1u << 20)))
+                   continue;
+                 insn = bfd_get_32 (ibfd, buf + 4);
+                 if ((insn & (0x3fu << 26)) != 57u << 26)
+                   continue;
+                 break;
+               }
+#undef LIMIT_ADJUST
+
+             if (h != NULL)
+               ent = h->got.glist;
+             else
+               {
+                 struct got_entry **local_got_ents = elf_local_got_ents (ibfd);
+                 ent = local_got_ents[r_symndx];
+               }
+             for (; ent != NULL; ent = ent->next)
+               if (ent->addend == rel->r_addend
+                   && ent->owner == ibfd
+                   && ent->tls_type == 0)
+                 break;
+             BFD_ASSERT (ent && ent->got.refcount > 0);
+             ent->got.refcount -= 1;
+           }
+
+         if (elf_section_data (sec)->relocs != relstart)
+           free (relstart);
+       }
+
+      if (local_syms != NULL
+         && symtab_hdr->contents != (unsigned char *) local_syms)
+       {
+         if (!info->keep_memory)
+           free (local_syms);
+         else
+           symtab_hdr->contents = (unsigned char *) local_syms;
+       }
+    }
+
+  return TRUE;
+}
+
+/* Return true iff input section I references the TOC using
+   instructions limited to +/-32k offsets.  */
+
+bfd_boolean
+ppc64_elf_has_small_toc_reloc (asection *i)
+{
+  return (is_ppc64_elf (i->owner)
+         && ppc64_elf_tdata (i->owner)->has_small_toc_reloc);
+}
+
+/* Allocate space for one GOT entry.  */
+
+static void
+allocate_got (struct elf_link_hash_entry *h,
+             struct bfd_link_info *info,
+             struct got_entry *gent)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  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
+                 ? 2 : 1) * sizeof (Elf64_External_Rela);
+  asection *got = ppc64_elf_tdata (gent->owner)->got;
+
+  gent->got.offset = got->size;
+  got->size += entsize;
+
+  if (h->type == STT_GNU_IFUNC)
+    {
+      htab->elf.irelplt->size += rentsize;
+      htab->got_reli_size += rentsize;
+    }
+  else if (((bfd_link_pic (info)
+            && !(gent->tls_type != 0
+                 && bfd_link_executable (info)
+                 && SYMBOL_REFERENCES_LOCAL (info, h)))
+           || (htab->elf.dynamic_sections_created
+               && h->dynindx != -1
+               && !SYMBOL_REFERENCES_LOCAL (info, h)))
+          && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
+    {
+      asection *relgot = ppc64_elf_tdata (gent->owner)->relgot;
       relgot->size += rentsize;
     }
 }
       relgot->size += rentsize;
     }
 }
@@ -8687,10 +9581,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (htab == NULL)
     return FALSE;
 
   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.  */
   /* 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)
     for (gent = h->got.glist; gent != NULL; gent = gent->next)
       if (gent->got.refcount > 0
          && (gent->tls_type & TLS_GD) != 0)
@@ -8721,7 +9615,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     if (gent->got.refcount > 0)
       {
        if ((gent->tls_type & TLS_LD) != 0
     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;
          {
            ppc64_tlsld_got (gent->owner)->got.refcount += 1;
            *pgent = gent->next;
@@ -8738,7 +9632,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)
       {
   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;
 
        if (!ensure_undef_dynamic (info, h))
          return FALSE;
 
@@ -8752,19 +9647,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)
      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)
 
   /* 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))
 
   /* 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;
 
     {
       struct elf_dyn_relocs *p, **pp;
 
@@ -8773,7 +9668,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.  */
         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
       if (bfd_link_pic (info))
        {
          /* Relocs that use pc_count are those that appear on a call
@@ -8785,7 +9679,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             avoid writing weird assembly.  */
          if (SYMBOL_CALLS_LOCAL (info, h))
            {
             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;
                {
                  p->count -= p->pc_count;
                  p->pc_count = 0;
@@ -8796,35 +9690,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;
            }
        }
              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))
            {
          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;
 
              if (!ensure_undef_dynamic (info, h))
                return FALSE;
 
+             /* But if that didn't work out, discard dynamic relocs.  */
              if (h->dynindx == -1)
              if (h->dynindx == -1)
-               eh->dyn_relocs = NULL;
+               h->dyn_relocs = NULL;
            }
          else
            }
          else
-           eh->dyn_relocs = NULL;
+           h->dyn_relocs = NULL;
        }
 
       /* Finally, allocate space.  */
        }
 
       /* 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)
        {
          asection *sreloc = elf_section_data (p->sec)->sreloc;
          if (eh->elf.type == STT_GNU_IFUNC)
@@ -8845,7 +9742,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
          && 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;
              & (TLS_TLS | PLT_KEEP)) == PLT_KEEP))
     {
       struct plt_entry *pent;
@@ -8925,6 +9822,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 #define PPC_LO(v) ((v) & 0xffff)
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
 #define PPC_LO(v) ((v) & 0xffff)
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
+#define D34(v) \
+  ((((v) & 0x3ffff0000ULL) << 16) | (v & 0xffff))
+#define HA34(v) ((v + (1ULL << 33)) >> 34)
 
 /* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections
    to set up space for global entry stubs.  These are put in glink,
 
 /* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections
    to set up space for global entry stubs.  These are put in glink,
@@ -9000,33 +9900,6 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
   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
 /* Set the sizes of the dynamic sections.  */
 
 static bfd_boolean
@@ -9144,7 +10017,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
                        htab->got_reli_size += rel_size;
                      }
                    else if (bfd_link_pic (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;
                                  && bfd_link_executable (info)))
                      {
                        asection *srel = ppc64_elf_tdata (ibfd)->relgot;
@@ -9221,7 +10094,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
              ent->got.offset = s->size;
              ent->owner = ibfd;
              s->size += 16;
              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);
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
                  srel->size += sizeof (Elf64_External_Rela);
@@ -9382,8 +10255,10 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
        }
 
       tls_opt = (htab->params->tls_get_addr_opt
        }
 
       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))
       if (tls_opt || !htab->opd_abi)
        {
          if (!add_dynamic_entry (DT_PPC64_OPT, tls_opt ? PPC64_OPT_TLS : 0))
@@ -9400,7 +10275,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)
          /* 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)
            {
 
          if ((info->flags & DF_TEXTREL) != 0)
            {
@@ -9706,6 +10582,146 @@ emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
   return r;
 }
 
   return r;
 }
 
+static bfd_byte *
+build_power10_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
+                     bfd_boolean load)
+{
+  uint64_t insn;
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    {
+      off -= odd;
+      if (odd)
+       {
+         bfd_put_32 (abfd, NOP, p);
+         p += 4;
+       }
+      if (load)
+       insn = PLD_R12_PC;
+      else
+       insn = PADDI_R12_PC;
+      insn |= D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+    }
+  /* The minimum value for paddi is -0x200000000.  The minimum value
+     for li is -0x8000, which when shifted by 34 and added gives a
+     minimum value of -0x2000200000000.  The maximum value is
+     0x1ffffffff+0x7fff<<34 which is 0x2000200000000-1.  */
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    {
+      off -= 8 - odd;
+      bfd_put_32 (abfd, LI_R11_0 | (HA34 (off) & 0xffff), p);
+      p += 4;
+      if (!odd)
+       {
+         bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+         p += 4;
+       }
+      insn = PADDI_R12_PC | D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+      p += 4;
+      if (odd)
+       {
+         bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+         p += 4;
+       }
+      if (load)
+       bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+      else
+       bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+    }
+  else
+    {
+      off -= odd + 8;
+      bfd_put_32 (abfd, LIS_R11 | ((HA34 (off) >> 16) & 0x3fff), p);
+      p += 4;
+      bfd_put_32 (abfd, ORI_R11_R11_0 | (HA34 (off) & 0xffff), p);
+      p += 4;
+      if (odd)
+       {
+         bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+         p += 4;
+       }
+      insn = PADDI_R12_PC | D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+      p += 4;
+      if (!odd)
+       {
+         bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+         p += 4;
+       }
+      if (load)
+       bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+      else
+       bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+    }
+  p += 4;
+  return p;
+}
+
+static unsigned int
+size_power10_offset (bfd_vma off, int odd)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    return odd + 8;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    return 20;
+  else
+    return 24;
+}
+
+static unsigned int
+num_relocs_for_power10_offset (bfd_vma off, int odd)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    return 1;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    return 2;
+  else
+    return 3;
+}
+
+static Elf_Internal_Rela *
+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)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    roff += odd;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    {
+      int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 8 - odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+      ++r;
+      roff += 8 - odd;
+    }
+  else
+    {
+      int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 8 + odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHESTA34);
+      ++r;
+      roff += 4;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 4 + odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+      ++r;
+      roff += 4 + odd;
+    }
+  r->r_offset = roff;
+  r->r_addend = targ;
+  r->r_info = ELF64_R_INFO (0, R_PPC64_PCREL34);
+  return r;
+}
+
 /* Emit .eh_frame opcode to advance pc by DELTA.  */
 
 static bfd_byte *
 /* Emit .eh_frame opcode to advance pc by DELTA.  */
 
 static bfd_byte *
@@ -9783,7 +10799,17 @@ plt_stub_size (struct ppc_link_hash_table *htab,
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
-      size = 8 + size_offset (off - 8);
+      if (htab->params->power10_stubs != 0)
+       {
+         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_power10_offset (off, start & 4);
+       }
+      else
+       size = 8 + size_offset (off - 8);
       if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
        size += 4;
       return size;
       if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
        size += 4;
       return size;
@@ -9809,13 +10835,21 @@ plt_stub_size (struct ppc_link_hash_table *htab,
        size += 4;
     }
   if (stub_entry->h != NULL
        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)
     {
       && 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 += 7 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
+           size += 6 * 4;
+       }
+      else
+       {
+         size += 30 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
+           size += 4;
+       }
     }
   return size;
 }
     }
   return size;
 }
@@ -9871,8 +10905,7 @@ build_plt_stub (struct ppc_link_hash_table *htab,
   if (!ALWAYS_USE_FAKE_DEP
       && plt_load_toc
       && plt_thread_safe
   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)
+      && !(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;
           && htab->params->tls_get_addr_opt))
     {
       bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
@@ -10031,17 +11064,14 @@ build_plt_stub (struct ppc_link_hash_table *htab,
 
 /* Build a special .plt call stub for __tls_get_addr.  */
 
 
 /* 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 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 ADD_R3_R12_R13 0x7c6c6a14
 #define BEQLR          0x4d820020
 #define MR_R3_R0       0x7c030378
-#define STD_R11_0R1    0xf9610000
 #define BCTRL          0x4e800421
 #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 inline bfd_byte *
 build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
@@ -10050,48 +11080,121 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
 {
   bfd *obfd = htab->params->stub_bfd;
   bfd_byte *loc = p;
 {
   bfd *obfd = htab->params->stub_bfd;
   bfd_byte *loc = p;
+  unsigned int i;
 
 
-  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, 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, MR_R0_R3, p),              p += 4;
-  bfd_put_32 (obfd, CMPDI_R11_0, p),           p += 4;
   bfd_put_32 (obfd, ADD_R3_R12_R13, p),                p += 4;
   bfd_put_32 (obfd, BEQLR, p),                 p += 4;
   bfd_put_32 (obfd, MR_R3_R0, p),              p += 4;
   bfd_put_32 (obfd, 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);
+  if (htab->params->no_tls_get_addr_regsave)
+    {
+      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;
+      bfd_put_32 (obfd, MFLR_R0, p);
+      p += 4;
+      bfd_put_32 (obfd, STD_R0_0R1 + STK_LINKER (htab), p);
+      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 (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);
 
 
-  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;
+    }
+  else
+    {
+      p = tls_get_addr_prologue (obfd, p, htab);
+
+      if (r != NULL)
+       r[0].r_offset += 18 * 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)
+       {
+         bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
+       }
+
+      p = tls_get_addr_epilogue (obfd, p, htab);
+    }
 
   if (htab->glink_eh_frame != NULL
       && htab->glink_eh_frame->size != 0)
     {
       bfd_byte *base, *eh;
 
   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;
 
       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;
+      if (htab->params->no_tls_get_addr_regsave)
+       {
+         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;
+       }
+      else
+       {
+         unsigned int cfa_updt, delta;
+         /* 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;
       *eh++ = DW_CFA_restore_extended;
       *eh++ = 65;
       stub_entry->group->eh_size = eh - base;
@@ -10168,9 +11271,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);
     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));
   while (num_rel-- != 0)
     {
       r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info));
@@ -10236,11 +11337,31 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   Elf_Internal_Rela *r;
   asection *plt;
   int num_rel;
   Elf_Internal_Rela *r;
   asection *plt;
   int num_rel;
+  int odd;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
   info = in_arg;
 
 
   /* 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;
   htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
@@ -10514,18 +11635,28 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        targ = (stub_entry->target_value
                + stub_entry->target_section->output_offset
                + stub_entry->target_section->output_section->vma);
        targ = (stub_entry->target_value
                + stub_entry->target_section->output_offset
                + stub_entry->target_section->output_section->vma);
+      odd = off & 4;
       off = targ - off;
 
       relp = p;
       num_rel = 0;
       off = targ - off;
 
       relp = p;
       num_rel = 0;
-      /* 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,
-                       stub_entry->stub_type >= ppc_stub_plt_call_notoc);
+      if (htab->params->power10_stubs != 0)
+       {
+         bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc;
+         p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load);
+       }
+      else
+       {
+         /* 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,
+                           stub_entry->stub_type >= ppc_stub_plt_call_notoc);
+       }
+
       if (stub_entry->stub_type <= ppc_stub_long_branch_both)
        {
          bfd_vma from;
       if (stub_entry->stub_type <= ppc_stub_long_branch_both)
        {
          bfd_vma from;
@@ -10547,13 +11678,21 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
        {
 
       if (info->emitrelocations)
        {
-         bfd_vma roff;
-         num_rel += num_relocs_for_offset (off);
+         bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
+         if (htab->params->power10_stubs != 0)
+           num_rel += num_relocs_for_power10_offset (off, odd);
+         else
+           {
+             num_rel += num_relocs_for_offset (off);
+             roff += 16;
+           }
          r = get_relocs (stub_entry->group->stub_sec, num_rel);
          if (r == NULL)
            return FALSE;
          r = get_relocs (stub_entry->group->stub_sec, num_rel);
          if (r == NULL)
            return FALSE;
-         roff = relp + 16 - stub_entry->group->stub_sec->contents;
-         r = emit_relocs_for_offset (info, r, roff, targ, off);
+         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
              || stub_entry->stub_type == ppc_stub_long_branch_both)
            {
          if (stub_entry->stub_type == ppc_stub_long_branch_notoc
              || stub_entry->stub_type == ppc_stub_long_branch_both)
            {
@@ -10568,8 +11707,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            }
        }
 
            }
        }
 
-      if (htab->glink_eh_frame != NULL
-       && htab->glink_eh_frame->size != 0)
+      if (htab->params->power10_stubs == 0
+         && htab->glink_eh_frame != NULL
+         && htab->glink_eh_frame->size != 0)
        {
          bfd_byte *base, *eh;
          unsigned int lr_used, delta;
        {
          bfd_byte *base, *eh;
          unsigned int lr_used, delta;
@@ -10662,8 +11802,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r[0].r_addend = targ;
        }
       if (stub_entry->h != NULL
          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)
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
          && htab->params->tls_get_addr_opt)
        p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
       else
          && htab->params->tls_get_addr_opt)
        p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
       else
@@ -10738,7 +11877,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_link_hash_table *htab;
   asection *plt;
   bfd_vma targ, off, r2off;
   struct ppc_link_hash_table *htab;
   asection *plt;
   bfd_vma targ, off, r2off;
-  unsigned int size, extra, lr_used, delta;
+  unsigned int size, extra, lr_used, delta, odd;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -10748,6 +11887,25 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   if (htab == NULL)
     return FALSE;
 
   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;
 
   /* Make a note of the offset within the stubs for this entry.  */
   stub_entry->stub_offset = stub_entry->group->stub_sec->size;
 
@@ -10891,16 +12049,24 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
       targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
+      odd = off & 4;
       off = targ - off;
 
       if (info->emitrelocations)
        {
       off = targ - off;
 
       if (info->emitrelocations)
        {
-         stub_entry->group->stub_sec->reloc_count
-           += num_relocs_for_offset (off);
+         unsigned int num_rel;
+         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;
        }
 
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      extra = size_offset (off - 8);
+      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.  */
       size += 4 + extra;
       /* The branch insn is at the end, or "extra" bytes along.  So
       /* Include branch insn plus those in the offset sequence.  */
       size += 4 + extra;
       /* The branch insn is at the end, or "extra" bytes along.  So
@@ -10908,17 +12074,20 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
         calculated.  */
       off -= extra;
 
         calculated.  */
       off -= extra;
 
-      /* 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_long_branch_both)
-       lr_used += 4;
-      /* 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.  */
-      delta = lr_used - stub_entry->group->lr_restore;
-      stub_entry->group->eh_size += eh_advance_size (delta) + 6;
-      stub_entry->group->lr_restore = lr_used + 8;
+      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_long_branch_both)
+           lr_used += 4;
+         /* 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.  */
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+         stub_entry->group->lr_restore = lr_used + 8;
+       }
 
       /* If the branch can't reach, use a plt_branch.  */
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
 
       /* If the branch can't reach, use a plt_branch.  */
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
@@ -10953,6 +12122,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            plt = htab->pltlocal;
        }
       targ += plt->output_offset + plt->output_section->vma;
            plt = htab->pltlocal;
        }
       targ += plt->output_offset + plt->output_section->vma;
+      odd = off & 4;
       off = targ - off;
 
       if (htab->params->plt_stub_align != 0)
       off = targ - off;
 
       if (htab->params->plt_stub_align != 0)
@@ -10966,24 +12136,31 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
        {
 
       if (info->emitrelocations)
        {
-         stub_entry->group->stub_sec->reloc_count
-           += num_relocs_for_offset (off - 8);
+         unsigned int num_rel;
+         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);
 
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
       size = plt_stub_size (htab, stub_entry, off);
 
-      /* 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;
-      /* 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.  */
-      delta = lr_used - stub_entry->group->lr_restore;
-      stub_entry->group->eh_size += eh_advance_size (delta) + 6;
-      stub_entry->group->lr_restore = lr_used + 8;
+      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;
+         /* 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.  */
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+         stub_entry->group->lr_restore = lr_used + 8;
+       }
       break;
 
     case ppc_stub_plt_call:
       break;
 
     case ppc_stub_plt_call:
@@ -11029,24 +12206,27 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       size = plt_stub_size (htab, stub_entry, off);
 
       if (stub_entry->h != NULL
       size = plt_stub_size (htab, stub_entry, off);
 
       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)
        {
          && 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;
+         if (htab->params->no_tls_get_addr_regsave)
+           {
+             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;
+           }
+         else
+           {
+             /* 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;
+           }
          stub_entry->group->lr_restore = size - 4;
        }
       break;
          stub_entry->group->lr_restore = size - 4;
        }
       break;
@@ -11068,7 +12248,7 @@ int
 ppc64_elf_setup_section_lists (struct bfd_link_info *info)
 {
   unsigned int id;
 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)
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
   if (htab == NULL)
@@ -11327,7 +12507,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
                  htab->got_reli_size += rel_size;
                }
              else if (bfd_link_pic (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;
                            && bfd_link_executable (info)))
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
@@ -11353,7 +12533,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;
          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);
            {
              asection *srel = ppc64_elf_tdata (ibfd)->relgot;
              srel->size += sizeof (Elf64_External_Rela);
@@ -11464,7 +12644,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
              && r_type != R_PPC64_REL14
              && r_type != R_PPC64_REL14_BRTAKEN
              && r_type != R_PPC64_REL14_BRNTAKEN
              && r_type != R_PPC64_REL14
              && r_type != R_PPC64_REL14_BRTAKEN
              && r_type != R_PPC64_REL14_BRNTAKEN
-             && r_type != R_PPC64_PLTCALL)
+             && r_type != R_PPC64_PLTCALL
+             && r_type != R_PPC64_PLTCALL_NOTOC)
            continue;
 
          r_symndx = ELF64_R_SYM (rel->r_info);
            continue;
 
          r_symndx = ELF64_R_SYM (rel->r_info);
@@ -11477,7 +12658,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.  */
 
          /* 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
          if (eh != NULL
              && (eh->elf.plt.plist != NULL
                  || (eh->oh != NULL
@@ -11588,9 +12769,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);
        free (local_syms);
       if (elf_section_data (isec)->relocs != relstart)
        free (relstart);
@@ -11892,6 +13072,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (htab == NULL)
     return FALSE;
 
   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)
   if (htab->params->plt_thread_safe == -1 && !bfd_link_executable (info))
     htab->params->plt_thread_safe = 1;
   if (!htab->opd_abi)
@@ -11945,6 +13128,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (!group_sections (info, stub_group_size, stubs_always_before_branch))
     return FALSE;
 
   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
 #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
@@ -12050,7 +13267,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;
                  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;
 
                  ok_dest = FALSE;
                  fdh = NULL;
@@ -12156,7 +13373,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)
                                   && 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)
                        stub_type = ppc_stub_long_branch_notoc;
                    }
                  else if (stub_type != ppc_stub_plt_call)
@@ -12186,8 +13403,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
                  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)
                    {
                      && section->has_tls_reloc
                      && irela != internal_relocs)
                    {
@@ -12197,7 +13413,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 (!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;
                    }
 
                        continue;
                    }
 
@@ -12233,6 +13450,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  if (stub_entry != NULL)
                    {
                      enum ppc_stub_type old_type;
                  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
                      /* 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
@@ -12240,6 +13458,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);
                         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)
                        {
                      old_type = stub_entry->stub_type;
                      switch (old_type)
                        {
@@ -12309,9 +13560,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 (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;
                    }
                        free (local_syms);
                      return FALSE;
                    }
@@ -12374,6 +13624,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
              stub_sec->flags &= ~SEC_RELOC;
            }
        }
              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)
 
       if (htab->stub_iteration <= STUB_SHRINK_ITER
          || htab->brlt->rawsize < htab->brlt->size)
@@ -12437,7 +13695,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->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.  */
        break;
 
       /* Ask the linker to do its stuff.  */
@@ -12533,6 +13793,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
     }
 
   maybe_strip_output (info, htab->brlt);
     }
 
   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);
 
   if (htab->glink_eh_frame != NULL)
     maybe_strip_output (info, htab->glink_eh_frame);
 
@@ -12568,9 +13830,7 @@ ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
          && (!is_elf_hash_table (htab)
              || h->def_regular))
        {
          && (!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;
        }
          _bfd_set_gp_value (obfd, TOCstart);
          return TOCstart;
        }
@@ -12689,7 +13949,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
              {
                plt = htab->elf.iplt;
                relplt = htab->elf.irelplt;
              {
                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
                if (htab->opd_abi)
                  rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
                else
@@ -12709,10 +13969,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
                else
                  relplt = NULL;
              }
                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)
              {
 
            if (relplt == NULL)
              {
@@ -12746,7 +14003,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))
                   + ((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);
          }
       }
            bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
          }
       }
@@ -12875,20 +14132,20 @@ 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 (!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;
                    free (local_syms);
                  return FALSE;
                }
 
              val = sym->st_value + ent->addend;
-             val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
+             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)
                {
              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;
                }
                  plt = htab->elf.iplt;
                  relplt = htab->elf.irelplt;
                }
@@ -12944,7 +14201,75 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
            symtab_hdr->contents = (unsigned char *) local_syms;
        }
     }
            symtab_hdr->contents = (unsigned char *) local_syms;
        }
     }
-  return TRUE;
+  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.
 }
 
 /* Build all the stubs associated with the current output file.
@@ -13106,6 +14431,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);
 
   /* Build .glink global entry stubs, and PLT relocs for globals.  */
   elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
 
@@ -13231,42 +14574,46 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
 
   if (stats != NULL)
     {
 
   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;
 }
     }
   return TRUE;
 }
@@ -13289,6 +14636,66 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (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.
 
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -13342,6 +14749,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   bfd_boolean is_opd;
   /* Assume 'at' branch hints.  */
   bfd_boolean is_isa_v2 = TRUE;
   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.  */
   bfd_vma d_offset = (bfd_big_endian (input_bfd) ? 2 : 0);
 
   /* Initialize howto table if needed.  */
@@ -13356,7 +14764,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   if (input_section->owner == htab->params->stub_bfd)
     return TRUE;
 
   if (input_section->owner == htab->params->stub_bfd)
     return TRUE;
 
-  BFD_ASSERT (is_ppc64_elf (input_bfd));
+  if (!is_ppc64_elf (input_bfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
+    }
 
   local_got_ents = elf_local_got_ents (input_bfd);
   TOCstart = elf_gp (output_bfd);
 
   local_got_ents = elf_local_got_ents (input_bfd);
   TOCstart = elf_gp (output_bfd);
@@ -13393,6 +14805,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       Elf_Internal_Rela orig_rel;
       reloc_howto_type *howto;
       struct reloc_howto_struct alt_howto;
       Elf_Internal_Rela orig_rel;
       reloc_howto_type *howto;
       struct reloc_howto_struct alt_howto;
+      uint64_t pinsn;
+      bfd_vma offset;
 
     again:
       orig_rel = *rel;
 
     again:
       orig_rel = *rel;
@@ -13485,7 +14899,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))
        {
 
       if (sec != NULL && discarded_section (sec))
        {
@@ -13557,10 +14971,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)
          && (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
        {
          if ((tls_mask & TLS_TLS) != 0
              && (r_type == R_PPC64_TLS
@@ -13600,7 +15011,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);
 
        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);
            abort ();
          insn += (14u << 26) - (58u << 26);
          bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset);
@@ -13638,7 +15049,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                       doing a GD->IE transition.  */
                    if (retval == 2)
                      {
                       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;
                        if ((tls_mask & TLS_TLS) != 0
                            && (tls_mask & TLS_GD) == 0)
                          goto tls_ldgd_opt;
@@ -13692,35 +15103,78 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
            }
          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)
            {
        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)
              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:
            }
          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;
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_gdld_hi;
          break;
@@ -13745,7 +15199,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT_TLSGD16:
        case R_PPC64_GOT_TLSGD16_LO:
 
        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;
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_ldgd_opt;
          break;
@@ -13755,7 +15209,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
              unsigned int insn1, insn2;
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
              unsigned int insn1, insn2;
-             bfd_vma offset;
 
            tls_ldgd_opt:
              offset = (bfd_vma) -1;
 
            tls_ldgd_opt:
              offset = (bfd_vma) -1;
@@ -13764,11 +15217,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.  */
                 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,
                  && 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,
-                                             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
                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
@@ -13780,15 +15235,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                {
                  /* IE */
                  insn1 &= (0x1f << 21) | (0x1f << 16);
                {
                  /* 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);
                  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;
                    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
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                }
              else
@@ -13800,20 +15256,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  if (tls_gd == 0)
                    {
                      /* Was an LD reloc.  */
                  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;
                      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)
                    {
                    }
                  else if (toc_symndx != 0)
                    {
@@ -13833,7 +15277,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              bfd_put_32 (input_bfd, insn1,
                          contents + rel->r_offset - d_offset);
              if (offset != (bfd_vma) -1)
              bfd_put_32 (input_bfd, insn1,
                          contents + rel->r_offset - d_offset);
              if (offset != (bfd_vma) -1)
-               bfd_put_32 (input_bfd, insn2, contents + offset);
+               {
+                 bfd_put_32 (input_bfd, insn2, contents + offset);
+                 if (offset + 8 <= input_section->size)
+                   {
+                     insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
+                     if (insn2 == LD_R2_0R1 + STK_TOC (htab))
+                       bfd_put_32 (input_bfd, NOP, contents + offset + 4);
+                   }
+               }
              if ((tls_mask & tls_gd) == 0
                  && (tls_gd == 0 || toc_symndx != 0))
                {
              if ((tls_mask & tls_gd) == 0
                  && (tls_gd == 0 || toc_symndx != 0))
                {
@@ -13844,16 +15296,65 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
            }
          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)
            {
              unsigned int insn2;
        case R_PPC64_TLSGD:
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
              && rel + 1 < relend)
            {
              unsigned int insn2;
-             bfd_vma offset = rel->r_offset;
+             enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
 
-             if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+             offset = rel->r_offset;
+             if (is_plt_seq_reloc (r_type1))
                {
                  bfd_put_32 (output_bfd, NOP, contents + offset);
                {
                  bfd_put_32 (output_bfd, NOP, contents + offset);
+                 if (r_type1 == R_PPC64_PLT_PCREL34
+                     || r_type1 == R_PPC64_PLT_PCREL34_NOTOC)
+                   bfd_put_32 (output_bfd, NOP, contents + offset + 4);
                  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
                  break;
                }
                  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
                  break;
                }
@@ -13861,7 +15362,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 (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;
                {
                  /* IE */
                  r_type = R_PPC64_NONE;
@@ -13875,16 +15376,27 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      r_symndx = toc_symndx;
                      rel->r_addend = toc_addend;
                    }
                      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);
                }
              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;
                goto again;
            }
          break;
@@ -13894,11 +15406,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              && rel + 1 < relend)
            {
              unsigned int insn2;
              && rel + 1 < relend)
            {
              unsigned int insn2;
-             bfd_vma offset = rel->r_offset;
+             enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
 
-             if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+             offset = rel->r_offset;
+             if (is_plt_seq_reloc (r_type1))
                {
                  bfd_put_32 (output_bfd, NOP, contents + offset);
                {
                  bfd_put_32 (output_bfd, NOP, contents + offset);
+                 if (r_type1 == R_PPC64_PLT_PCREL34
+                     || r_type1 == R_PPC64_PLT_PCREL34_NOTOC)
+                   bfd_put_32 (output_bfd, NOP, contents + offset + 4);
                  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
                  break;
                }
                  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
                  break;
                }
@@ -13906,30 +15422,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 (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_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);
              /* 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);
              bfd_put_32 (input_bfd, insn2, contents + offset);
-             goto again;
+             if (r_type != R_PPC64_NONE)
+               goto again;
            }
          break;
 
            }
          break;
 
@@ -13941,7 +15454,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_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
                    {
                    r_type = R_PPC64_TPREL64;
                  else
                    {
@@ -14036,7 +15549,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              && relocation + 0x80008000 <= 0xffffffff)
            {
              unsigned int insn1, insn2;
              && relocation + 0x80008000 <= 0xffffffff)
            {
              unsigned int insn1, insn2;
-             bfd_vma offset = rel->r_offset - d_offset;
+             offset = rel->r_offset - d_offset;
              insn1 = bfd_get_32 (input_bfd, contents + offset);
              insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
              if ((insn1 & 0xffff0000) == ADDIS_R2_R12
              insn1 = bfd_get_32 (input_bfd, contents + offset);
              insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
              if ((insn1 & 0xffff0000) == ADDIS_R2_R12
@@ -14099,6 +15612,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_REL24:
        case R_PPC64_REL24_NOTOC:
        case R_PPC64_PLTCALL:
        case R_PPC64_REL24:
        case R_PPC64_REL24_NOTOC:
        case R_PPC64_PLTCALL:
+       case R_PPC64_PLTCALL_NOTOC:
          /* Calls to functions with a different TOC, such as calls to
             shared objects, need to alter the TOC pointer.  This is
             done using a linkage stub.  A REL24 branching to these
          /* Calls to functions with a different TOC, such as calls to
             shared objects, need to alter the TOC pointer.  This is
             done using a linkage stub.  A REL24 branching to these
@@ -14112,7 +15626,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            fdh = ppc_follow_link (h->oh);
          stub_entry = ppc_get_stub_entry (input_section, sec, fdh, &orig_rel,
                                           htab);
            fdh = ppc_follow_link (h->oh);
          stub_entry = ppc_get_stub_entry (input_section, sec, fdh, &orig_rel,
                                           htab);
-         if (r_type == R_PPC64_PLTCALL
+         if ((r_type == R_PPC64_PLTCALL
+              || r_type == R_PPC64_PLTCALL_NOTOC)
              && stub_entry != NULL
              && stub_entry->stub_type >= ppc_stub_plt_call
              && stub_entry->stub_type <= ppc_stub_plt_call_both)
              && stub_entry != NULL
              && stub_entry->stub_type >= ppc_stub_plt_call
              && stub_entry->stub_type <= ppc_stub_plt_call_both)
@@ -14164,8 +15679,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                               || nop == CROR_313131)
                        {
                          if (h != NULL
                               || 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.  */
                              && htab->params->tls_get_addr_opt)
                            {
                              /* Special stub used, leave nop alone.  */
@@ -14291,7 +15805,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)
                  || 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;
              && (relocation + addend - from + max_br_offset
                  < 2 * max_br_offset))
            stub_entry = NULL;
@@ -14330,8 +15844,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   || stub_entry->stub_type == ppc_stub_plt_call_r2save
                   || stub_entry->stub_type == ppc_stub_plt_call_both)
                  && !(h != NULL
                   || 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)
+                      && is_tls_get_addr (&h->elf, htab)
                       && htab->params->tls_get_addr_opt)
                  && rel + 1 < relend
                  && rel[1].r_offset == rel->r_offset + 4
                       && htab->params->tls_get_addr_opt)
                  && rel + 1 < relend
                  && rel[1].r_offset == rel->r_offset + 4
@@ -14342,6 +15855,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        || stub_entry->stub_type == ppc_stub_plt_call_both)
                       && r_type == R_PPC64_REL24_NOTOC)
                relocation += 4;
                        || stub_entry->stub_type == ppc_stub_plt_call_both)
                       && r_type == R_PPC64_REL24_NOTOC)
                relocation += 4;
+
+             if (r_type == R_PPC64_REL24_NOTOC
+                 && (stub_entry->stub_type == ppc_stub_plt_call_notoc
+                     || stub_entry->stub_type == ppc_stub_plt_call_both))
+               htab->notoc_plt = 1;
            }
 
          if (insn != 0)
            }
 
          if (insn != 0)
@@ -14383,9 +15901,131 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              goto copy_reloc;
            }
          break;
              goto copy_reloc;
            }
          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))
+           {
+             insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
+             if ((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;
+                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+           }
+         break;
+
+       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))
+           {
+             insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
+             if ((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 & (0x3fu << 26)) == 15u << 26 /* addis */)
+               {
+                 r_type = R_PPC64_TOC16_HA;
+                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+           }
+         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)))
+           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 (!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)
+             && 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) | (2ULL << 56) | (1ULL << 52)
+                      | (14ULL << 26) /* paddi */))
+               {
+                 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)
+                       {
+                         if (off2 + 8 > input_section->size)
+                           break;
+                         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);
+                       }
+                   }
+               }
+           }
+         break;
        }
 
        }
 
-      /* Set `addend'.  */
       tls_type = 0;
       save_unresolved_reloc = unresolved_reloc;
       switch (r_type)
       tls_type = 0;
       save_unresolved_reloc = unresolved_reloc;
       switch (r_type)
@@ -14407,6 +16047,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GNU_VTINHERIT:
        case R_PPC64_GNU_VTENTRY:
        case R_PPC64_ENTRY:
        case R_PPC64_GNU_VTINHERIT:
        case R_PPC64_GNU_VTENTRY:
        case R_PPC64_ENTRY:
+       case R_PPC64_PCREL_OPT:
          goto copy_reloc;
 
          /* GOT16 relocations.  Like an ADDR16 using the symbol's
          goto copy_reloc;
 
          /* GOT16 relocations.  Like an ADDR16 using the symbol's
@@ -14417,6 +16058,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_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;
 
          tls_type = TLS_TLS | TLS_GD;
          goto dogot;
 
@@ -14424,6 +16066,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_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;
 
          tls_type = TLS_TLS | TLS_LD;
          goto dogot;
 
@@ -14431,6 +16074,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_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;
 
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogot;
 
@@ -14438,6 +16082,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_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;
 
          tls_type = TLS_TLS | TLS_DTPREL;
          goto dogot;
 
@@ -14447,6 +16092,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT16_HA:
        case R_PPC64_GOT16_DS:
        case R_PPC64_GOT16_LO_DS:
        case R_PPC64_GOT16_HA:
        case R_PPC64_GOT16_DS:
        case R_PPC64_GOT16_LO_DS:
+       case R_PPC64_GOT_PCREL34:
        dogot:
          {
            /* Relocation is to the entry for this symbol in the global
        dogot:
          {
            /* Relocation is to the entry for this symbol in the global
@@ -14458,8 +16104,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            struct got_entry *ent;
 
            if (tls_type == (TLS_TLS | TLS_LD)
            struct got_entry *ent;
 
            if (tls_type == (TLS_TLS | TLS_LD)
-               && (h == NULL
-                   || !h->elf.def_dynamic))
+               && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
              ent = ppc64_tlsld_got (input_bfd);
            else
              {
              ent = ppc64_tlsld_got (input_bfd);
            else
              {
@@ -14526,18 +16171,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (ifunc)
                  {
                    relgot = htab->elf.irelplt;
                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
                  }
                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))))
                  relgot = ppc64_elf_tdata (ent->owner)->relgot;
                                  && bfd_link_executable (info)
                                  && SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
                  relgot = ppc64_elf_tdata (ent->owner)->relgot;
@@ -14546,7 +16187,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
                                       + off);
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
                                       + off);
-                   outrel.r_addend = addend;
+                   outrel.r_addend = orig_rel.r_addend;
                    if (tls_type & (TLS_LD | TLS_GD))
                      {
                        outrel.r_addend = 0;
                    if (tls_type & (TLS_LD | TLS_GD))
                      {
                        outrel.r_addend = 0;
@@ -14559,7 +16200,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                            bfd_elf64_swap_reloca_out (output_bfd,
                                                       &outrel, loc);
                            outrel.r_offset += 8;
                            bfd_elf64_swap_reloca_out (output_bfd,
                                                       &outrel, loc);
                            outrel.r_offset += 8;
-                           outrel.r_addend = addend;
+                           outrel.r_addend = orig_rel.r_addend;
                            outrel.r_info
                              = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
                          }
                            outrel.r_info
                              = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
                          }
@@ -14605,7 +16246,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   emitting a reloc.  */
                else
                  {
                   emitting a reloc.  */
                else
                  {
-                   relocation += addend;
+                   relocation += orig_rel.r_addend;
                    if (tls_type != 0)
                      {
                        if (htab->elf.tls_sec == NULL)
                    if (tls_type != 0)
                      {
                        if (htab->elf.tls_sec == NULL)
@@ -14636,7 +16277,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              abort ();
 
            relocation = got->output_section->vma + got->output_offset + off;
              abort ();
 
            relocation = got->output_section->vma + got->output_offset + off;
-           addend = -(TOCstart + htab->sec_info[input_section->id].toc_off);
+           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;
 
          }
          break;
 
@@ -14644,10 +16291,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_PLT16_HI:
        case R_PPC64_PLT16_LO:
        case R_PPC64_PLT16_LO_DS:
        case R_PPC64_PLT16_HI:
        case R_PPC64_PLT16_LO:
        case R_PPC64_PLT16_LO_DS:
+       case R_PPC64_PLT_PCREL34:
+       case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_PLT32:
        case R_PPC64_PLT64:
        case R_PPC64_PLTSEQ:
        case R_PPC64_PLT32:
        case R_PPC64_PLT64:
        case R_PPC64_PLTSEQ:
+       case R_PPC64_PLTSEQ_NOTOC:
        case R_PPC64_PLTCALL:
        case R_PPC64_PLTCALL:
+       case R_PPC64_PLTCALL_NOTOC:
          /* Relocation is to the entry for this symbol in the
             procedure linkage table.  */
          unresolved_reloc = TRUE;
          /* Relocation is to the entry for this symbol in the
             procedure linkage table.  */
          unresolved_reloc = TRUE;
@@ -14728,6 +16379,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;
        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.  */
          break;
 
          /* Relocate against the beginning of the section.  */
@@ -14751,14 +16404,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_REL16_HIGHERA:
        case R_PPC64_REL16_HIGHEST:
        case R_PPC64_REL16_HIGHESTA:
        case R_PPC64_REL16_HIGHERA:
        case R_PPC64_REL16_HIGHEST:
        case R_PPC64_REL16_HIGHESTA:
+       case R_PPC64_REL16_HIGHER34:
+       case R_PPC64_REL16_HIGHERA34:
+       case R_PPC64_REL16_HIGHEST34:
+       case R_PPC64_REL16_HIGHESTA34:
        case R_PPC64_REL16DX_HA:
        case R_PPC64_REL16DX_HA:
-         break;
-
        case R_PPC64_REL14:
        case R_PPC64_REL14_BRNTAKEN:
        case R_PPC64_REL14_BRTAKEN:
        case R_PPC64_REL24:
        case R_PPC64_REL24_NOTOC:
        case R_PPC64_REL14:
        case R_PPC64_REL14_BRNTAKEN:
        case R_PPC64_REL14_BRTAKEN:
        case R_PPC64_REL24:
        case R_PPC64_REL24_NOTOC:
+       case R_PPC64_PCREL34:
+       case R_PPC64_PCREL28:
          break;
 
        case R_PPC64_TPREL16:
          break;
 
        case R_PPC64_TPREL16:
@@ -14773,6 +16430,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_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)
          if (h != NULL
              && h->elf.root.type == bfd_link_hash_undefweak
              && h->elf.dynindx == -1)
@@ -14808,6 +16466,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_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;
          if (htab->elf.tls_sec != NULL)
            addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
          break;
@@ -14853,12 +16512,21 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_ADDR16_HIGHESTA:
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_ADDR16_HIGHESTA:
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
+       case R_PPC64_ADDR16_HIGHER34:
+       case R_PPC64_ADDR16_HIGHERA34:
+       case R_PPC64_ADDR16_HIGHEST34:
+       case R_PPC64_ADDR16_HIGHESTA34:
        case R_PPC64_ADDR24:
        case R_PPC64_ADDR32:
        case R_PPC64_ADDR64:
        case R_PPC64_UADDR16:
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
        case R_PPC64_ADDR24:
        case R_PPC64_ADDR32:
        case R_PPC64_ADDR64:
        case R_PPC64_UADDR16:
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
+       case R_PPC64_D34:
+       case R_PPC64_D34_LO:
+       case R_PPC64_D34_HI30:
+       case R_PPC64_D34_HA30:
+       case R_PPC64_D28:
        dodyn:
          if ((input_section->flags & SEC_ALLOC) == 0)
            break;
        dodyn:
          if ((input_section->flags & SEC_ALLOC) == 0)
            break;
@@ -14868,11 +16536,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          if (bfd_link_pic (info)
              ? ((h == NULL
 
          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 != 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;
                 : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
            {
              bfd_boolean skip, relocate;
@@ -14982,21 +16650,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        }
                      else
                        {
                        }
                      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;
                              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
 
                          /* We are turning this relocation into one
                             against a section symbol, so subtract out
@@ -15016,10 +16701,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                {
                  sreloc = htab->elf.irelplt;
                  : 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 ();
                }
              if (sreloc == NULL)
                abort ();
@@ -15031,6 +16714,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);
 
              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
              /* 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
@@ -15110,6 +16806,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             insn.  */
          break;
 
             insn.  */
          break;
 
+       case R_PPC64_PLTCALL_NOTOC:
+         if (!unresolved_reloc)
+           htab->notoc_plt = 1;
+         /* Fall through.  */
        case R_PPC64_PLTCALL:
          if (unresolved_reloc)
            {
        case R_PPC64_PLTCALL:
          if (unresolved_reloc)
            {
@@ -15118,12 +16818,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              insn = bfd_get_32 (input_bfd, p);
              insn &= 1;
              bfd_put_32 (input_bfd, B_DOT | insn, p);
              insn = bfd_get_32 (input_bfd, p);
              insn &= 1;
              bfd_put_32 (input_bfd, B_DOT | insn, p);
-             bfd_put_32 (input_bfd, NOP, p + 4);
+             if (r_type == R_PPC64_PLTCALL)
+               bfd_put_32 (input_bfd, NOP, p + 4);
              unresolved_reloc = save_unresolved_reloc;
              r_type = R_PPC64_REL24;
            }
          break;
 
              unresolved_reloc = save_unresolved_reloc;
              r_type = R_PPC64_REL24;
            }
          break;
 
+       case R_PPC64_PLTSEQ_NOTOC:
        case R_PPC64_PLTSEQ:
          if (unresolved_reloc)
            {
        case R_PPC64_PLTSEQ:
          if (unresolved_reloc)
            {
@@ -15132,6 +16834,21 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
            }
          break;
 
+       case R_PPC64_PLT_PCREL34_NOTOC:
+         if (!unresolved_reloc)
+           htab->notoc_plt = 1;
+         /* Fall through.  */
+       case R_PPC64_PLT_PCREL34:
+         if (unresolved_reloc)
+           {
+             bfd_byte *p = contents + rel->r_offset;
+             bfd_put_32 (input_bfd, PNOP >> 32, p);
+             bfd_put_32 (input_bfd, PNOP, p + 4);
+             unresolved_reloc = FALSE;
+             goto copy_reloc;
+           }
+         break;
+
        case R_PPC64_PLT16_HA:
          if (unresolved_reloc)
            {
        case R_PPC64_PLT16_HA:
          if (unresolved_reloc)
            {
@@ -15177,10 +16894,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              insn = bfd_get_32 (input_bfd, p);
            {
              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.  */
                {
                  /* Transform addic to addi when we change reg.  */
-                 insn &= ~((0x3f << 26) | (0x1f << 16));
+                 insn &= ~((0x3fu << 26) | (0x1f << 16));
                  insn |= (14u << 26) | (2 << 16);
                }
              else
                  insn |= (14u << 26) | (2 << 16);
                }
              else
@@ -15197,7 +16914,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              insn = bfd_get_32 (input_bfd, p);
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              insn = bfd_get_32 (input_bfd, p);
-             if ((insn & ((0x3f << 26) | 0x1f << 16))
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
                  != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */)
                /* xgettext:c-format */
                info->callbacks->minfo
                  != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */)
                /* xgettext:c-format */
                info->callbacks->minfo
@@ -15270,6 +16987,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          addend += 0x8000;
          break;
 
          addend += 0x8000;
          break;
 
+       case R_PPC64_D34_HA30:
+       case R_PPC64_ADDR16_HIGHERA34:
+       case R_PPC64_ADDR16_HIGHESTA34:
+       case R_PPC64_REL16_HIGHERA34:
+       case R_PPC64_REL16_HIGHESTA34:
+         if (sec != NULL)
+           addend += 1ULL << 33;
+         break;
+
        case R_PPC64_ADDR16_DS:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_GOT16_DS:
        case R_PPC64_ADDR16_DS:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_GOT16_DS:
@@ -15297,8 +17023,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.  */
             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);
            mask = 15;
          relocation += addend;
          addend = insn & (mask ^ 3);
@@ -15347,15 +17073,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));
          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
            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)
            {
            complain = complain_overflow_unsigned;
          if (howto->complain_on_overflow != complain)
            {
@@ -15365,9 +17091,54 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
        }
 
            }
        }
 
-      if (r_type == R_PPC64_REL16DX_HA)
+      switch (r_type)
        {
        {
-         /* Split field reloc isn't handled by _bfd_final_link_relocate.  */
+         /* Split field relocs aren't handled by _bfd_final_link_relocate.  */
+       case R_PPC64_D34:
+       case R_PPC64_D34_LO:
+       case R_PPC64_D34_HI30:
+       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:
+       case R_PPC64_PCREL28:
+         if (rel->r_offset + 8 > input_section->size)
+           r = bfd_reloc_outofrange;
+         else
+           {
+             relocation += addend;
+             if (howto->pc_relative)
+               relocation -= (rel->r_offset
+                              + input_section->output_offset
+                              + input_section->output_section->vma);
+             relocation >>= howto->rightshift;
+
+             pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             pinsn <<= 32;
+             pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+
+             pinsn &= ~howto->dst_mask;
+             pinsn |= (((relocation << 16) | (relocation & 0xffff))
+                       & howto->dst_mask);
+             bfd_put_32 (input_bfd, pinsn >> 32, contents + rel->r_offset);
+             bfd_put_32 (input_bfd, pinsn, contents + rel->r_offset + 4);
+             r = bfd_reloc_ok;
+             if (howto->complain_on_overflow == complain_overflow_signed
+                 && (relocation + (1ULL << (howto->bitsize - 1))
+                     >= 1ULL << howto->bitsize))
+               r = bfd_reloc_overflow;
+           }
+         break;
+
+       case R_PPC64_REL16DX_HA:
          if (rel->r_offset + 4 > input_section->size)
            r = bfd_reloc_outofrange;
          else
          if (rel->r_offset + 4 > input_section->size)
            r = bfd_reloc_outofrange;
          else
@@ -15385,10 +17156,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (relocation + 0x8000 > 0xffff)
                r = bfd_reloc_overflow;
            }
              if (relocation + 0x8000 > 0xffff)
                r = bfd_reloc_overflow;
            }
+         break;
+
+       default:
+         r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                       contents, rel->r_offset,
+                                       relocation, addend);
        }
        }
-      else
-       r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
-                                     rel->r_offset, relocation, addend);
 
       if (r != bfd_reloc_ok)
        {
 
       if (r != bfd_reloc_ok)
        {
@@ -15432,8 +17206,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                 reloc_name, sym_name, (int) r);
              ret = FALSE;
            }
                 reloc_name, sym_name, (int) r);
              ret = FALSE;
            }
-         if (more_info != NULL)
-           free (more_info);
+         free (more_info);
        }
     copy_reloc:
       if (wrel != rel)
        }
     copy_reloc:
       if (wrel != rel)
@@ -15551,23 +17324,21 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
          break;
        }
 
          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;
 
     {
       /* 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 ();
 
        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)
       rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_COPY);
       rela.r_addend = 0;
       if (h->root.u.def.section == htab->elf.sdynrelro)
@@ -15666,7 +17437,8 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_PPC64_OPT:
              break;
 
            case DT_PPC64_OPT:
-             if (htab->do_multi_toc && htab->multi_toc_needed)
+             if ((htab->do_multi_toc && htab->multi_toc_needed)
+                 || htab->notoc_plt)
                dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
              if (htab->has_plt_localentry0)
                dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;
                dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
              if (htab->has_plt_localentry0)
                dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;
@@ -15694,11 +17466,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_TEXTREL:
              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"));
                info->callbacks->einfo
                  (_("%P: warning: text relocations and GNU indirect "
                     "functions may result in a segfault at runtime\n"));