[PR gdb/25939] Move push_target call earlier in procfs.c
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index adc285f48762d86a5517eb7a5c8fb1168e5fece7..8d710848ba60bd89ab5c5ef039d01cd7c4208a66 100644 (file)
@@ -1,6 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
-   2009, 2010, 2011, 2012 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.
@@ -36,6 +35,9 @@
 #include "elf64-ppc.h"
 #include "dwarf2.h"
 
+/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1.  */
+#define OCTETS_PER_BYTE(ABFD, SEC) 1
+
 static bfd_reloc_status_type ppc64_elf_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_branch_reloc
@@ -52,20 +54,23 @@ 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 **);
+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
   (asection *, bfd_vma, asection **, bfd_vma *, bfd_boolean);
 
-#define TARGET_LITTLE_SYM      bfd_elf64_powerpcle_vec
+#define TARGET_LITTLE_SYM      powerpc_elf64_le_vec
 #define TARGET_LITTLE_NAME     "elf64-powerpcle"
-#define TARGET_BIG_SYM         bfd_elf64_powerpc_vec
+#define TARGET_BIG_SYM         powerpc_elf64_vec
 #define TARGET_BIG_NAME                "elf64-powerpc"
 #define ELF_ARCH               bfd_arch_powerpc
 #define ELF_TARGET_ID          PPC64_ELF_DATA
 #define ELF_MACHINE_CODE       EM_PPC64
 #define ELF_MAXPAGESIZE                0x10000
 #define ELF_COMMONPAGESIZE     0x1000
+#define ELF_RELROPAGESIZE      ELF_MAXPAGESIZE
 #define elf_info_to_howto      ppc64_elf_info_to_howto
 
 #define elf_backend_want_got_sym 0
@@ -73,9 +78,11 @@ static bfd_vma opd_entry_value
 #define elf_backend_plt_alignment 3
 #define elf_backend_plt_not_loaded 1
 #define elf_backend_got_header_size 8
+#define elf_backend_want_dynrelro 1
 #define elf_backend_can_gc_sections 1
 #define elf_backend_can_refcount 1
 #define elf_backend_rela_normal 1
+#define elf_backend_dtrel_excludes_plt 1
 #define elf_backend_default_execstack 0
 
 #define bfd_elf64_mkobject                   ppc64_elf_mkobject
@@ -85,25 +92,25 @@ static bfd_vma opd_entry_value
 #define bfd_elf64_bfd_print_private_bfd_data  ppc64_elf_print_private_bfd_data
 #define bfd_elf64_new_section_hook           ppc64_elf_new_section_hook
 #define bfd_elf64_bfd_link_hash_table_create  ppc64_elf_link_hash_table_create
-#define bfd_elf64_bfd_link_hash_table_free    ppc64_elf_link_hash_table_free
 #define bfd_elf64_get_synthetic_symtab       ppc64_elf_get_synthetic_symtab
 #define bfd_elf64_bfd_link_just_syms         ppc64_elf_link_just_syms
+#define bfd_elf64_bfd_gc_sections            ppc64_elf_gc_sections
 
 #define elf_backend_object_p                 ppc64_elf_object_p
 #define elf_backend_grok_prstatus            ppc64_elf_grok_prstatus
 #define elf_backend_grok_psinfo                      ppc64_elf_grok_psinfo
 #define elf_backend_write_core_note          ppc64_elf_write_core_note
-#define elf_backend_create_dynamic_sections   ppc64_elf_create_dynamic_sections
+#define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_copy_indirect_symbol      ppc64_elf_copy_indirect_symbol
 #define elf_backend_add_symbol_hook          ppc64_elf_add_symbol_hook
-#define elf_backend_check_directives         ppc64_elf_process_dot_syms
+#define elf_backend_check_directives         ppc64_elf_before_check_relocs
 #define elf_backend_notice_as_needed         ppc64_elf_notice_as_needed
 #define elf_backend_archive_symbol_lookup     ppc64_elf_archive_symbol_lookup
 #define elf_backend_check_relocs             ppc64_elf_check_relocs
+#define elf_backend_relocs_compatible        _bfd_elf_relocs_compatible
 #define elf_backend_gc_keep                  ppc64_elf_gc_keep
 #define elf_backend_gc_mark_dynamic_ref       ppc64_elf_gc_mark_dynamic_ref
 #define elf_backend_gc_mark_hook             ppc64_elf_gc_mark_hook
-#define elf_backend_gc_sweep_hook            ppc64_elf_gc_sweep_hook
 #define elf_backend_adjust_dynamic_symbol     ppc64_elf_adjust_dynamic_symbol
 #define elf_backend_hide_symbol                      ppc64_elf_hide_symbol
 #define elf_backend_maybe_function_sym       ppc64_elf_maybe_function_sym
@@ -118,8 +125,10 @@ 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_post_process_headers      _bfd_elf_set_osabi
+#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
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -127,6 +136,7 @@ static bfd_vma opd_entry_value
 
 /* The size in bytes of an entry in the procedure linkage table.  */
 #define PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 8)
+#define LOCAL_PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 16 : 8)
 
 /* The initial size of the plt reserved for the dynamic linker.  */
 #define PLT_INITIAL_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 16)
@@ -134,13 +144,15 @@ static bfd_vma opd_entry_value
 /* Offsets to some stack save slots.  */
 #define STK_LR 16
 #define STK_TOC(htab) (htab->opd_abi ? 40 : 24)
-/* This one is dodgy.  ABIv2 does not have a linker word, so use the
+/* This one is dodgy.  ELFv2 does not have a linker word, so use the
    CR save slot.  Used only by optimised __tls_get_addr call stub,
    relying on __tls_get_addr_opt not saving CR..  */
 #define STK_LINKER(htab) (htab->opd_abi ? 32 : 8)
 
 /* TOC base pointers offset from start of TOC.  */
 #define TOC_BASE_OFF   0x8000
+/* TOC base alignment.  */
+#define TOC_BASE_ALIGN 256
 
 /* Offset of tp and dtp pointers from start of TLS block.  */
 #define TP_OFFSET      0x7000
@@ -157,29 +169,51 @@ static bfd_vma opd_entry_value
 #define LD_R11_0R11    0xe96b0000      /* ld    %r11,xxx+16@l(%r11) */
 #define BCTR           0x4e800420      /* bctr                      */
 
-#define ADDI_R11_R11   0x396b0000      /* addi %r11,%r11,off@l  */
-#define ADDIS_R2_R2    0x3c420000      /* addis %r2,%r2,off@ha  */
-#define ADDI_R2_R2     0x38420000      /* addi  %r2,%r2,off@l   */
+#define ADDI_R11_R11   0x396b0000      /* addi %r11,%r11,off@l  */
+#define ADDI_R12_R11   0x398b0000      /* addi %r12,%r11,off@l  */
+#define ADDI_R12_R12   0x398c0000      /* addi %r12,%r12,off@l  */
+#define ADDIS_R2_R2    0x3c420000      /* addis %r2,%r2,off@ha  */
+#define ADDI_R2_R2     0x38420000      /* addi  %r2,%r2,off@l   */
 
-#define XOR_R2_R12_R12 0x7d826278      /* xor   %r2,%r12,%r12   */
-#define ADD_R11_R11_R2 0x7d6b1214      /* add   %r11,%r11,%r2   */
-#define XOR_R11_R12_R12        0x7d8b6278      /* xor   %r11,%r12,%r12  */
-#define ADD_R2_R2_R11  0x7c425a14      /* add   %r2,%r2,%r11    */
-#define CMPLDI_R2_0    0x28220000      /* cmpldi %r2,0          */
-#define BNECTR         0x4ca20420      /* bnectr+               */
-#define BNECTR_P4      0x4ce20420      /* bnectr+               */
+#define XOR_R2_R12_R12 0x7d826278      /* xor   %r2,%r12,%r12   */
+#define ADD_R11_R11_R2 0x7d6b1214      /* add   %r11,%r11,%r2   */
+#define XOR_R11_R12_R12        0x7d8b6278      /* xor   %r11,%r12,%r12  */
+#define ADD_R2_R2_R11  0x7c425a14      /* add   %r2,%r2,%r11    */
+#define CMPLDI_R2_0    0x28220000      /* cmpldi %r2,0          */
+#define BNECTR         0x4ca20420      /* bnectr+               */
+#define BNECTR_P4      0x4ce20420      /* bnectr+               */
 
 #define LD_R12_0R2     0xe9820000      /* ld    %r12,xxx+0(%r2) */
 #define LD_R11_0R2     0xe9620000      /* ld    %r11,xxx+0(%r2) */
-#define LD_R2_0R2      0xe8420000      /* ld    %r2,xxx+0(%r2)  */
-
-#define LD_R2_0R1      0xe8410000      /* ld    %r2,0(%r1)      */
-
+#define LD_R2_0R2      0xe8420000      /* ld    %r2,xxx+0(%r2)  */
+
+#define LD_R2_0R1      0xe8410000      /* ld    %r2,0(%r1)      */
+#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_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 LD_R12_0R12    0xe98c0000      /* ld    %r12,xxx@l(%r12) */
-
-/* glink call stub instructions.  We enter with the index in R0.  */
-#define GLINK_CALL_STUB_SIZE (16*4)
+#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 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 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)                    \
+  (8u + (htab->opd_abi ? 11 * 4 : 14 * 4))
                                        /* 0:                           */
                                        /*  .quad plt0-1f               */
                                        /* __glink:                     */
@@ -237,793 +271,281 @@ static bfd_vma opd_entry_value
 #ifndef NO_OPD_RELOCS
 #define NO_OPD_RELOCS 0
 #endif
-\f
-#define ONES(n) (((bfd_vma) 1 << ((n) - 1) << 1) - 1)
 
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+#endif
+
+static inline int
+abiversion (bfd *abfd)
+{
+  return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI;
+}
+
+static inline void
+set_abiversion (bfd *abfd, int ver)
+{
+  elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI;
+  elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI;
+}
+\f
 /* Relocation HOWTO's.  */
+/* Like other ELF RELA targets that don't apply multiple
+   field-altering relocations to the same localation, src_mask is
+   always zero and pcrel_offset is the same as pc_relative.
+   PowerPC can always use a zero bitpos, even when the field is not at
+   the LSB.  For example, a REL24 could use rightshift=2, bisize=24
+   and bitpos=2 which matches the ABI description, or as we do here,
+   rightshift=0, bitsize=26 and bitpos=0.  */
+#define HOW(type, size, bitsize, mask, rightshift, pc_relative, \
+           complain, special_func)                             \
+  HOWTO (type, rightshift, size, bitsize, pc_relative, 0,      \
+        complain_overflow_ ## complain, special_func,          \
+        #type, FALSE, 0, mask, pc_relative)
+
 static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC64_max];
 
-static reloc_howto_type ppc64_elf_howto_raw[] = {
+static reloc_howto_type ppc64_elf_howto_raw[] =
+{
   /* This reloc does nothing.  */
-  HOWTO (R_PPC64_NONE,         /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_NONE",        /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_NONE, 3, 0, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* A standard 32 bit relocation.  */
-  HOWTO (R_PPC64_ADDR32,       /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR32",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffffffff,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR32, 2, 32, 0xffffffff, 0, FALSE, bitfield,
+       bfd_elf_generic_reloc),
 
   /* An absolute 26 bit branch; the lower two bits must be zero.
      FIXME: we don't check that, we just clear them.  */
-  HOWTO (R_PPC64_ADDR24,       /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        26,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR24",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x03fffffc,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR24, 2, 26, 0x03fffffc, 0, FALSE, bitfield,
+       bfd_elf_generic_reloc),
 
   /* A standard 16 bit relocation.  */
-  HOWTO (R_PPC64_ADDR16,       /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16, 1, 16, 0xffff, 0, FALSE, bitfield,
+       bfd_elf_generic_reloc),
 
   /* A 16 bit relocation without overflow.  */
-  HOWTO (R_PPC64_ADDR16_LO,    /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_LO",   /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* Bits 16-31 of an address.  */
-  HOWTO (R_PPC64_ADDR16_HI,    /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_HI",   /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       bfd_elf_generic_reloc),
 
   /* Bits 16-31 of an address, plus 1 if the contents of the low 16
      bits, treated as a signed number, is negative.  */
-  HOWTO (R_PPC64_ADDR16_HA,    /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_ha_reloc,    /* special_function */
-        "R_PPC64_ADDR16_HA",   /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_ha_reloc),
 
   /* An absolute 16 bit branch; the lower two bits must be zero.
      FIXME: we don't check that, we just clear them.  */
-  HOWTO (R_PPC64_ADDR14,       /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_branch_reloc, /* special_function */
-        "R_PPC64_ADDR14",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR14, 2, 16, 0x0000fffc, 0, FALSE, signed,
+       ppc64_elf_branch_reloc),
 
   /* An absolute 16 bit branch, for which bit 10 should be set to
      indicate that the branch is expected to be taken.  The lower two
      bits must be zero.  */
-  HOWTO (R_PPC64_ADDR14_BRTAKEN, /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_brtaken_reloc, /* special_function */
-        "R_PPC64_ADDR14_BRTAKEN",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR14_BRTAKEN, 2, 16, 0x0000fffc, 0, FALSE, signed,
+       ppc64_elf_brtaken_reloc),
 
   /* An absolute 16 bit branch, for which bit 10 should be set to
      indicate that the branch is not expected to be taken.  The lower
      two bits must be zero.  */
-  HOWTO (R_PPC64_ADDR14_BRNTAKEN, /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_brtaken_reloc, /* special_function */
-        "R_PPC64_ADDR14_BRNTAKEN",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR14_BRNTAKEN, 2, 16, 0x0000fffc, 0, FALSE, signed,
+       ppc64_elf_brtaken_reloc),
 
   /* A relative 26 bit branch; the lower two bits must be zero.  */
-  HOWTO (R_PPC64_REL24,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        26,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_branch_reloc, /* special_function */
-        "R_PPC64_REL24",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x03fffffc,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL24, 2, 26, 0x03fffffc, 0, TRUE, signed,
+       ppc64_elf_branch_reloc),
+
+  /* A variant of R_PPC64_REL24, used when r2 is not the toc pointer.  */
+  HOW (R_PPC64_REL24_NOTOC, 2, 26, 0x03fffffc, 0, TRUE, signed,
+       ppc64_elf_branch_reloc),
 
   /* A relative 16 bit branch; the lower two bits must be zero.  */
-  HOWTO (R_PPC64_REL14,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_branch_reloc, /* special_function */
-        "R_PPC64_REL14",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL14, 2, 16, 0x0000fffc, 0, TRUE, signed,
+       ppc64_elf_branch_reloc),
 
   /* A relative 16 bit branch.  Bit 10 should be set to indicate that
      the branch is expected to be taken.  The lower two bits must be
      zero.  */
-  HOWTO (R_PPC64_REL14_BRTAKEN,        /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_brtaken_reloc, /* special_function */
-        "R_PPC64_REL14_BRTAKEN", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL14_BRTAKEN, 2, 16, 0x0000fffc, 0, TRUE, signed,
+       ppc64_elf_brtaken_reloc),
 
   /* A relative 16 bit branch.  Bit 10 should be set to indicate that
      the branch is not expected to be taken.  The lower two bits must
      be zero.  */
-  HOWTO (R_PPC64_REL14_BRNTAKEN, /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_brtaken_reloc, /* special_function */
-        "R_PPC64_REL14_BRNTAKEN",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0x0000fffc,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL14_BRNTAKEN, 2, 16, 0x0000fffc, 0, TRUE, signed,
+       ppc64_elf_brtaken_reloc),
 
   /* Like R_PPC64_ADDR16, but referring to the GOT table entry for the
      symbol.  */
-  HOWTO (R_PPC64_GOT16,                /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_LO, but referring to the GOT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_GOT16_LO,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16_LO",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_HI, but referring to the GOT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_GOT16_HI,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed,/* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16_HI",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_HA, but referring to the GOT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_GOT16_HA,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed,/* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16_HA",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* This is used only by the dynamic linker.  The symbol should exist
      both in the object being run and in some shared library.  The
      dynamic linker copies the data addressed by the symbol from the
      shared library into the object, because the object being
      run has to have the data at some particular address.  */
-  HOWTO (R_PPC64_COPY,         /* type */
-        0,                     /* rightshift */
-        0,                     /* this one is variable size */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_COPY",        /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_COPY, 0, 0, 0, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR64, but used when setting global offset table
      entries.  */
-  HOWTO (R_PPC64_GLOB_DAT,     /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc,  /* special_function */
-        "R_PPC64_GLOB_DAT",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GLOB_DAT, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Created by the link editor.  Marks a procedure linkage table
      entry for a symbol.  */
-  HOWTO (R_PPC64_JMP_SLOT,     /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_JMP_SLOT",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_JMP_SLOT, 0, 0, 0, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Used only by the dynamic linker.  When the object is run, this
      doubleword64 is set to the load address of the object, plus the
      addend.  */
-  HOWTO (R_PPC64_RELATIVE,     /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_RELATIVE",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_RELATIVE, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* Like R_PPC64_ADDR32, but may be unaligned.  */
-  HOWTO (R_PPC64_UADDR32,      /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_UADDR32",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffffffff,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_UADDR32, 2, 32, 0xffffffff, 0, FALSE, bitfield,
+       bfd_elf_generic_reloc),
 
   /* Like R_PPC64_ADDR16, but may be unaligned.  */
-  HOWTO (R_PPC64_UADDR16,      /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_UADDR16",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_UADDR16, 1, 16, 0xffff, 0, FALSE, bitfield,
+       bfd_elf_generic_reloc),
 
   /* 32-bit PC relative.  */
-  HOWTO (R_PPC64_REL32,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        /* FIXME: Verify.  Was complain_overflow_bitfield.  */
-        complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL32",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffffffff,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL32, 2, 32, 0xffffffff, 0, TRUE, signed,
+       bfd_elf_generic_reloc),
 
   /* 32-bit relocation to the symbol's procedure linkage table.  */
-  HOWTO (R_PPC64_PLT32,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT32",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffffffff,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT32, 2, 32, 0xffffffff, 0, FALSE, bitfield,
+       ppc64_elf_unhandled_reloc),
 
   /* 32-bit PC relative relocation to the symbol's procedure linkage table.
      FIXME: R_PPC64_PLTREL32 not supported.  */
-  HOWTO (R_PPC64_PLTREL32,     /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_PLTREL32",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffffffff,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_PLTREL32, 2, 32, 0xffffffff, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_LO, but referring to the PLT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_PLT16_LO,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT16_LO",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_HI, but referring to the PLT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_PLT16_HI,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT16_HI",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16_HA, but referring to the PLT table entry for
      the symbol.  */
-  HOWTO (R_PPC64_PLT16_HA,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT16_HA",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* 16-bit section relative relocation.  */
-  HOWTO (R_PPC64_SECTOFF,      /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_sectoff_reloc, /* special_function */
-        "R_PPC64_SECTOFF",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_sectoff_reloc),
 
   /* Like R_PPC64_SECTOFF, but no overflow warning.  */
-  HOWTO (R_PPC64_SECTOFF_LO,   /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_sectoff_reloc, /* special_function */
-        "R_PPC64_SECTOFF_LO",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_sectoff_reloc),
 
   /* 16-bit upper half section relative relocation.  */
-  HOWTO (R_PPC64_SECTOFF_HI,   /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_sectoff_reloc, /* special_function */
-        "R_PPC64_SECTOFF_HI",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_sectoff_reloc),
 
   /* 16-bit upper half adjusted section relative relocation.  */
-  HOWTO (R_PPC64_SECTOFF_HA,   /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_sectoff_ha_reloc, /* special_function */
-        "R_PPC64_SECTOFF_HA",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_sectoff_ha_reloc),
 
   /* Like R_PPC64_REL24 without touching the two least significant bits.  */
-  HOWTO (R_PPC64_REL30,                /* type */
-        2,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        30,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL30",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffffffc,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL30, 2, 30, 0xfffffffc, 2, TRUE, dont,
+       bfd_elf_generic_reloc),
 
   /* Relocs in the 64-bit PowerPC ELF ABI, not in the 32-bit ABI.  */
 
   /* A standard 64-bit relocation.  */
-  HOWTO (R_PPC64_ADDR64,       /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR64",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* The bits 32-47 of an address.  */
-  HOWTO (R_PPC64_ADDR16_HIGHER,        /* type */
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_HIGHER", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGHER, 1, 16, 0xffff, 32, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* The bits 32-47 of an address, plus 1 if the contents of the low
      16 bits, treated as a signed number, is negative.  */
-  HOWTO (R_PPC64_ADDR16_HIGHERA, /* type */
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_ha_reloc,    /* special_function */
-        "R_PPC64_ADDR16_HIGHERA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGHERA, 1, 16, 0xffff, 32, FALSE, dont,
+       ppc64_elf_ha_reloc),
 
   /* The bits 48-63 of an address.  */
-  HOWTO (R_PPC64_ADDR16_HIGHEST,/* type */
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_HIGHEST", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGHEST, 1, 16, 0xffff, 48, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* The bits 48-63 of an address, plus 1 if the contents of the low
      16 bits, treated as a signed number, is negative.  */
-  HOWTO (R_PPC64_ADDR16_HIGHESTA,/* type */
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_ha_reloc,    /* special_function */
-        "R_PPC64_ADDR16_HIGHESTA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGHESTA, 1, 16, 0xffff, 48, FALSE, dont,
+       ppc64_elf_ha_reloc),
 
   /* Like ADDR64, but may be unaligned.  */
-  HOWTO (R_PPC64_UADDR64,      /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_UADDR64",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_UADDR64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* 64-bit relative relocation.  */
-  HOWTO (R_PPC64_REL64,                /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL64",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL64, 4, 64, 0xffffffffffffffffULL, 0, TRUE, dont,
+       bfd_elf_generic_reloc),
 
   /* 64-bit relocation to the symbol's procedure linkage table.  */
-  HOWTO (R_PPC64_PLT64,                /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT64",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* 64-bit PC relative relocation to the symbol's procedure linkage
      table.  */
   /* FIXME: R_PPC64_PLTREL64 not supported.  */
-  HOWTO (R_PPC64_PLTREL64,     /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTREL64",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_PLTREL64, 4, 64, 0xffffffffffffffffULL, 0, TRUE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* 16 bit TOC-relative relocation.  */
-
   /* R_PPC64_TOC16       47       half16*      S + A - .TOC.  */
-  HOWTO (R_PPC64_TOC16,                /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_toc_reloc,   /* special_function */
-        "R_PPC64_TOC16",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_toc_reloc),
 
   /* 16 bit TOC-relative relocation without overflow.  */
-
   /* R_PPC64_TOC16_LO    48       half16        #lo (S + A - .TOC.)  */
-  HOWTO (R_PPC64_TOC16_LO,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_toc_reloc,   /* special_function */
-        "R_PPC64_TOC16_LO",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_toc_reloc),
 
   /* 16 bit TOC-relative relocation, high 16 bits.  */
-
   /* R_PPC64_TOC16_HI    49       half16        #hi (S + A - .TOC.)  */
-  HOWTO (R_PPC64_TOC16_HI,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_toc_reloc,   /* special_function */
-        "R_PPC64_TOC16_HI",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_toc_reloc),
 
   /* 16 bit TOC-relative relocation, high 16 bits, plus 1 if the
      contents of the low 16 bits, treated as a signed number, is
      negative.  */
-
   /* R_PPC64_TOC16_HA    50       half16        #ha (S + A - .TOC.)  */
-  HOWTO (R_PPC64_TOC16_HA,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_toc_ha_reloc, /* special_function */
-        "R_PPC64_TOC16_HA",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_toc_ha_reloc),
 
   /* 64-bit relocation; insert value of TOC base (.TOC.).  */
-
   /* R_PPC64_TOC                 51       doubleword64  .TOC.  */
-  HOWTO (R_PPC64_TOC,          /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_toc64_reloc, /* special_function */
-        "R_PPC64_TOC",         /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_toc64_reloc),
 
   /* Like R_PPC64_GOT16, but also informs the link editor that the
      value to relocate may (!) refer to a PLT entry which the link
@@ -1034,1097 +556,429 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
      The link editor may also skip all of this and just (c) emit a
      R_PPC64_GLOB_DAT to tie the symbol to the GOT entry.  */
   /* FIXME: R_PPC64_PLTGOT16 not implemented.  */
-    HOWTO (R_PPC64_PLTGOT16,   /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+    HOW (R_PPC64_PLTGOT16, 1, 16, 0xffff, 0, FALSE,signed,
+         ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_PLTGOT16, but without overflow.  */
   /* FIXME: R_PPC64_PLTGOT16_LO not implemented.  */
-  HOWTO (R_PPC64_PLTGOT16_LO,  /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16_LO", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLTGOT16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_PLT_GOT16, but using bits 16-31 of the address.  */
   /* FIXME: R_PPC64_PLTGOT16_HI not implemented.  */
-  HOWTO (R_PPC64_PLTGOT16_HI,  /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLTGOT16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_PLT_GOT16, but using bits 16-31 of the address, plus
      1 if the contents of the low 16 bits, treated as a signed number,
      is negative.  */
   /* FIXME: R_PPC64_PLTGOT16_HA not implemented.  */
-  HOWTO (R_PPC64_PLTGOT16_HA,  /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLTGOT16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_ADDR16, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_ADDR16_DS,    /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_DS",   /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       bfd_elf_generic_reloc),
 
   /* Like R_PPC64_ADDR16_LO, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_ADDR16_LO_DS, /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_LO_DS",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* Like R_PPC64_GOT16, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_GOT16_DS,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16_DS",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_GOT16_LO, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_GOT16_LO_DS,  /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_PLT16_LO, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_PLT16_LO_DS,  /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLT16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLT16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_SECTOFF, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_SECTOFF_DS,   /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc64_elf_sectoff_reloc, /* special_function */
-        "R_PPC64_SECTOFF_DS",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_sectoff_reloc),
 
   /* Like R_PPC64_SECTOFF_LO, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_SECTOFF_LO_DS, /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_sectoff_reloc, /* special_function */
-        "R_PPC64_SECTOFF_LO_DS",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_SECTOFF_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_sectoff_reloc),
 
   /* Like R_PPC64_TOC16, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_TOC16_DS,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_toc_reloc,   /* special_function */
-        "R_PPC64_TOC16_DS",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_toc_reloc),
 
   /* Like R_PPC64_TOC16_LO, but for instructions with a DS field.  */
-  HOWTO (R_PPC64_TOC16_LO_DS,  /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_toc_reloc,   /* special_function */
-        "R_PPC64_TOC16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TOC16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_toc_reloc),
 
   /* Like R_PPC64_PLTGOT16, but for instructions with a DS field.  */
   /* FIXME: R_PPC64_PLTGOT16_DS not implemented.  */
-  HOWTO (R_PPC64_PLTGOT16_DS,  /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLTGOT16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_PLTGOT16_LO, but for instructions with a DS field.  */
   /* FIXME: R_PPC64_PLTGOT16_LO not implemented.  */
-  HOWTO (R_PPC64_PLTGOT16_LO_DS,/* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_PLTGOT16_LO_DS",/* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_PLTGOT16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Marker relocs for TLS.  */
-  HOWTO (R_PPC64_TLS,
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_TLS",         /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
-
-  HOWTO (R_PPC64_TLSGD,
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_TLSGD",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
-
-  HOWTO (R_PPC64_TLSLD,
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_TLSLD",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
-
-  HOWTO (R_PPC64_TOCSAVE,
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_TOCSAVE",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TLS, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_TLSGD, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_TLSLD, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  /* Marker reloc for optimizing r2 save in prologue rather than on
+     each plt call stub.  */
+  HOW (R_PPC64_TOCSAVE, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  /* Marker relocs on inline plt call instructions.  */
+  HOW (R_PPC64_PLTSEQ, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_PLTCALL, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* Computes the load module index of the load module that contains the
      definition of its TLS sym.  */
-  HOWTO (R_PPC64_DTPMOD64,
-        0,                     /* rightshift */
-        4,                     /* size (0 = byte, 1 = short, 2 = long) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPMOD64",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPMOD64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Computes a dtv-relative displacement, the difference between the value
      of sym+add and the base address of the thread-local storage block that
      contains the definition of sym, minus 0x8000.  */
-  HOWTO (R_PPC64_DTPREL64,
-        0,                     /* rightshift */
-        4,                     /* size (0 = byte, 1 = short, 2 = long) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL64",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* A 16 bit dtprel reloc.  */
-  HOWTO (R_PPC64_DTPREL16,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16, but no overflow.  */
-  HOWTO (R_PPC64_DTPREL16_LO,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_LO", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_LO, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_HI, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HIGHER,
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGHER", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGHER, 1, 16, 0xffff, 32, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_HIGHER, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HIGHERA,
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGHERA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGHERA, 1, 16, 0xffff, 32, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_HIGHER, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HIGHEST,
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGHEST", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGHEST, 1, 16, 0xffff, 48, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_HIGHEST, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_DTPREL16_HIGHESTA,
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGHESTA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGHESTA, 1, 16, 0xffff, 48, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16, but for insns with a DS field.  */
-  HOWTO (R_PPC64_DTPREL16_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like DTPREL16_DS, but no overflow.  */
-  HOWTO (R_PPC64_DTPREL16_LO_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Computes a tp-relative displacement, the difference between the value of
      sym+add and the value of the thread pointer (r13).  */
-  HOWTO (R_PPC64_TPREL64,
-        0,                     /* rightshift */
-        4,                     /* size (0 = byte, 1 = short, 2 = long) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL64",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL64, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* A 16 bit tprel reloc.  */
-  HOWTO (R_PPC64_TPREL16,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16",     /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16, but no overflow.  */
-  HOWTO (R_PPC64_TPREL16_LO,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_LO",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_LO, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HI",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HA",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_HI, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HIGHER,
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGHER",      /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGHER, 1, 16, 0xffff, 32, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_HIGHER, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HIGHERA,
-        32,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGHERA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGHERA, 1, 16, 0xffff, 32, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_HIGHER, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HIGHEST,
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGHEST", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGHEST, 1, 16, 0xffff, 48, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_HIGHEST, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_TPREL16_HIGHESTA,
-        48,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGHESTA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGHESTA, 1, 16, 0xffff, 48, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16, but for insns with a DS field.  */
-  HOWTO (R_PPC64_TPREL16_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_DS",  /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like TPREL16_DS, but no overflow.  */
-  HOWTO (R_PPC64_TPREL16_LO_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Allocates two contiguous entries in the GOT to hold a tls_index structure,
      with values (sym+add)@dtpmod and (sym+add)@dtprel, and computes the offset
      to the first entry relative to the TOC base (r2).  */
-  HOWTO (R_PPC64_GOT_TLSGD16,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSGD16", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSGD16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSGD16, but no overflow.  */
-  HOWTO (R_PPC64_GOT_TLSGD16_LO,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSGD16_LO", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSGD16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSGD16_LO, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_GOT_TLSGD16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSGD16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSGD16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSGD16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_GOT_TLSGD16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSGD16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSGD16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Allocates two contiguous entries in the GOT to hold a tls_index structure,
      with values (sym+add)@dtpmod and zero, and computes the offset to the
      first entry relative to the TOC base (r2).  */
-  HOWTO (R_PPC64_GOT_TLSLD16,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSLD16", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSLD16, 1, 16, 0xffff, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSLD16, but no overflow.  */
-  HOWTO (R_PPC64_GOT_TLSLD16_LO,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSLD16_LO", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSLD16_LO, 1, 16, 0xffff, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSLD16_LO, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_GOT_TLSLD16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSLD16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSLD16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TLSLD16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_GOT_TLSLD16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TLSLD16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TLSLD16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Allocates an entry in the GOT with value (sym+add)@dtprel, and computes
      the offset to the entry relative to the TOC base (r2).  */
-  HOWTO (R_PPC64_GOT_DTPREL16_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_DTPREL16_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_DTPREL16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_DTPREL16_DS, but no overflow.  */
-  HOWTO (R_PPC64_GOT_DTPREL16_LO_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_DTPREL16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_DTPREL16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_DTPREL16_LO_DS, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_GOT_DTPREL16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_DTPREL16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_DTPREL16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_DTPREL16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_GOT_DTPREL16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_DTPREL16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_DTPREL16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Allocates an entry in the GOT with value (sym+add)@tprel, and computes the
      offset to the entry relative to the TOC base (r2).  */
-  HOWTO (R_PPC64_GOT_TPREL16_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TPREL16_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TPREL16_DS, 1, 16, 0xfffc, 0, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TPREL16_DS, but no overflow.  */
-  HOWTO (R_PPC64_GOT_TPREL16_LO_DS,
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TPREL16_LO_DS", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xfffc,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TPREL16_LO_DS, 1, 16, 0xfffc, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TPREL16_LO_DS, but next higher group of 16 bits.  */
-  HOWTO (R_PPC64_GOT_TPREL16_HI,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TPREL16_HI", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TPREL16_HI, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
 
   /* Like GOT_TPREL16_HI, but adjust for low 16 bits.  */
-  HOWTO (R_PPC64_GOT_TPREL16_HA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_GOT_TPREL16_HA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
-
-  HOWTO (R_PPC64_JMP_IREL,     /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_JMP_IREL",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
-
-  HOWTO (R_PPC64_IRELATIVE,    /* type */
-        0,                     /* rightshift */
-        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
-        64,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_IRELATIVE",   /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        ONES (64),             /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GOT_TPREL16_HA, 1, 16, 0xffff, 16, FALSE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_JMP_IREL, 0, 0, 0, 0, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_IRELATIVE, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* A 16 bit relative relocation.  */
-  HOWTO (R_PPC64_REL16,                /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL16",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL16, 1, 16, 0xffff, 0, TRUE, signed,
+       bfd_elf_generic_reloc),
 
   /* A 16 bit relative relocation without overflow.  */
-  HOWTO (R_PPC64_REL16_LO,     /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL16_LO",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL16_LO, 1, 16, 0xffff, 0, TRUE, dont,
+       bfd_elf_generic_reloc),
 
   /* The high order 16 bits of a relative address.  */
-  HOWTO (R_PPC64_REL16_HI,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_REL16_HI",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL16_HI, 1, 16, 0xffff, 16, TRUE, signed,
+       bfd_elf_generic_reloc),
 
   /* The high order 16 bits of a relative address, plus 1 if the contents of
      the low 16 bits, treated as a signed number, is negative.  */
-  HOWTO (R_PPC64_REL16_HA,     /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        TRUE,                  /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_signed, /* complain_on_overflow */
-        ppc64_elf_ha_reloc,    /* special_function */
-        "R_PPC64_REL16_HA",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+  HOW (R_PPC64_REL16_HA, 1, 16, 0xffff, 16, TRUE, signed,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGH, 1, 16, 0xffff, 16, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHA, 1, 16, 0xffff, 16, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHER, 1, 16, 0xffff, 32, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHERA, 1, 16, 0xffff, 32, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHEST, 1, 16, 0xffff, 48, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHESTA, 1, 16, 0xffff, 48, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  /* Like R_PPC64_REL16_HA but for split field in addpcis.  */
+  HOW (R_PPC64_REL16DX_HA, 2, 16, 0x1fffc1, 16, TRUE, signed,
+       ppc64_elf_ha_reloc),
+
+  /* A split-field reloc for addpcis, non-relative (gas internal use only).  */
+  HOW (R_PPC64_16DX_HA, 2, 16, 0x1fffc1, 16, FALSE, signed,
+       ppc64_elf_ha_reloc),
 
   /* Like R_PPC64_ADDR16_HI, but no overflow.  */
-  HOWTO (R_PPC64_ADDR16_HIGH,  /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR16_HIGH", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGH, 1, 16, 0xffff, 16, FALSE, dont,
+       bfd_elf_generic_reloc),
 
   /* Like R_PPC64_ADDR16_HA, but no overflow.  */
-  HOWTO (R_PPC64_ADDR16_HIGHA, /* type */
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_ha_reloc,    /* special_function */
-        "R_PPC64_ADDR16_HIGHA",        /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_ADDR16_HIGHA, 1, 16, 0xffff, 16, FALSE, dont,
+       ppc64_elf_ha_reloc),
 
   /* Like R_PPC64_DTPREL16_HI, but no overflow.  */
-  HOWTO (R_PPC64_DTPREL16_HIGH,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGH", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGH, 1, 16, 0xffff, 16, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_DTPREL16_HA, but no overflow.  */
-  HOWTO (R_PPC64_DTPREL16_HIGHA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_DTPREL16_HIGHA", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_DTPREL16_HIGHA, 1, 16, 0xffff, 16, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_TPREL16_HI, but no overflow.  */
-  HOWTO (R_PPC64_TPREL16_HIGH,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGH",        /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGH, 1, 16, 0xffff, 16, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
 
   /* Like R_PPC64_TPREL16_HA, but no overflow.  */
-  HOWTO (R_PPC64_TPREL16_HIGHA,
-        16,                    /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        ppc64_elf_unhandled_reloc, /* special_function */
-        "R_PPC64_TPREL16_HIGHA",       /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_TPREL16_HIGHA, 1, 16, 0xffff, 16, FALSE, dont,
+       ppc64_elf_unhandled_reloc),
+
+  /* Marker reloc on ELFv2 large-model function entry.  */
+  HOW (R_PPC64_ENTRY, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  /* Like ADDR64, but use local entry point of function.  */
+  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.  */
-  HOWTO (R_PPC64_GNU_VTINHERIT,        /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        NULL,                  /* special_function */
-        "R_PPC64_GNU_VTINHERIT", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, FALSE, dont,
+       NULL),
 
   /* GNU extension to record C++ vtable member usage.  */
-  HOWTO (R_PPC64_GNU_VTENTRY,  /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont, /* complain_on_overflow */
-        NULL,                  /* special_function */
-        "R_PPC64_GNU_VTENTRY", /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOW (R_PPC64_GNU_VTENTRY, 0, 0, 0, 0, FALSE, dont,
+       NULL),
 };
 
 \f
@@ -2136,20 +990,16 @@ ppc_howto_init (void)
 {
   unsigned int i, type;
 
-  for (i = 0;
-       i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
-       i++)
+  for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++)
     {
       type = ppc64_elf_howto_raw[i].type;
-      BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
-                         / sizeof (ppc64_elf_howto_table[0])));
+      BFD_ASSERT (type < ARRAY_SIZE (ppc64_elf_howto_table));
       ppc64_elf_howto_table[type] = &ppc64_elf_howto_raw[i];
     }
 }
 
 static reloc_howto_type *
-ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
-                            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;
 
@@ -2160,6 +1010,10 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
   switch (code)
     {
     default:
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd,
+                         (int) code);
+      bfd_set_error (bfd_error_bad_value);
       return NULL;
 
     case BFD_RELOC_NONE:                       r = R_PPC64_NONE;
@@ -2188,6 +1042,8 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       break;
     case BFD_RELOC_PPC_B26:                    r = R_PPC64_REL24;
       break;
+    case BFD_RELOC_PPC64_REL24_NOTOC:          r = R_PPC64_REL24_NOTOC;
+      break;
     case BFD_RELOC_PPC_B16:                    r = R_PPC64_REL14;
       break;
     case BFD_RELOC_PPC_B16_BRTAKEN:            r = R_PPC64_REL14_BRTAKEN;
@@ -2284,6 +1140,7 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       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;
@@ -2384,6 +1241,72 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       break;
     case BFD_RELOC_HI16_S_PCREL:               r = R_PPC64_REL16_HA;
       break;
+    case BFD_RELOC_PPC64_REL16_HIGH:           r = R_PPC64_REL16_HIGH;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHA:          r = R_PPC64_REL16_HIGHA;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHER:         r = R_PPC64_REL16_HIGHER;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHERA:                r = R_PPC64_REL16_HIGHERA;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHEST:                r = R_PPC64_REL16_HIGHEST;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHESTA:       r = R_PPC64_REL16_HIGHESTA;
+      break;
+    case BFD_RELOC_PPC_16DX_HA:                        r = R_PPC64_16DX_HA;
+      break;
+    case BFD_RELOC_PPC_REL16DX_HA:             r = R_PPC64_REL16DX_HA;
+      break;
+    case BFD_RELOC_PPC64_ENTRY:                        r = R_PPC64_ENTRY;
+      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;
@@ -2394,25 +1317,40 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 };
 
 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;
-
-  for (i = 0;
-       i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
-       i++)
+  static char *compat_map[][2] = {
+    { "R_PPC64_GOT_TLSGD34", "R_PPC64_GOT_TLSGD_PCREL34" },
+    { "R_PPC64_GOT_TLSLD34", "R_PPC64_GOT_TLSLD_PCREL34" },
+    { "R_PPC64_GOT_TPREL34", "R_PPC64_GOT_TPREL_PCREL34" },
+    { "R_PPC64_GOT_DTPREL34", "R_PPC64_GOT_DTPREL_PCREL34" }
+  };
+
+  for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++)
     if (ppc64_elf_howto_raw[i].name != NULL
        && strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
       return &ppc64_elf_howto_raw[i];
 
+  /* Handle old names of relocations in case they were used by
+     .reloc directives.
+     FIXME: Remove this soon.  Mapping the reloc names is very likely
+     completely unnecessary.  */
+  for (i = 0; i < ARRAY_SIZE (compat_map); i++)
+    if (strcasecmp (compat_map[i][0], r_name) == 0)
+      {
+       _bfd_error_handler (_("warning: %s should be used rather than %s"),
+                           compat_map[i][1], compat_map[i][0]);
+       return ppc64_elf_reloc_name_lookup (abfd, compat_map[i][1]);
+      }
+
   return NULL;
 }
 
 /* Set the howto pointer for a PowerPC ELF reloc.  */
 
-static void
-ppc64_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr,
+static bfd_boolean
+ppc64_elf_info_to_howto (bfd *abfd, arelent *cache_ptr,
                         Elf_Internal_Rela *dst)
 {
   unsigned int type;
@@ -2422,14 +1360,25 @@ ppc64_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr,
     ppc_howto_init ();
 
   type = ELF64_R_TYPE (dst->r_info);
-  if (type >= (sizeof (ppc64_elf_howto_table)
-              / sizeof (ppc64_elf_howto_table[0])))
+  if (type >= ARRAY_SIZE (ppc64_elf_howto_table))
     {
-      (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
-                            abfd, (int) type);
-      type = R_PPC64_NONE;
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                         abfd, type);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
     }
   cache_ptr->howto = ppc64_elf_howto_table[type];
+  if (cache_ptr->howto == NULL || cache_ptr->howto->name == NULL)
+    {
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                         abfd, type);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
 /* Handle the R_PPC64_ADDR16_HA and similar relocs.  */
@@ -2439,6 +1388,11 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
                    void *data, asection *input_section,
                    bfd *output_bfd, char **error_message)
 {
+  enum elf_ppc64_reloc_type r_type;
+  long insn;
+  bfd_size_type octets;
+  bfd_vma value;
+
   /* If this is a relocatable link (output_bfd test tells us), just
      call the generic function.  Any adjustment will be done at final
      link time.  */
@@ -2446,11 +1400,39 @@ 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);
 
-  /* 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.  */
-  reloc_entry->addend += 0x8000;
-  return bfd_reloc_continue;
+  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;
+
+  value = 0;
+  if (!bfd_is_com_section (symbol->section))
+    value = symbol->value;
+  value += (reloc_entry->addend
+           + symbol->section->output_offset
+           + symbol->section->output_section->vma);
+  value -= (reloc_entry->address
+           + input_section->output_offset
+           + input_section->output_section->vma);
+  value = (bfd_signed_vma) value >> 16;
+
+  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);
+  bfd_put_32 (abfd, insn, (bfd_byte *) data + octets);
+  if (value + 0x8000 > 0xffff)
+    return bfd_reloc_overflow;
+  return bfd_reloc_ok;
 }
 
 static bfd_reloc_status_type
@@ -2473,6 +1455,30 @@ ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
                                      + symbol->section->output_section->vma
                                      + symbol->section->output_offset);
     }
+  else
+    {
+      elf_symbol_type *elfsym = (elf_symbol_type *) symbol;
+
+      if (symbol->section->owner != abfd
+         && symbol->section->owner != NULL
+         && abiversion (symbol->section->owner) >= 2)
+       {
+         unsigned int i;
+
+         for (i = 0; i < symbol->section->owner->symcount; ++i)
+           {
+             asymbol *symdef = symbol->section->owner->outsymbols[i];
+
+             if (strcmp (symdef->name, symbol->name) == 0)
+               {
+                 elfsym = (elf_symbol_type *) symdef;
+                 break;
+               }
+           }
+       }
+      reloc_entry->addend
+       += PPC64_LOCAL_ENTRY_OFFSET (elfsym->internal_elf_sym.st_other);
+    }
   return bfd_reloc_continue;
 }
 
@@ -2494,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);
 
-  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;
@@ -2644,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);
 
-  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;
 }
 
+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,
@@ -2698,11 +1746,11 @@ struct got_entry
 
   /* Reference count until size_dynamic_sections, GOT offset thereafter.  */
   union
-    {
-      bfd_signed_vma refcount;
-      bfd_vma offset;
-      struct got_entry *ent;
-    } got;
+  {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+    struct got_entry *ent;
+  } got;
 };
 
 /* The same for PLT.  */
@@ -2713,10 +1761,10 @@ struct plt_entry
   bfd_vma addend;
 
   union
-    {
-      bfd_signed_vma refcount;
-      bfd_vma offset;
-    } plt;
+  {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+  } plt;
 };
 
 struct ppc64_elf_obj_tdata
@@ -2735,7 +1783,8 @@ struct ppc64_elf_obj_tdata
      sections means we potentially need one of these for each input bfd.  */
   struct got_entry tlsld_got;
 
-  union {
+  union
+  {
     /* A copy of relocs before they are modified for --emit-relocs.  */
     Elf_Internal_Rela *relocs;
 
@@ -2750,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 PLT/GOT/TOC relocs that can be optimised are present in
+     this file.  */
+  unsigned int has_optrel : 1;
 };
 
 #define ppc64_elf_tdata(bfd) \
@@ -2772,12 +1825,15 @@ ppc64_elf_mkobject (bfd *abfd)
 }
 
 /* Fix bad default arch selected for a 64 bit input bfd when the
-   default is 32 bit.  */
+   default is 32 bit.  Also select arch based on apuinfo.  */
 
 static bfd_boolean
 ppc64_elf_object_p (bfd *abfd)
 {
-  if (abfd->arch_info->the_default && abfd->arch_info->bits_per_word == 32)
+  if (!abfd->arch_info->the_default)
+    return TRUE;
+
+  if (abfd->arch_info->bits_per_word == 32)
     {
       Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd);
 
@@ -2788,7 +1844,7 @@ ppc64_elf_object_p (bfd *abfd)
          BFD_ASSERT (abfd->arch_info->bits_per_word == 64);
        }
     }
-  return TRUE;
+  return _bfd_elf_ppc_set_arch (abfd);
 }
 
 /* Support for core dump NOTE sections.  */
@@ -2843,13 +1899,24 @@ ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type,
 
     case NT_PRPSINFO:
       {
-       char data[136];
+       char data[136] ATTRIBUTE_NONSTRING;
        va_list ap;
 
        va_start (ap, note_type);
        memset (data, 0, sizeof (data));
        strncpy (data + 40, va_arg (ap, const char *), 16);
+#if GCC_VERSION == 8000 || GCC_VERSION == 8001
+       DIAGNOSTIC_PUSH;
+       /* GCC 8.0 and 8.1 warn about 80 equals destination size with
+          -Wstringop-truncation:
+          https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85643
+        */
+       DIAGNOSTIC_IGNORE_STRINGOP_TRUNCATION;
+#endif
        strncpy (data + 56, va_arg (ap, const char *), 80);
+#if GCC_VERSION == 8000 || GCC_VERSION == 8001
+       DIAGNOSTIC_POP;
+#endif
        va_end (ap);
        return elfcore_write_note (abfd, buf, bufsiz,
                                   "CORE", note_type, data, sizeof (data));
@@ -2881,15 +1948,15 @@ ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type,
 
 /* Add extra PPC sections.  */
 
-static const struct bfd_elf_special_section ppc64_elf_special_sections[]=
+static const struct bfd_elf_special_section ppc64_elf_special_sections[] =
 {
-  { STRING_COMMA_LEN (".plt"),    0, SHT_NOBITS,   0 },
-  { STRING_COMMA_LEN (".sbss"),  -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".plt"),   0, SHT_NOBITS,   0 },
+  { STRING_COMMA_LEN (".sbss"),         -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
   { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { STRING_COMMA_LEN (".toc"),    0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { STRING_COMMA_LEN (".toc1"),   0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".toc"),   0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".toc1"),          0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
   { STRING_COMMA_LEN (".tocbss"), 0, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { NULL,                     0,  0, 0,            0 }
+  { NULL,                    0,  0, 0,            0 }
 };
 
 enum _ppc64_sec_type {
@@ -2904,7 +1971,9 @@ struct _ppc64_elf_section_data
 
   union
   {
-    /* An array with one entry for each opd function descriptor.  */
+    /* An array with one entry for each opd function descriptor,
+       and some spares since opd entries may be either 16 or 24 bytes.  */
+#define OPD_NDX(OFF) ((OFF) >> 4)
     struct _opd_sec_data
     {
       /* Points to the function code section for local opd entries.  */
@@ -2930,6 +1999,13 @@ struct _ppc64_elf_section_data
   /* Flag set when small branches are detected.  Used to
      select suitable defaults for the stub group size.  */
   unsigned int has_14bit_branch: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) \
@@ -2941,7 +2017,7 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   if (!sec->used_by_bfd)
     {
       struct _ppc64_elf_section_data *sdata;
-      bfd_size_type amt = sizeof (*sdata);
+      size_t amt = sizeof (*sdata);
 
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
@@ -2952,6 +2028,18 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec)
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
+static bfd_boolean
+ppc64_elf_section_flags (const Elf_Internal_Shdr *hdr)
+{
+  const char *name = hdr->bfd_section->name;
+
+  if (strncmp (name, ".sbss", 5) == 0
+      || strncmp (name, ".sdata", 6) == 0)
+    hdr->bfd_section->flags |= SEC_SMALL_DATA;
+
+  return TRUE;
+}
+
 static struct _opd_sec_data *
 get_opd_info (asection * sec)
 {
@@ -2961,30 +2049,18 @@ get_opd_info (asection * sec)
     return &ppc64_elf_section_data (sec)->u.opd;
   return NULL;
 }
-
-static inline int
-abiversion (bfd *abfd)
-{
-  return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI;
-}
-
-static inline void
-set_abiversion (bfd *abfd, int ver)
-{
-  elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI;
-  elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI;
-}
 \f
 /* Parameters for the qsort hook.  */
 static bfd_boolean synthetic_relocatable;
+static asection *synthetic_opd;
 
 /* qsort comparison function for ppc64_elf_get_synthetic_symtab.  */
 
 static int
 compare_symbols (const void *ap, const void *bp)
 {
-  const asymbol *a = * (const asymbol **) ap;
-  const asymbol *b = * (const asymbol **) bp;
+  const asymbol *a = *(const asymbol **) ap;
+  const asymbol *b = *(const asymbol **) bp;
 
   /* Section symbols first.  */
   if ((a->flags & BSF_SECTION_SYM) && !(b->flags & BSF_SECTION_SYM))
@@ -2993,24 +2069,27 @@ compare_symbols (const void *ap, const void *bp)
     return 1;
 
   /* then .opd symbols.  */
-  if (strcmp (a->section->name, ".opd") == 0
-      && strcmp (b->section->name, ".opd") != 0)
-    return -1;
-  if (strcmp (a->section->name, ".opd") != 0
-      && strcmp (b->section->name, ".opd") == 0)
-    return 1;
+  if (synthetic_opd != NULL)
+    {
+      if (strcmp (a->section->name, ".opd") == 0
+         && strcmp (b->section->name, ".opd") != 0)
+       return -1;
+      if (strcmp (a->section->name, ".opd") != 0
+         && strcmp (b->section->name, ".opd") == 0)
+       return 1;
+    }
 
   /* then other code symbols.  */
-  if ((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-      == (SEC_CODE | SEC_ALLOC)
-      && (b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-        != (SEC_CODE | SEC_ALLOC))
+  if (((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+       == (SEC_CODE | SEC_ALLOC))
+      && ((b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+         != (SEC_CODE | SEC_ALLOC)))
     return -1;
 
-  if ((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-      != (SEC_CODE | SEC_ALLOC)
-      && (b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-        == (SEC_CODE | SEC_ALLOC))
+  if (((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+       != (SEC_CODE | SEC_ALLOC))
+      && ((b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+         == (SEC_CODE | SEC_ALLOC)))
     return 1;
 
   if (synthetic_relocatable)
@@ -3054,17 +2133,28 @@ compare_symbols (const void *ap, const void *bp)
   if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0)
     return 1;
 
+  /* Finally, sort on where the symbol is in memory.  The symbols will
+     be in at most two malloc'd blocks, one for static syms, one for
+     dynamic syms, and we distinguish the two blocks above by testing
+     BSF_DYNAMIC.  Since we are sorting the symbol pointers which were
+     originally in the same order as the symbols (and we're not
+     sorting the symbols themselves), this ensures a stable sort.  */
+  if (a < b)
+    return -1;
+  if (a > b)
+    return 1;
   return 0;
 }
 
 /* Search SYMS for a symbol of the given VALUE.  */
 
 static asymbol *
-sym_exists_at (asymbol **syms, long lo, long hi, 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 == -1)
+  if (id == (unsigned) -1)
     {
       while (lo < hi)
        {
@@ -3107,7 +2197,8 @@ section_covers_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *section, void *ptr)
 }
 
 /* Create synthetic symbols, effectively restoring "dot-symbol" function
-   entry syms.  Also generate @plt symbols for the glink branch table.  */
+   entry syms.  Also generate @plt symbols for the glink branch table.
+   Returns count of synthetic symbols in RET or -1 on error.  */
 
 static long
 ppc64_elf_get_synthetic_symtab (bfd *abfd,
@@ -3116,10 +2207,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
                                asymbol **ret)
 {
   asymbol *s;
-  long i;
-  long count;
+  size_t i, j, count;
   char *names;
-  long symcount, codesecsym, codesecsymend, secsymend, opdsymend;
+  size_t symcount, codesecsym, codesecsymend, secsymend, opdsymend;
   asection *opd = NULL;
   bfd_boolean relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
   asymbol **syms;
@@ -3134,71 +2224,104 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
        return 0;
     }
 
-  symcount = static_count;
-  if (!relocatable)
-    symcount += dyn_count;
-  if (symcount == 0)
-    return 0;
-
-  syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
-  if (syms == NULL)
-    return -1;
-
-  if (!relocatable && static_count != 0 && dyn_count != 0)
+  syms = NULL;
+  codesecsym = 0;
+  codesecsymend = 0;
+  secsymend = 0;
+  opdsymend = 0;
+  symcount = 0;
+  if (opd != NULL)
     {
-      /* Use both symbol tables.  */
-      memcpy (syms, static_syms, static_count * sizeof (*syms));
-      memcpy (syms + static_count, dyn_syms, (dyn_count + 1) * sizeof (*syms));
-    }
-  else if (!relocatable && static_count == 0)
-    memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms));
-  else
-    memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
+      symcount = static_count;
+      if (!relocatable)
+       symcount += dyn_count;
+      if (symcount == 0)
+       return 0;
 
-  synthetic_relocatable = relocatable;
-  qsort (syms, symcount, sizeof (*syms), compare_symbols);
+      syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
+      if (syms == NULL)
+       return -1;
 
-  if (!relocatable && symcount > 1)
-    {
-      long j;
-      /* Trim duplicate syms, since we may have merged the normal and
-        dynamic symbols.  Actually, we only care about syms that have
-        different values, so trim any with the same value.  */
-      for (i = 1, j = 1; i < symcount; ++i)
-       if (syms[i - 1]->value + syms[i - 1]->section->vma
-           != syms[i]->value + syms[i]->section->vma)
+      if (!relocatable && static_count != 0 && dyn_count != 0)
+       {
+         /* Use both symbol tables.  */
+         memcpy (syms, static_syms, static_count * sizeof (*syms));
+         memcpy (syms + static_count, dyn_syms,
+                 (dyn_count + 1) * sizeof (*syms));
+       }
+      else if (!relocatable && static_count == 0)
+       memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms));
+      else
+       memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
+
+      /* Trim uninteresting symbols.  Interesting symbols are section,
+        function, and notype symbols.  */
+      for (i = 0, j = 0; i < symcount; ++i)
+       if ((syms[i]->flags & (BSF_FILE | BSF_OBJECT | BSF_THREAD_LOCAL
+                              | BSF_RELC | BSF_SRELC)) == 0)
          syms[j++] = syms[i];
       symcount = j;
-    }
 
-  i = 0;
-  if (strcmp (syms[i]->section->name, ".opd") == 0)
-    ++i;
-  codesecsym = i;
+      synthetic_relocatable = relocatable;
+      synthetic_opd = opd;
+      qsort (syms, symcount, sizeof (*syms), compare_symbols);
 
-  for (; i < symcount; ++i)
-    if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-        != (SEC_CODE | SEC_ALLOC))
-       || (syms[i]->flags & BSF_SECTION_SYM) == 0)
-      break;
-  codesecsymend = i;
+      if (!relocatable && symcount > 1)
+       {
+         /* Trim duplicate syms, since we may have merged the normal
+            and dynamic symbols.  Actually, we only care about syms
+            that have different values, so trim any with the same
+            value.  Don't consider ifunc and ifunc resolver symbols
+            duplicates however, because GDB wants to know whether a
+            text symbol is an ifunc resolver.  */
+         for (i = 1, j = 1; i < symcount; ++i)
+           {
+             const asymbol *s0 = syms[i - 1];
+             const asymbol *s1 = syms[i];
+
+             if ((s0->value + s0->section->vma
+                  != s1->value + s1->section->vma)
+                 || ((s0->flags & BSF_GNU_INDIRECT_FUNCTION)
+                     != (s1->flags & BSF_GNU_INDIRECT_FUNCTION)))
+               syms[j++] = syms[i];
+           }
+         symcount = j;
+       }
 
-  for (; i < symcount; ++i)
-    if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
-      break;
-  secsymend = i;
+      i = 0;
+      /* Note that here and in compare_symbols we can't compare opd and
+        sym->section directly.  With separate debug info files, the
+        symbols will be extracted from the debug file while abfd passed
+        to this function is the real binary.  */
+      if (strcmp (syms[i]->section->name, ".opd") == 0)
+       ++i;
+      codesecsym = i;
+
+      for (; i < symcount; ++i)
+       if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC
+                                        | SEC_THREAD_LOCAL))
+            != (SEC_CODE | SEC_ALLOC))
+           || (syms[i]->flags & BSF_SECTION_SYM) == 0)
+         break;
+      codesecsymend = i;
 
-  for (; i < symcount; ++i)
-    if (strcmp (syms[i]->section->name, ".opd") != 0)
-      break;
-  opdsymend = i;
+      for (; i < symcount; ++i)
+       if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
+         break;
+      secsymend = i;
 
-  for (; i < symcount; ++i)
-    if ((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
-       != (SEC_CODE | SEC_ALLOC))
-      break;
-  symcount = i;
+      for (; i < symcount; ++i)
+       if (strcmp (syms[i]->section->name, ".opd") != 0)
+         break;
+      opdsymend = i;
 
+      for (; i < symcount; ++i)
+       if (((syms[i]->section->flags
+             & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL)))
+           != (SEC_CODE | SEC_ALLOC))
+         break;
+      symcount = i;
+    }
   count = 0;
 
   if (relocatable)
@@ -3206,7 +2329,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
       bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
       arelent *r;
       size_t size;
-      long relcount;
+      size_t relcount;
 
       if (opdsymend == secsymend)
        goto done;
@@ -3250,6 +2373,8 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
            }
        }
 
+      if (size == 0)
+       goto done;
       s = *ret = bfd_malloc (size);
       if (s == NULL)
        {
@@ -3303,17 +2428,17 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
       bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
       bfd_byte *contents = NULL;
       size_t size;
-      long plt_count = 0;
+      size_t plt_count = 0;
       bfd_vma glink_vma = 0, resolv_vma = 0;
       asection *dynamic, *glink = NULL, *relplt = NULL;
       arelent *p;
 
       if (opd != NULL && !bfd_malloc_and_get_section (abfd, opd, &contents))
        {
-       free_contents_and_exit:
-         if (contents)
-           free (contents);
+       free_contents_and_exit_err:
          count = -1;
+       free_contents_and_exit:
+         free (contents);
          goto done;
        }
 
@@ -3344,7 +2469,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
          void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
 
          if (!bfd_malloc_and_get_section (abfd, dynamic, &dynbuf))
-           goto free_contents_and_exit;
+           goto free_contents_and_exit_err;
 
          extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
          swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
@@ -3361,9 +2486,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
 
              if (dyn.d_tag == DT_PPC64_GLINK)
                {
-                 /* The first glink stub starts at offset 32; see
-                    comment in ppc64_elf_finish_dynamic_sections. */
-                 glink_vma = dyn.d_un.d_val + GLINK_CALL_STUB_SIZE - 8 * 4;
+                 /* The first glink stub starts at DT_PPC64_GLINK plus 32.
+                    See comment in ppc64_elf_finish_dynamic_sections. */
+                 glink_vma = dyn.d_un.d_val + 8 * 4;
                  /* The .glink section usually does not survive the final
                     link; search for the section (usually .text) where the
                     glink stubs now reside.  */
@@ -3390,7 +2515,8 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
              insn ^= B_DOT;
              if ((insn & ~0x3fffffc) == 0)
                {
-                 resolv_vma = glink_vma + off + (insn ^ 0x2000000) - 0x2000000;
+                 resolv_vma
+                   = glink_vma + off + (insn ^ 0x2000000) - 0x2000000;
                  break;
                }
              off += 4;
@@ -3405,8 +2531,8 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
          if (relplt != NULL)
            {
              slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
-             if (! (*slurp_relocs) (abfd, relplt, dyn_syms, TRUE))
-               goto free_contents_and_exit;
+             if (!(*slurp_relocs) (abfd, relplt, dyn_syms, TRUE))
+               goto free_contents_and_exit_err;
 
              plt_count = relplt->size / sizeof (Elf64_External_Rela);
              size += plt_count * sizeof (asymbol);
@@ -3421,9 +2547,11 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
            }
        }
 
+      if (size == 0)
+       goto free_contents_and_exit;
       s = *ret = bfd_malloc (size);
       if (s == NULL)
-       goto free_contents_and_exit;
+       goto free_contents_and_exit_err;
 
       names = (char *) (s + count + plt_count + (resolv_vma != 0));
 
@@ -3437,7 +2565,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
          ent = bfd_get_64 (abfd, contents + syms[i]->value);
          if (!sym_exists_at (syms, opdsymend, symcount, -1, ent))
            {
-             long lo, hi;
+             size_t lo, hi;
              size_t len;
              asection *sec = abfd->sections;
 
@@ -3446,7 +2574,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
              hi = codesecsymend;
              while (lo < hi)
                {
-                 long mid = (lo + hi) >> 1;
+                 size_t mid = (lo + hi) >> 1;
                  if (syms[mid]->section->vma < ent)
                    lo = mid + 1;
                  else if (syms[mid]->section->vma > ent)
@@ -3498,7 +2626,8 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
              s->section = glink;
              s->value = resolv_vma - glink->vma;
              s->name = names;
-             memcpy (names, "__glink_PLTresolve", sizeof ("__glink_PLTresolve"));
+             memcpy (names, "__glink_PLTresolve",
+                     sizeof ("__glink_PLTresolve"));
              names += sizeof ("__glink_PLTresolve");
              s++;
              count++;
@@ -3638,9 +2767,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
    calls may use the function descriptor symbol, ie. "bl foo".  This
    behaves exactly as "bl .foo".  */
 
-/* Of those relocs that might be copied as dynamic relocs, this function
-   selects those that must be copied when linking a shared library,
-   even when the symbol is local.  */
+/* Of those relocs that might be copied as dynamic relocs, this
+   function selects those that must be copied when linking a shared
+   library or PIE, even when the symbol is local.  */
 
 static int
 must_be_dyn_reloc (struct bfd_link_info *info,
@@ -3649,11 +2778,21 @@ must_be_dyn_reloc (struct bfd_link_info *info,
   switch (r_type)
     {
     default:
+      /* Only relative relocs can be resolved when the object load
+        address isn't fixed.  DTPREL64 is excluded because the
+        dynamic linker needs to differentiate global dynamic from
+        local dynamic __tls_index pairs when PPC64_OPT_TLS is set.  */
       return 1;
 
     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:
@@ -3669,25 +2808,28 @@ must_be_dyn_reloc (struct bfd_link_info *info,
     case R_PPC64_TPREL16_HIGHEST:
     case R_PPC64_TPREL16_HIGHESTA:
     case R_PPC64_TPREL64:
-      return !info->executable;
+    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);
     }
 }
 
 /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
-   copying dynamic variables from a shared lib into an app's dynbss
+   copying dynamic variables from a shared lib into an app's .dynbss
    section, and instead use a dynamic relocation to point into the
-   shared lib.  With code that gcc generates, it's vital that this be
-   enabled;  In the PowerPC64 ABI, the address of a function is actually
-   the address of a function descriptor, which resides in the .opd
-   section.  gcc uses the descriptor directly rather than going via the
-   GOT as some other ABI's do, which means that initialized function
-   pointers must reference the descriptor.  Thus, a function pointer
-   initialized to the address of a function in a shared library will
-   either require a copy reloc, or a dynamic reloc.  Using a copy reloc
-   redefines the function descriptor symbol to point to the copy.  This
-   presents a problem as a plt entry for that function is also
-   initialized from the function descriptor symbol and the copy reloc
-   may not be initialized first.  */
+   shared lib.  With code that gcc generates it is vital that this be
+   enabled;  In the PowerPC64 ELFv1 ABI the address of a function is
+   actually the address of a function descriptor which resides in the
+   .opd section.  gcc uses the descriptor directly rather than going
+   via the GOT as some other ABIs do, which means that initialized
+   function pointers reference the descriptor.  Thus, a function
+   pointer initialized to the address of a function in a shared
+   library will either require a .dynbss copy and a copy reloc, or a
+   dynamic reloc.  Using a .dynbss copy redefines the function
+   descriptor symbol to point to the copy.  This presents a problem as
+   a PLT entry for that function is also initialized from the function
+   descriptor symbol and the copy may not be initialized first.  */
 #define ELIMINATE_COPY_RELOCS 1
 
 /* Section name for stubs is the associated section name plus this
@@ -3703,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.
-   .   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
 
@@ -3712,7 +2854,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    Used to call a function in a shared library.  If it so happens that
    the plt entry referenced crosses a 64k boundary, then an extra
    "addi %r11,%r11,xxx@toc@l" will be inserted before the "mtctr".
-   .   std     %r2,40(%r1)
+   ppc_stub_plt_call_r2save starts with "std %r2,40(%r1)".
    .   addis   %r11,%r2,xxx@toc@ha
    .   ld      %r12,xxx+0@toc@l(%r11)
    .   mtctr   %r12
@@ -3730,36 +2872,164 @@ must_be_dyn_reloc (struct bfd_link_info *info,
 
    A ppc_stub_plt_branch with an r2 offset looks like:
    .   std     %r2,40(%r1)
-   .   addis   %r11,%r2,xxx@toc@ha
-   .   ld      %r12,xxx@toc@l(%r11)
+   .   addis   %r12,%r2,xxx@toc@ha
+   .   ld      %r12,xxx@toc@l(%r12)
    .   addis   %r2,%r2,off@ha
    .   addi    %r2,%r2,off@l
    .   mtctr   %r12
    .   bctr
 
-   In cases where the "addis" instruction would add zero, the "addis" is
-   omitted and following instructions modified slightly in some cases.
-*/
+   All of the above stubs are shown as their ELFv1 variants.  ELFv2
+   variants exist too, simpler for plt calls since a new toc pointer
+   and static chain are not loaded by the stub.  In addition, ELFv2
+   has some more complex stubs to handle calls marked with NOTOC
+   relocs from functions where r2 is not a valid toc pointer.  These
+   come in two flavours, the ones shown below, and _both variants that
+   start with "std %r2,24(%r1)" to save r2 in the unlikely event that
+   one call is from a function where r2 is used as the toc pointer but
+   needs a toc adjusting stub for small-model multi-toc, and another
+   call is from a function where r2 is not valid.
+   ppc_stub_long_branch_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   addis   %r12,%r11,dest-1b@ha
+   .   addi    %r12,%r12,dest-1b@l
+   .   b       dest
 
-enum ppc_stub_type {
-  ppc_stub_none,
-  ppc_stub_long_branch,
-  ppc_stub_long_branch_r2off,
-  ppc_stub_plt_branch,
-  ppc_stub_plt_branch_r2off,
-  ppc_stub_plt_call,
-  ppc_stub_plt_call_r2save
-};
+   ppc_stub_plt_branch_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,%r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@high
+   .   ori     %r12,%r12,xxx-1b@l
+   .   add     %r12,%r11,%r12
+   .   mtctr   %r12
+   .   bctr
 
-struct ppc_stub_hash_entry {
+   ppc_stub_plt_call_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,%r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@high
+   .   ori     %r12,%r12,xxx-1b@l
+   .   ldx     %r12,%r11,%r12
+   .   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.
+   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
+   particular function symbol.  For example, if printf is called from
+   code with the tocsave optimization (ie. r2 saved in function
+   prologue) and therefore calls use a ppc_stub_plt_call linkage stub,
+   and from other code without the tocsave optimization requiring a
+   ppc_stub_plt_call_r2save linkage stub, a single stub of the latter
+   type will be created.  Calls with the tocsave optimization will
+   enter this stub after the instruction saving r2.  A similar
+   situation exists when calls are marked with R_PPC64_REL24_NOTOC
+   relocations.  These require a ppc_stub_plt_call_notoc linkage stub
+   to call an external function like printf.  If other calls to printf
+   require a ppc_stub_plt_call linkage stub then a single
+   ppc_stub_plt_call_notoc linkage stub will be used for both types of
+   call.  If other calls to printf require a ppc_stub_plt_call_r2save
+   linkage stub then a single ppc_stub_plt_call_both linkage stub will
+   be created and calls not requiring r2 to be saved will enter the
+   stub after the r2 save instruction.  There is an analogous
+   hierarchy of long branch and plt branch stubs for local call
+   linkage.  */
+
+enum ppc_stub_type
+{
+  ppc_stub_none,
+  ppc_stub_long_branch,
+  ppc_stub_long_branch_r2off,
+  ppc_stub_long_branch_notoc,
+  ppc_stub_long_branch_both, /* r2off and notoc variants both needed.  */
+  ppc_stub_plt_branch,
+  ppc_stub_plt_branch_r2off,
+  ppc_stub_plt_branch_notoc,
+  ppc_stub_plt_branch_both,
+  ppc_stub_plt_call,
+  ppc_stub_plt_call_r2save,
+  ppc_stub_plt_call_notoc,
+  ppc_stub_plt_call_both,
+  ppc_stub_global_entry,
+  ppc_stub_save_res
+};
+
+/* Information on stub grouping.  */
+struct map_stub
+{
+  /* The stub section.  */
+  asection *stub_sec;
+  /* This is the section to which stubs in the group will be attached.  */
+  asection *link_sec;
+  /* Next group.  */
+  struct map_stub *next;
+  /* Whether to emit a copy of register save/restore functions in this
+     group.  */
+  int needs_save_res;
+  /* Current offset within stubs after the insn restoring lr in a
+     _notoc or _both stub using bcl for pc-relative addressing, or
+     after the insn restoring lr in a __tls_get_addr_opt plt stub.  */
+  unsigned int lr_restore;
+  /* Accumulated size of EH info emitted to describe return address
+     if stubs modify lr.  Does not include 17 byte FDE header.  */
+  unsigned int eh_size;
+  /* Offset in glink_eh_frame to the start of EH info for this group.  */
+  unsigned int eh_base;
+};
+
+struct ppc_stub_hash_entry
+{
   /* Base hash table entry structure.  */
   struct bfd_hash_entry root;
 
   enum ppc_stub_type stub_type;
 
-  /* The stub section.  */
-  asection *stub_sec;
+  /* Group information.  */
+  struct map_stub *group;
 
   /* Offset within stub_sec of the beginning of this stub.  */
   bfd_vma stub_offset;
@@ -3773,16 +3043,15 @@ struct ppc_stub_hash_entry {
   struct ppc_link_hash_entry *h;
   struct plt_entry *plt_ent;
 
-  /* Where this stub is being called from, or, in the case of combined
-     stub sections, the first input section in the group.  */
-  asection *id_sec;
+  /* Symbol type.  */
+  unsigned char symtype;
 
   /* Symbol st_other.  */
   unsigned char other;
 };
 
-struct ppc_branch_hash_entry {
-
+struct ppc_branch_hash_entry
+{
   /* Base hash table entry structure.  */
   struct bfd_hash_entry root;
 
@@ -3812,7 +3081,8 @@ struct ppc_link_hash_entry
 {
   struct elf_link_hash_entry elf;
 
-  union {
+  union
+  {
     /* A pointer to the most recently used stub hash entry against this
        symbol.  */
     struct ppc_stub_hash_entry *stub_cache;
@@ -3821,9 +3091,6 @@ struct ppc_link_hash_entry
     struct ppc_link_hash_entry *next_dot_sym;
   } u;
 
-  /* Track dynamic relocs copied for this symbol.  */
-  struct elf_dyn_relocs *dyn_relocs;
-
   /* Link between function code and descriptor symbols.  */
   struct ppc_link_hash_entry *oh;
 
@@ -3837,28 +3104,45 @@ struct ppc_link_hash_entry
      should be set for all globals defined in any opd/toc section.  */
   unsigned int adjust_done:1;
 
-  /* Set if we twiddled this symbol to weak at some stage.  */
-  unsigned int was_undefined:1;
+  /* Set if this is an out-of-line register save/restore function,
+     with non-standard calling convention.  */
+  unsigned int save_res:1;
+
+  /* Set if a duplicate symbol with non-zero localentry is detected,
+     even when the duplicate symbol does not provide a definition.  */
+  unsigned int non_zero_localentry:1;
 
   /* Contexts in which symbol is used in the GOT (or TOC).
-     TLS_GD .. TLS_EXPLICIT bits are or'd into the mask as the
-     corresponding relocs are encountered during check_relocs.
-     tls_optimize clears TLS_GD .. TLS_TPREL when optimizing to
-     indicate the corresponding GOT entry type is not needed.
-     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.  */
-#define TLS_GD          1      /* GD reloc. */
-#define TLS_LD          2      /* LD reloc. */
-#define TLS_TPREL       4      /* TPREL reloc, => IE. */
-#define TLS_DTPREL      8      /* DTPREL reloc, => LD. */
-#define TLS_TLS                16      /* Any TLS reloc.  */
-#define TLS_EXPLICIT   32      /* Marks TOC section TLS relocs. */
-#define TLS_TPRELGD    64      /* TPREL reloc resulting from GD->IE. */
-#define PLT_IFUNC      128     /* STT_GNU_IFUNC.  */
+     Bits are or'd into the mask as the corresponding relocs are
+     encountered during check_relocs, with TLS_TLS being set when any
+     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_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. */
+#define TLS_LD          4      /* LD reloc. */
+#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_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
+     case TLS_TLS will be 0.  */
+#define PLT_IFUNC       2      /* STT_GNU_IFUNC.  */
+#define PLT_KEEP        4      /* inline plt call requires plt entry.  */
+#define NON_GOT        256     /* local symbol plt, not stored.  */
 };
 
+static inline struct ppc_link_hash_entry *
+ppc_elf_hash_entry (struct elf_link_hash_entry *ent)
+{
+  return (struct ppc_link_hash_entry *) ent;
+}
+
 /* ppc64 ELF linker hash table.  */
 
 struct ppc_link_hash_table
@@ -3874,46 +3158,46 @@ struct ppc_link_hash_table
   /* Hash table for function prologue tocsave.  */
   htab_t tocsave_htab;
 
-  /* Linker stub bfd.  */
-  bfd *stub_bfd;
-
-  /* Linker call-backs.  */
-  asection * (*add_stub_section) (const char *, asection *);
-  void (*layout_sections_again) (void);
-
-  /* Array to keep track of which stub sections have been created, and
-     information on stub grouping.  */
-  struct map_stub {
-    /* This is the section to which stubs in the group will be attached.  */
-    asection *link_sec;
-    /* The stub section.  */
-    asection *stub_sec;
-    /* Along with elf_gp, specifies the TOC pointer used in this group.  */
+  /* Various options and other info passed from the linker.  */
+  struct ppc64_elf_params *params;
+
+  /* The size of sec_info below.  */
+  unsigned int sec_info_arr_size;
+
+  /* Per-section array of extra section info.  Done this way rather
+     than as part of ppc64_elf_section_data so we have the info for
+     non-ppc64 sections.  */
+  struct
+  {
+    /* Along with elf_gp, specifies the TOC pointer used by this section.  */
     bfd_vma toc_off;
-  } *stub_group;
+
+    union
+    {
+      /* The section group that this section belongs to.  */
+      struct map_stub *group;
+      /* A temp section list pointer.  */
+      asection *list;
+    } u;
+  } *sec_info;
+
+  /* Linked list of groups.  */
+  struct map_stub *group;
 
   /* Temp used when calculating TOC pointers.  */
   bfd_vma toc_curr;
   bfd *toc_bfd;
   asection *toc_first_sec;
 
-  /* Highest input section id.  */
-  int top_id;
-
-  /* Highest output section index.  */
-  int top_index;
-
   /* Used when adding symbols.  */
   struct ppc_link_hash_entry *dot_syms;
 
-  /* List of input sections for each output section.  */
-  asection **input_list;
-
   /* Shortcuts to get to dynamic linker sections.  */
-  asection *dynbss;
-  asection *relbss;
   asection *glink;
+  asection *global_entry;
   asection *sfpr;
+  asection *pltlocal;
+  asection *relpltlocal;
   asection *brlt;
   asection *relbrlt;
   asection *glink_eh_frame;
@@ -3921,45 +3205,48 @@ struct ppc_link_hash_table
   /* Shortcut to .__tls_get_addr and __tls_get_addr.  */
   struct ppc_link_hash_entry *tls_get_addr;
   struct ppc_link_hash_entry *tls_get_addr_fd;
+  struct ppc_link_hash_entry *tga_desc;
+  struct ppc_link_hash_entry *tga_desc_fd;
+  struct map_stub *tga_group;
 
   /* The size of reliplt used by got entry relocs.  */
   bfd_size_type got_reli_size;
 
   /* Statistics.  */
-  unsigned long stub_count[ppc_stub_plt_call_r2save];
+  unsigned long stub_count[ppc_stub_global_entry];
 
   /* Number of stubs against global syms.  */
   unsigned long stub_globals;
 
-  /* Alignment of PLT call stubs.  */
-  unsigned int plt_stub_align:4;
-
   /* Set if we're linking code with function descriptors.  */
   unsigned int opd_abi:1;
 
-  /* Set if PLT call stubs should load r11.  */
-  unsigned int plt_static_chain:1;
-
-  /* Set if PLT call stubs need a read-read barrier.  */
-  unsigned int plt_thread_safe:1;
-
-  /* Set if we should emit symbols for stubs.  */
-  unsigned int emit_stub_syms:1;
-
-  /* Set if __tls_get_addr optimization should not be done.  */
-  unsigned int no_tls_get_addr_opt:1;
-
   /* Support for multiple toc sections.  */
   unsigned int do_multi_toc:1;
   unsigned int multi_toc_needed:1;
   unsigned int second_toc_pass:1;
   unsigned int do_toc_opt:1;
 
+  /* Set if tls optimization is enabled.  */
+  unsigned int do_tls_opt:1;
+
+  /* Set if inline plt calls should be converted to direct calls.  */
+  unsigned int can_convert_all_inline_plt:1;
+
   /* Set on error.  */
   unsigned int stub_error:1;
 
-  /* Temp used by ppc64_elf_process_dot_syms.  */
-  unsigned int twiddled_syms:1;
+  /* Whether func_desc_adjust needs to be run over symbols.  */
+  unsigned int need_func_desc_adj:1;
+
+  /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized.  */
+  unsigned int has_plt_localentry0:1;
+
+  /* Whether calls are made via the PLT from NOTOC functions.  */
+  unsigned int notoc_plt:1;
+
+  /* Whether to use power10 instructions in linkage stubs.  */
+  unsigned int power10_stubs:1;
 
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
@@ -3974,8 +3261,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 a 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
@@ -4027,13 +3315,12 @@ stub_hash_newfunc (struct bfd_hash_entry *entry,
       /* Initialize the local fields.  */
       eh = (struct ppc_stub_hash_entry *) entry;
       eh->stub_type = ppc_stub_none;
-      eh->stub_sec = NULL;
+      eh->group = NULL;
       eh->stub_offset = 0;
       eh->target_value = 0;
       eh->target_section = NULL;
       eh->h = NULL;
       eh->plt_ent = NULL;
-      eh->id_sec = NULL;
       eh->other = 0;
     }
 
@@ -4126,7 +3413,8 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
   return entry;
 }
 
-struct tocsave_entry {
+struct tocsave_entry
+{
   asection *sec;
   bfd_vma offset;
 };
@@ -4135,7 +3423,7 @@ static hashval_t
 tocsave_htab_hash (const void *p)
 {
   const struct tocsave_entry *e = (const struct tocsave_entry *) p;
-  return ((bfd_vma)(intptr_t) e->sec ^ e->offset) >> 3;
+  return ((bfd_vma) (intptr_t) e->sec ^ e->offset) >> 3;
 }
 
 static int
@@ -4146,13 +3434,28 @@ tocsave_htab_eq (const void *p1, const void *p2)
   return e1->sec == e2->sec && e1->offset == e2->offset;
 }
 
+/* Destroy a ppc64 ELF linker hash table.  */
+
+static void
+ppc64_elf_link_hash_table_free (bfd *obfd)
+{
+  struct ppc_link_hash_table *htab;
+
+  htab = (struct ppc_link_hash_table *) obfd->link.hash;
+  if (htab->tocsave_htab)
+    htab_delete (htab->tocsave_htab);
+  bfd_hash_table_free (&htab->branch_hash_table);
+  bfd_hash_table_free (&htab->stub_hash_table);
+  _bfd_elf_link_hash_table_free (obfd);
+}
+
 /* Create a ppc64 ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
 ppc64_elf_link_hash_table_create (bfd *abfd)
 {
   struct ppc_link_hash_table *htab;
-  bfd_size_type amt = sizeof (struct ppc_link_hash_table);
+  size_t amt = sizeof (struct ppc_link_hash_table);
 
   htab = bfd_zmalloc (amt);
   if (htab == NULL)
@@ -4169,19 +3472,30 @@ ppc64_elf_link_hash_table_create (bfd *abfd)
   /* Init the stub hash table too.  */
   if (!bfd_hash_table_init (&htab->stub_hash_table, stub_hash_newfunc,
                            sizeof (struct ppc_stub_hash_entry)))
-    return NULL;
+    {
+      _bfd_elf_link_hash_table_free (abfd);
+      return NULL;
+    }
 
   /* And the branch hash table.  */
   if (!bfd_hash_table_init (&htab->branch_hash_table, branch_hash_newfunc,
                            sizeof (struct ppc_branch_hash_entry)))
-    return NULL;
+    {
+      bfd_hash_table_free (&htab->stub_hash_table);
+      _bfd_elf_link_hash_table_free (abfd);
+      return NULL;
+    }
 
   htab->tocsave_htab = htab_try_create (1024,
                                        tocsave_htab_hash,
                                        tocsave_htab_eq,
                                        NULL);
   if (htab->tocsave_htab == NULL)
-    return NULL;
+    {
+      ppc64_elf_link_hash_table_free (abfd);
+      return NULL;
+    }
+  htab->elf.root.hash_table_free = ppc64_elf_link_hash_table_free;
 
   /* Initializing two fields of the union is just cosmetic.  We really
      only care about glist, but when compiled on a 32-bit host the
@@ -4199,20 +3513,6 @@ ppc64_elf_link_hash_table_create (bfd *abfd)
   return &htab->elf.root;
 }
 
-/* Free the derived linker hash table.  */
-
-static void
-ppc64_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
-{
-  struct ppc_link_hash_table *htab = (struct ppc_link_hash_table *) hash;
-
-  bfd_hash_table_free (&htab->stub_hash_table);
-  bfd_hash_table_free (&htab->branch_hash_table);
-  if (htab->tocsave_htab)
-    htab_delete (htab->tocsave_htab);
-  _bfd_elf_link_hash_table_free (hash);
-}
-
 /* Create sections for linker generated code.  */
 
 static bfd_boolean
@@ -4223,20 +3523,34 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
 
   htab = ppc_hash_table (info);
 
-  /* Create .sfpr for code to save and restore fp regs.  */
   flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
           | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-  htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
-                                                  flags);
-  if (htab->sfpr == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
-    return FALSE;
+  if (htab->params->save_restore_funcs)
+    {
+      /* Create .sfpr for code to save and restore fp regs.  */
+      htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
+                                                      flags);
+      if (htab->sfpr == NULL
+         || !bfd_set_section_alignment (htab->sfpr, 2))
+       return FALSE;
+    }
+
+  if (bfd_link_relocatable (info))
+    return TRUE;
 
   /* Create .glink for lazy dynamic linking support.  */
   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
+     it can be aligned appropriately without affecting htab->glink.  */
+  htab->global_entry = bfd_make_section_anyway_with_flags (dynobj, ".glink",
+                                                          flags);
+  if (htab->global_entry == NULL
+      || !bfd_set_section_alignment (htab->global_entry, 2))
     return FALSE;
 
   if (!info->no_ld_generated_unwind_info)
@@ -4247,14 +3561,14 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
                                                                 ".eh_frame",
                                                                 flags);
       if (htab->glink_eh_frame == NULL
-         || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2))
+         || !bfd_set_section_alignment (htab->glink_eh_frame, 2))
        return FALSE;
     }
 
   flags = SEC_ALLOC | SEC_LINKER_CREATED;
   htab->elf.iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags);
   if (htab->elf.iplt == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->elf.iplt, 3))
+      || !bfd_set_section_alignment (htab->elf.iplt, 3))
     return FALSE;
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
@@ -4262,7 +3576,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->elf.irelplt
     = bfd_make_section_anyway_with_flags (dynobj, ".rela.iplt", flags);
   if (htab->elf.irelplt == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->elf.irelplt, 3))
+      || !bfd_set_section_alignment (htab->elf.irelplt, 3))
     return FALSE;
 
   /* Create branch lookup table for plt_branch stubs.  */
@@ -4271,19 +3585,32 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
                                                   flags);
   if (htab->brlt == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
+      || !bfd_set_section_alignment (htab->brlt, 3))
     return FALSE;
 
-  if (!info->shared)
+  /* Local plt entries, put in .branch_lt but a separate section for
+     convenience.  */
+  htab->pltlocal = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
+                                                      flags);
+  if (htab->pltlocal == NULL
+      || !bfd_set_section_alignment (htab->pltlocal, 3))
+    return FALSE;
+
+  if (!bfd_link_pic (info))
     return TRUE;
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
           | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-  htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
-                                                     ".rela.branch_lt",
-                                                     flags);
+  htab->relbrlt
+    = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags);
   if (htab->relbrlt == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
+      || !bfd_set_section_alignment (htab->relbrlt, 3))
+    return FALSE;
+
+  htab->relpltlocal
+    = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags);
+  if (htab->relpltlocal == NULL
+      || !bfd_set_section_alignment (htab->relpltlocal, 3))
     return FALSE;
 
   return TRUE;
@@ -4292,23 +3619,19 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
 /* Satisfy the ELF linker by filling in some fields in our fake bfd.  */
 
 bfd_boolean
-ppc64_elf_init_stub_bfd (bfd *abfd, struct bfd_link_info *info)
+ppc64_elf_init_stub_bfd (struct bfd_link_info *info,
+                        struct ppc64_elf_params *params)
 {
   struct ppc_link_hash_table *htab;
 
-  elf_elfheader (abfd)->e_ident[EI_CLASS] = ELFCLASS64;
+  elf_elfheader (params->stub_bfd)->e_ident[EI_CLASS] = ELFCLASS64;
 
 /* Always hook our dynamic sections into the first bfd, which is the
    linker created stub bfd.  This ensures that the GOT header is at
    the start of the output TOC section.  */
   htab = ppc_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-  htab->stub_bfd = abfd;
-  htab->elf.dynobj = abfd;
-
-  if (info->relocatable)
-    return TRUE;
+  htab->elf.dynobj = params->stub_bfd;
+  htab->params = params;
 
   return create_linkage_sections (htab->elf.dynobj, info);
 }
@@ -4370,18 +3693,20 @@ ppc_get_stub_entry (const asection *input_section,
                    struct ppc_link_hash_table *htab)
 {
   struct ppc_stub_hash_entry *stub_entry;
-  const asection *id_sec;
+  struct map_stub *group;
 
   /* If this input section is part of a group of sections sharing one
      stub section, then use the id of the first section in the group.
      Stub names need to include a section id, as there may well be
      more than one stub used to reach say, printf, and we need to
      distinguish between them.  */
-  id_sec = htab->stub_group[input_section->id].link_sec;
+  group = htab->sec_info[input_section->id].u.group;
+  if (group == NULL)
+    return NULL;
 
   if (h != NULL && h->u.stub_cache != NULL
       && h->u.stub_cache->h == h
-      && h->u.stub_cache->id_sec == id_sec)
+      && h->u.stub_cache->group == group)
     {
       stub_entry = h->u.stub_cache;
     }
@@ -4389,7 +3714,7 @@ ppc_get_stub_entry (const asection *input_section,
     {
       char *stub_name;
 
-      stub_name = ppc_stub_name (id_sec, sym_sec, h, rel);
+      stub_name = ppc_stub_name (group->link_sec, sym_sec, h, rel);
       if (stub_name == NULL)
        return NULL;
 
@@ -4413,35 +3738,32 @@ ppc_add_stub (const char *stub_name,
              struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  struct map_stub *group;
   asection *link_sec;
   asection *stub_sec;
   struct ppc_stub_hash_entry *stub_entry;
 
-  link_sec = htab->stub_group[section->id].link_sec;
-  stub_sec = htab->stub_group[section->id].stub_sec;
+  group = htab->sec_info[section->id].u.group;
+  link_sec = group->link_sec;
+  stub_sec = group->stub_sec;
   if (stub_sec == NULL)
     {
-      stub_sec = htab->stub_group[link_sec->id].stub_sec;
-      if (stub_sec == NULL)
-       {
-         size_t namelen;
-         bfd_size_type len;
-         char *s_name;
-
-         namelen = strlen (link_sec->name);
-         len = namelen + sizeof (STUB_SUFFIX);
-         s_name = bfd_alloc (htab->stub_bfd, len);
-         if (s_name == NULL)
-           return NULL;
+      size_t namelen;
+      bfd_size_type len;
+      char *s_name;
+
+      namelen = strlen (link_sec->name);
+      len = namelen + sizeof (STUB_SUFFIX);
+      s_name = bfd_alloc (htab->params->stub_bfd, len);
+      if (s_name == NULL)
+       return NULL;
 
-         memcpy (s_name, link_sec->name, namelen);
-         memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
-         stub_sec = (*htab->add_stub_section) (s_name, link_sec);
-         if (stub_sec == NULL)
-           return NULL;
-         htab->stub_group[link_sec->id].stub_sec = stub_sec;
-       }
-      htab->stub_group[section->id].stub_sec = stub_sec;
+      memcpy (s_name, link_sec->name, namelen);
+      memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
+      stub_sec = (*htab->params->add_stub_section) (s_name, link_sec);
+      if (stub_sec == NULL)
+       return NULL;
+      group->stub_sec = stub_sec;
     }
 
   /* Enter this entry into the linker stub hash table.  */
@@ -4449,14 +3771,14 @@ ppc_add_stub (const char *stub_name,
                                     TRUE, FALSE);
   if (stub_entry == NULL)
     {
-      info->callbacks->einfo (_("%P: %B: cannot create stub entry %s\n"),
-                             section->owner, stub_name);
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: cannot create stub entry %s"),
+                         section->owner, stub_name);
       return NULL;
     }
 
-  stub_entry->stub_sec = stub_sec;
+  stub_entry->group = group;
   stub_entry->stub_offset = 0;
-  stub_entry->id_sec = link_sec;
   return stub_entry;
 }
 
@@ -4484,13 +3806,13 @@ create_got_section (bfd *abfd, struct bfd_link_info *info)
 
   got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
   if (!got
-      || !bfd_set_section_alignment (abfd, got, 3))
+      || !bfd_set_section_alignment (got, 3))
     return FALSE;
 
   relgot = bfd_make_section_anyway_with_flags (abfd, ".rela.got",
                                               flags | SEC_READONLY);
   if (!relgot
-      || ! bfd_set_section_alignment (abfd, relgot, 3))
+      || !bfd_set_section_alignment (relgot, 3))
     return FALSE;
 
   ppc64_elf_tdata (abfd)->got = got;
@@ -4498,31 +3820,6 @@ create_got_section (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
-/* Create the dynamic sections, and set up shortcuts.  */
-
-static bfd_boolean
-ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
-{
-  struct ppc_link_hash_table *htab;
-
-  if (!_bfd_elf_create_dynamic_sections (dynobj, info))
-    return FALSE;
-
-  htab = ppc_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  htab->dynbss = bfd_get_linker_section (dynobj, ".dynbss");
-  if (!info->shared)
-    htab->relbss = bfd_get_linker_section (dynobj, ".rela.bss");
-
-  if (!htab->elf.sgot || !htab->elf.splt || !htab->elf.srelplt || !htab->dynbss
-      || (!info->shared && !htab->relbss))
-    abort ();
-
-  return TRUE;
-}
-
 /* Follow indirect and warning symbol links.  */
 
 static inline struct bfd_link_hash_entry *
@@ -4543,7 +3840,7 @@ elf_follow_link (struct elf_link_hash_entry *h)
 static inline struct ppc_link_hash_entry *
 ppc_follow_link (struct ppc_link_hash_entry *h)
 {
-  return (struct ppc_link_hash_entry *) follow_link (&h->elf.root);
+  return ppc_elf_hash_entry (elf_follow_link (&h->elf));
 }
 
 /* Merge PLT info on FROM with that on TO.  */
@@ -4590,8 +3887,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
 {
   struct ppc_link_hash_entry *edir, *eind;
 
-  edir = (struct ppc_link_hash_entry *) dir;
-  eind = (struct ppc_link_hash_entry *) ind;
+  edir = ppc_elf_hash_entry (dir);
+  eind = ppc_elf_hash_entry (ind);
 
   edir->is_func |= eind->is_func;
   edir->is_func_descriptor |= eind->is_func_descriptor;
@@ -4599,35 +3896,38 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
   if (eind->oh != NULL)
     edir->oh = ppc_follow_link (eind->oh);
 
-  /* If called to transfer flags for a weakdef during processing
-     of elf_adjust_dynamic_symbol, don't copy NON_GOT_REF.
-     We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
-  if (!(ELIMINATE_COPY_RELOCS
-       && eind->elf.root.type != bfd_link_hash_indirect
-       && edir->elf.dynamic_adjusted))
-    edir->elf.non_got_ref |= eind->elf.non_got_ref;
-
-  edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
+  if (edir->elf.versioned != versioned_hidden)
+    edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
   edir->elf.ref_regular |= eind->elf.ref_regular;
   edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
+  edir->elf.non_got_ref |= eind->elf.non_got_ref;
   edir->elf.needs_plt |= eind->elf.needs_plt;
   edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
 
+  /* If we were called to copy over info for a weak sym, don't copy
+     dyn_relocs, plt/got info, or dynindx.  We used to copy dyn_relocs
+     in order to simplify readonly_dynrelocs and save a field in the
+     symbol hash entry, but that means dyn_relocs can't be used in any
+     tests about a specific symbol, or affect other symbol flags which
+     are then tested.  */
+  if (eind->elf.root.type != bfd_link_hash_indirect)
+    return;
+
   /* Copy over any dynamic relocs we may have on the indirect sym.  */
-  if (eind->dyn_relocs != NULL)
+  if (ind->dyn_relocs != NULL)
     {
-      if (edir->dyn_relocs != NULL)
+      if (dir->dyn_relocs != NULL)
        {
          struct elf_dyn_relocs **pp;
          struct elf_dyn_relocs *p;
 
          /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
-         for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+         for (pp = &ind->dyn_relocs; (p = *pp) != NULL; )
            {
              struct elf_dyn_relocs *q;
 
-             for (q = edir->dyn_relocs; q != NULL; q = q->next)
+             for (q = dir->dyn_relocs; q != NULL; q = q->next)
                if (q->sec == p->sec)
                  {
                    q->pc_count += p->pc_count;
@@ -4638,23 +3938,13 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
              if (q == NULL)
                pp = &p->next;
            }
-         *pp = edir->dyn_relocs;
+         *pp = dir->dyn_relocs;
        }
 
-      edir->dyn_relocs = eind->dyn_relocs;
-      eind->dyn_relocs = NULL;
+      dir->dyn_relocs = ind->dyn_relocs;
+      ind->dyn_relocs = NULL;
     }
 
-  /* If we were called to copy over info for a weak sym, that's all.
-     You might think dyn_relocs need not be copied over;  After all,
-     both syms will be dynamic or both non-dynamic so we're just
-     moving reloc accounting around.  However, ELIMINATE_COPY_RELOCS
-     code in ppc64_elf_adjust_dynamic_symbol needs to check for
-     dyn_relocs in read-only sections, and it does so on what is the
-     DIR sym here.  */
-  if (eind->elf.root.type != bfd_link_hash_indirect)
-    return;
-
   /* Copy over got entries that we may have already seen to the
      symbol which just became indirect.  */
   if (eind->elf.got.glist != NULL)
@@ -4714,8 +4004,8 @@ lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
     {
       const char *fd_name = fh->elf.root.root.string + 1;
 
-      fdh = (struct ppc_link_hash_entry *)
-       elf_link_hash_lookup (&htab->elf, fd_name, FALSE, FALSE, FALSE);
+      fdh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, fd_name,
+                                                     FALSE, FALSE, FALSE));
       if (fdh == NULL)
        return fdh;
 
@@ -4725,32 +4015,29 @@ lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab)
       fh->oh = fdh;
     }
 
-  return ppc_follow_link (fdh);
+  fdh = ppc_follow_link (fdh);
+  fdh->is_func_descriptor = 1;
+  fdh->oh = fh;
+  return fdh;
 }
 
-/* Make a fake function descriptor sym for the code sym FH.  */
+/* Make a fake function descriptor sym for the undefined code sym FH.  */
 
 static struct ppc_link_hash_entry *
 make_fdh (struct bfd_link_info *info,
          struct ppc_link_hash_entry *fh)
 {
-  bfd *abfd;
-  asymbol *newsym;
-  struct bfd_link_hash_entry *bh;
+  bfd *abfd = fh->elf.root.u.undef.abfd;
+  struct bfd_link_hash_entry *bh = NULL;
   struct ppc_link_hash_entry *fdh;
-
-  abfd = fh->elf.root.u.undef.abfd;
-  newsym = bfd_make_empty_symbol (abfd);
-  newsym->name = fh->elf.root.root.string + 1;
-  newsym->section = bfd_und_section_ptr;
-  newsym->value = 0;
-  newsym->flags = BSF_WEAK;
-
-  bh = NULL;
-  if (!_bfd_generic_link_add_one_symbol (info, abfd, newsym->name,
-                                        newsym->flags, newsym->section,
-                                        newsym->value, NULL, FALSE, FALSE,
-                                        &bh))
+  flagword flags = (fh->elf.root.type == bfd_link_hash_undefweak
+                   ? BSF_WEAK
+                   : BSF_GLOBAL);
+
+  if (!_bfd_generic_link_add_one_symbol (info, abfd,
+                                        fh->elf.root.root.string + 1,
+                                        flags, bfd_und_section_ptr, 0,
+                                        NULL, FALSE, FALSE, &bh))
     return NULL;
 
   fdh = (struct ppc_link_hash_entry *) bh;
@@ -4773,22 +4060,37 @@ ppc64_elf_add_symbol_hook (bfd *ibfd,
                           const char **name,
                           flagword *flags ATTRIBUTE_UNUSED,
                           asection **sec,
-                          bfd_vma *value ATTRIBUTE_UNUSED)
+                          bfd_vma *value)
 {
-  if ((ibfd->flags & DYNAMIC) == 0
-      && ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
-    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
-
-  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+  if (*sec != NULL
+      && strcmp ((*sec)->name, ".opd") == 0)
     {
-      if ((ibfd->flags & DYNAMIC) == 0)
-       elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+      asection *code_sec;
+
+      if (!(ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+           || ELF_ST_TYPE (isym->st_info) == STT_FUNC))
+       isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+
+      /* If the symbol is a function defined in .opd, and the function
+        code is in a discarded group, let it appear to be undefined.  */
+      if (!bfd_link_relocatable (info)
+         && (*sec)->reloc_count != 0
+         && opd_entry_value (*sec, *value, &code_sec, NULL,
+                             FALSE) != (bfd_vma) -1
+         && discarded_section (code_sec))
+       {
+         *sec = bfd_und_section_ptr;
+         isym->st_shndx = SHN_UNDEF;
+       }
     }
-  else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
-    ;
   else if (*sec != NULL
-          && strcmp ((*sec)->name, ".opd") == 0)
-    isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+          && strcmp ((*sec)->name, ".toc") == 0
+          && ELF_ST_TYPE (isym->st_info) == STT_OBJECT)
+    {
+      struct ppc_link_hash_table *htab = ppc_hash_table (info);
+      if (htab != NULL)
+       htab->params->object_in_toc = 1;
+    }
 
   if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
     {
@@ -4796,8 +4098,8 @@ ppc64_elf_add_symbol_hook (bfd *ibfd,
        set_abiversion (ibfd, 2);
       else if (abiversion (ibfd) == 1)
        {
-         info->callbacks->einfo (_("%P: symbol '%s' has invalid st_other"
-                                   " for ABI version 1\n"), name);
+         _bfd_error_handler (_("symbol '%s' has invalid st_other"
+                               " for ABI version 1"), *name);
          bfd_set_error (bfd_error_bad_value);
          return FALSE;
        }
@@ -4814,11 +4116,29 @@ ppc64_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
                                  bfd_boolean definition,
                                  bfd_boolean dynamic)
 {
-  if (definition && !dynamic)
+  if (definition && (!dynamic || !h->def_regular))
     h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
                | ELF_ST_VISIBILITY (h->other));
 }
 
+/* Hook called on merging a symbol.  We use this to clear "fake" since
+   we now have a real symbol.  */
+
+static bfd_boolean
+ppc64_elf_merge_symbol (struct elf_link_hash_entry *h,
+                       const Elf_Internal_Sym *isym,
+                       asection **psec ATTRIBUTE_UNUSED,
+                       bfd_boolean newdef ATTRIBUTE_UNUSED,
+                       bfd_boolean olddef ATTRIBUTE_UNUSED,
+                       bfd *oldbfd ATTRIBUTE_UNUSED,
+                       const asection *oldsec ATTRIBUTE_UNUSED)
+{
+  ppc_elf_hash_entry (h)->fake = 0;
+  if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
+    ppc_elf_hash_entry (h)->non_zero_localentry = 1;
+  return TRUE;
+}
+
 /* This function makes an old ABI object reference to ".bar" cause the
    inclusion of a new ABI object archive that defines "bar".
    NAME is a symbol defined in an archive.  Return a symbol in the hash
@@ -4837,8 +4157,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.  */
-      && !(h->root.type == bfd_link_hash_undefweak
-          && ((struct ppc_link_hash_entry *) h)->fake))
+      && !ppc_elf_hash_entry (h)->fake)
     return h;
 
   if (name[0] == '.')
@@ -4847,11 +4166,16 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
   len = strlen (name);
   dot_name = bfd_alloc (abfd, len + 2);
   if (dot_name == NULL)
-    return (struct elf_link_hash_entry *) 0 - 1;
+    return (struct elf_link_hash_entry *) -1;
   dot_name[0] = '.';
   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;
 }
 
@@ -4870,12 +4194,12 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info)
   struct ppc_link_hash_table *htab;
   struct ppc_link_hash_entry *fdh;
 
-  if (eh->elf.root.type == bfd_link_hash_indirect)
-    return TRUE;
-
   if (eh->elf.root.type == bfd_link_hash_warning)
     eh = (struct ppc_link_hash_entry *) eh->elf.root.u.i.link;
 
+  if (eh->elf.root.type == bfd_link_hash_indirect)
+    return TRUE;
+
   if (eh->elf.root.root.string[0] != '.')
     abort ();
 
@@ -4884,90 +4208,177 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info)
     return FALSE;
 
   fdh = lookup_fdh (eh, htab);
-  if (fdh == NULL)
+  if (fdh == NULL
+      && !bfd_link_relocatable (info)
+      && (eh->elf.root.type == bfd_link_hash_undefined
+         || eh->elf.root.type == bfd_link_hash_undefweak)
+      && eh->elf.ref_regular)
     {
-      if (!info->relocatable
-         && (eh->elf.root.type == bfd_link_hash_undefined
-             || eh->elf.root.type == bfd_link_hash_undefweak)
-         && eh->elf.ref_regular)
-       {
-         /* Make an undefweak function descriptor sym, which is enough to
-            pull in an --as-needed shared lib, but won't cause link
-            errors.  Archives are handled elsewhere.  */
-         fdh = make_fdh (info, eh);
-         if (fdh == NULL)
-           return FALSE;
-         fdh->elf.ref_regular = 1;
-       }
+      /* Make an undefined function descriptor sym, in order to
+        pull in an --as-needed shared lib.  Archives are handled
+        elsewhere.  */
+      fdh = make_fdh (info, eh);
+      if (fdh == NULL)
+       return FALSE;
     }
-  else
+
+  if (fdh != NULL)
     {
       unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1;
       unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1;
+
+      /* Make both descriptor and entry symbol have the most
+        constraining visibility of either symbol.  */
       if (entry_vis < descr_vis)
        fdh->elf.other += entry_vis - descr_vis;
       else if (entry_vis > descr_vis)
        eh->elf.other += descr_vis - entry_vis;
 
-      if ((fdh->elf.root.type == bfd_link_hash_defined
-          || fdh->elf.root.type == bfd_link_hash_defweak)
-         && eh->elf.root.type == bfd_link_hash_undefined)
+      /* Propagate reference flags from entry symbol to function
+        descriptor symbol.  */
+      fdh->elf.root.non_ir_ref_regular |= eh->elf.root.non_ir_ref_regular;
+      fdh->elf.root.non_ir_ref_dynamic |= eh->elf.root.non_ir_ref_dynamic;
+      fdh->elf.ref_regular |= eh->elf.ref_regular;
+      fdh->elf.ref_regular_nonweak |= eh->elf.ref_regular_nonweak;
+
+      if (!fdh->elf.forced_local
+         && fdh->elf.dynindx == -1
+         && fdh->elf.versioned != versioned_hidden
+         && (bfd_link_dll (info)
+             || fdh->elf.def_dynamic
+             || fdh->elf.ref_dynamic)
+         && (eh->elf.ref_regular
+             || eh->elf.def_regular))
        {
-         eh->elf.root.type = bfd_link_hash_undefweak;
-         eh->was_undefined = 1;
-         htab->twiddled_syms = 1;
+         if (!bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
+           return FALSE;
        }
     }
 
   return TRUE;
 }
 
-/* Process list of dot-symbols we made in link_hash_newfunc.  */
+/* Set up opd section info and abiversion for IBFD, and process list
+   of dot-symbols we made in link_hash_newfunc.  */
 
 static bfd_boolean
-ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info)
+ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
   struct ppc_link_hash_entry **p, *eh;
+  asection *opd = bfd_get_section_by_name (ibfd, ".opd");
+
+  if (opd != NULL && opd->size != 0)
+    {
+      BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
+      ppc64_elf_section_data (opd)->sec_type = sec_opd;
+
+      if (abiversion (ibfd) == 0)
+       set_abiversion (ibfd, 1);
+      else if (abiversion (ibfd) >= 2)
+       {
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%pB .opd not allowed in ABI version %d"),
+                             ibfd, abiversion (ibfd));
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+    }
+
+  if (is_ppc64_elf (info->output_bfd))
+    {
+      /* For input files without an explicit abiversion in e_flags
+        we should have flagged any with symbol st_other bits set
+        as ELFv1 and above flagged those with .opd as ELFv2.
+        Set the output abiversion if not yet set, and for any input
+        still ambiguous, take its abiversion from the output.
+        Differences in ABI are reported later.  */
+      if (abiversion (info->output_bfd) == 0)
+       set_abiversion (info->output_bfd, abiversion (ibfd));
+      else if (abiversion (ibfd) == 0)
+       set_abiversion (ibfd, abiversion (info->output_bfd));
+    }
 
-  if (!is_ppc64_elf (info->output_bfd))
-    return TRUE;
   htab = ppc_hash_table (info);
   if (htab == NULL)
-    return FALSE;
+    return TRUE;
 
-  if (is_ppc64_elf (ibfd))
+  if (opd != NULL && opd->size != 0
+      && (ibfd->flags & DYNAMIC) == 0
+      && (opd->flags & SEC_RELOC) != 0
+      && opd->reloc_count != 0
+      && !bfd_is_abs_section (opd->output_section)
+      && info->gc_sections)
     {
-      p = &htab->dot_syms;
-      while ((eh = *p) != NULL)
+      /* Garbage collection needs some extra help with .opd sections.
+        We don't want to necessarily keep everything referenced by
+        relocs in .opd, as that would keep all functions.  Instead,
+        if we reference an .opd symbol (a function descriptor), we
+        want to keep the function code symbol's section.  This is
+        easy for global symbols, but for local syms we need to keep
+        information about the associated function section.  */
+      bfd_size_type amt;
+      asection **opd_sym_map;
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Rela *relocs, *rel_end, *rel;
+
+      amt = OPD_NDX (opd->size) * sizeof (*opd_sym_map);
+      opd_sym_map = bfd_zalloc (ibfd, amt);
+      if (opd_sym_map == NULL)
+       return FALSE;
+      ppc64_elf_section_data (opd)->u.opd.func_sec = opd_sym_map;
+      relocs = _bfd_elf_link_read_relocs (ibfd, opd, NULL, NULL,
+                                         info->keep_memory);
+      if (relocs == NULL)
+       return FALSE;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      rel_end = relocs + opd->reloc_count - 1;
+      for (rel = relocs; rel < rel_end; rel++)
        {
-         *p = NULL;
-         if (&eh->elf == htab->elf.hgot)
-           ;
-         else if (htab->elf.hgot == NULL
-                  && strcmp (eh->elf.root.root.string, ".TOC.") == 0)
-           htab->elf.hgot = &eh->elf;
-         else if (!add_symbol_adjust (eh, info))
-           return FALSE;
-         p = &eh->u.next_dot_sym;
+         enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
+         unsigned long r_symndx = ELF64_R_SYM (rel->r_info);
+
+         if (r_type == R_PPC64_ADDR64
+             && ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC
+             && r_symndx < symtab_hdr->sh_info)
+           {
+             Elf_Internal_Sym *isym;
+             asection *s;
+
+             isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, r_symndx);
+             if (isym == NULL)
+               {
+                 if (elf_section_data (opd)->relocs != relocs)
+                   free (relocs);
+                 return FALSE;
+               }
+
+             s = bfd_section_from_elf_index (ibfd, isym->st_shndx);
+             if (s != NULL && s != opd)
+               opd_sym_map[OPD_NDX (rel->r_offset)] = s;
+           }
        }
+      if (elf_section_data (opd)->relocs != relocs)
+       free (relocs);
     }
 
-  /* Clear the list for non-ppc64 input files.  */
   p = &htab->dot_syms;
   while ((eh = *p) != NULL)
     {
       *p = NULL;
+      if (&eh->elf == htab->elf.hgot)
+       ;
+      else if (htab->elf.hgot == NULL
+              && strcmp (eh->elf.root.root.string, ".TOC.") == 0)
+       htab->elf.hgot = &eh->elf;
+      else if (abiversion (ibfd) <= 1)
+       {
+         htab->need_func_desc_adj = 1;
+         if (!add_symbol_adjust (eh, info))
+           return FALSE;
+       }
       p = &eh->u.next_dot_sym;
     }
-
-  /* We need to fix the undefs list for any syms we have twiddled to
-     undef_weak.  */
-  if (htab->twiddled_syms)
-    {
-      bfd_link_repair_undef_list (&htab->elf.root);
-      htab->twiddled_syms = 0;
-    }
   return TRUE;
 }
 
@@ -5001,10 +4412,8 @@ ppc64_elf_link_just_syms (asection *sec, struct bfd_link_info *info)
       && (sec->owner->flags & (EXEC_P | DYNAMIC)) != 0
       && is_ppc64_elf (sec->owner))
     {
-      asection *got = bfd_get_section_by_name (sec->owner, ".got");
-      if (got != NULL
-         && got->size >= elf_backend_got_header_size
-         && bfd_get_section_by_name (sec->owner, ".opd") != NULL)
+      if (abiversion (sec->owner) >= 2
+         || bfd_get_section_by_name (sec->owner, ".opd") != NULL)
        sec->has_toc_reloc = 1;
     }
   _bfd_elf_link_just_syms (sec, info);
@@ -5031,7 +4440,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
       elf_local_got_ents (abfd) = local_got_ents;
     }
 
-  if ((tls_type & (PLT_IFUNC | TLS_EXPLICIT)) == 0)
+  if ((tls_type & (NON_GOT | TLS_EXPLICIT)) == 0)
     {
       struct got_entry *ent;
 
@@ -5042,7 +4451,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
          break;
       if (ent == NULL)
        {
-         bfd_size_type amt = sizeof (*ent);
+         size_t amt = sizeof (*ent);
          ent = bfd_alloc (abfd, amt);
          if (ent == NULL)
            return FALSE;
@@ -5059,7 +4468,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 
   local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info);
   local_got_tls_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info);
-  local_got_tls_masks[r_symndx] |= tls_type;
+  local_got_tls_masks[r_symndx] |= tls_type & 0xff;
 
   return local_plt + r_symndx;
 }
@@ -5074,7 +4483,7 @@ update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend)
       break;
   if (ent == NULL)
     {
-      bfd_size_type amt = sizeof (*ent);
+      size_t amt = sizeof (*ent);
       ent = bfd_alloc (abfd, amt);
       if (ent == NULL)
        return FALSE;
@@ -5091,13 +4500,31 @@ static bfd_boolean
 is_branch_reloc (enum elf_ppc64_reloc_type r_type)
 {
   return (r_type == R_PPC64_REL24
+         || r_type == R_PPC64_REL24_NOTOC
          || r_type == R_PPC64_REL14
          || r_type == R_PPC64_REL14_BRTAKEN
          || r_type == R_PPC64_REL14_BRNTAKEN
          || r_type == R_PPC64_ADDR24
          || r_type == R_PPC64_ADDR14
          || r_type == R_PPC64_ADDR14_BRTAKEN
-         || r_type == R_PPC64_ADDR14_BRNTAKEN);
+         || r_type == R_PPC64_ADDR14_BRNTAKEN
+         || r_type == R_PPC64_PLTCALL
+         || r_type == R_PPC64_PLTCALL_NOTOC);
+}
+
+/* Relocs on inline plt call sequence insns prior to the call.  */
+
+static bfd_boolean
+is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type)
+{
+  return (r_type == R_PPC64_PLT16_HA
+         || r_type == R_PPC64_PLT16_HI
+         || r_type == R_PPC64_PLT16_LO
+         || r_type == R_PPC64_PLT16_LO_DS
+         || 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
@@ -5114,19 +4541,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
-  asection **opd_sym_map;
   struct elf_link_hash_entry *tga, *dottga;
+  bfd_boolean is_opd;
 
-  if (info->relocatable)
-    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)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   BFD_ASSERT (is_ppc64_elf (abfd));
@@ -5142,36 +4560,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
   sreloc = NULL;
-  opd_sym_map = NULL;
-  if (strcmp (sec->name, ".opd") == 0)
-    {
-      /* Garbage collection needs some extra help with .opd sections.
-        We don't want to necessarily keep everything referenced by
-        relocs in .opd, as that would keep all functions.  Instead,
-        if we reference an .opd symbol (a function descriptor), we
-        want to keep the function code symbol's section.  This is
-        easy for global symbols, but for local syms we need to keep
-        information about the associated function section.  */
-      bfd_size_type amt;
-
-      if (abiversion (abfd) == 0)
-       set_abiversion (abfd, 1);
-      else if (abiversion (abfd) == 2)
-       {
-         info->callbacks->einfo (_("%P: .opd not allowed in ABI version %d\n"),
-                                 abiversion (abfd));
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
-      amt = sec->size * sizeof (*opd_sym_map) / 8;
-      opd_sym_map = bfd_zalloc (abfd, amt);
-      if (opd_sym_map == NULL)
-       return FALSE;
-      ppc64_elf_section_data (sec)->u.opd.func_sec = opd_sym_map;
-      BFD_ASSERT (ppc64_elf_section_data (sec)->sec_type == sec_normal);
-      ppc64_elf_section_data (sec)->sec_type = sec_opd;
-    }
-
+  is_opd = ppc64_elf_section_data (sec)->sec_type == sec_opd;
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -5180,7 +4569,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       enum elf_ppc64_reloc_type r_type;
       int tls_type;
       struct _ppc64_elf_section_data *ppc64_sec;
-      struct plt_entry **ifunc;
+      struct plt_entry **ifunc, **plt_list;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -5190,15 +4579,62 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          h = sym_hashes[r_symndx - symtab_hdr->sh_info];
          h = elf_follow_link (h);
 
-         /* PR15323, ref flags aren't set for references in the same
-            object.  */
-         h->root.non_ir_ref = 1;
-
          if (h == htab->elf.hgot)
            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->power10_stubs = 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)
        {
@@ -5218,45 +4654,35 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                            rel->r_addend, PLT_IFUNC);
+                                            rel->r_addend,
+                                            NON_GOT | PLT_IFUNC);
              if (ifunc == NULL)
                return FALSE;
            }
        }
-      r_type = ELF64_R_TYPE (rel->r_info);
-      if (is_branch_reloc (r_type))
-       {
-         if (h != NULL && (h == tga || h == dottga))
-           {
-             if (rel != relocs
-                 && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
-                     || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
-               /* We have a new-style __tls_get_addr call with a marker
-                  reloc.  */
-               ;
-             else
-               /* Mark this section as having an old-style call.  */
-               sec->has_tls_get_addr_call = 1;
-           }
-
-         /* STT_GNU_IFUNC symbols must have a PLT entry.  */
-         if (ifunc != NULL
-             && !update_plt_info (abfd, ifunc, rel->r_addend))
-           return FALSE;
-       }
 
+      tls_type = 0;
       switch (r_type)
        {
        case R_PPC64_TLSGD:
        case R_PPC64_TLSLD:
          /* These special tls relocs tie a call to __tls_get_addr with
             its parameter symbol.  */
+         if (h != NULL)
+           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,
+                                       NON_GOT | TLS_TLS | TLS_MARK))
+             return FALSE;
+         sec->has_tls_reloc = 1;
          break;
 
        case R_PPC64_GOT_TLSLD16:
        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;
 
@@ -5264,6 +4690,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT_TLSGD16_LO:
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TLSGD_PCREL34:
          tls_type = TLS_TLS | TLS_GD;
          goto dogottls;
 
@@ -5271,7 +4698,8 @@ 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:
-         if (!info->executable)
+       case R_PPC64_GOT_TPREL_PCREL34:
+         if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogottls;
@@ -5280,17 +4708,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_DTPREL_PCREL34:
          tls_type = TLS_TLS | TLS_DTPREL;
        dogottls:
          sec->has_tls_reloc = 1;
-         /* Fall thru */
+         goto dogot;
 
        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_HI:
+       case R_PPC64_GOT16_HA:
+       case R_PPC64_GOT16_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
@@ -5313,7 +4744,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              struct ppc_link_hash_entry *eh;
              struct got_entry *ent;
 
-             eh = (struct ppc_link_hash_entry *) h;
+             eh = ppc_elf_hash_entry (h);
              for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
                if (ent->addend == rel->r_addend
                    && ent->owner == abfd
@@ -5321,7 +4752,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  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;
@@ -5341,42 +4772,33 @@ 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;
-
-         /* We may also need a plt entry if the symbol turns out to be
-            an ifunc.  */
-         if (h != NULL && !info->shared && abiversion (abfd) == 2)
-           {
-             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:
+       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.  We
-            actually build the entry in adjust_dynamic_symbol,
-            because this might be a case of linking PIC code without
-            linking in any dynamic objects, in which case we don't
-            need to generate a procedure linkage table after all.  */
-         if (h == NULL)
-           {
-             /* It does not make sense to have a procedure linkage
-                table entry for a local symbol.  */
-             bfd_set_error (bfd_error_bad_value);
-             return FALSE;
-           }
-         else
+         /* This symbol requires a procedure linkage table entry.  */
+         plt_list = ifunc;
+         if (h != NULL)
            {
-             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
-               return FALSE;
              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;
+             ppc_elf_hash_entry (h)->tls_mask |= PLT_KEEP;
+             plt_list = &h->plt.plist;
            }
+         if (plt_list == NULL)
+           plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                             rel->r_addend,
+                                             NON_GOT | PLT_KEEP);
+         if (!update_plt_info (abfd, plt_list, rel->r_addend))
+           return FALSE;
          break;
 
          /* The following relocations don't need to propagate the
@@ -5407,17 +4829,59 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_REL16_LO:
        case R_PPC64_REL16_HI:
        case R_PPC64_REL16_HA:
+       case R_PPC64_REL16_HIGH:
+       case R_PPC64_REL16_HIGHA:
+       case R_PPC64_REL16_HIGHER:
+       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;
+
+         /* Not supported as a dynamic relocation.  */
+       case R_PPC64_ADDR64_LOCAL:
+         if (bfd_link_pic (info))
+           {
+             if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
+               ppc_howto_init ();
+             /* xgettext:c-format */
+             info->callbacks->einfo (_("%H: %s reloc unsupported "
+                                       "in shared libraries and PIEs\n"),
+                                     abfd, sec, rel->r_offset,
+                                     ppc64_elf_howto_table[r_type]->name);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
          break;
 
        case R_PPC64_TOC16:
        case R_PPC64_TOC16_DS:
          htab->do_multi_toc = 1;
          ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1;
+         /* Fall through.  */
        case R_PPC64_TOC16_LO:
        case R_PPC64_TOC16_HI:
        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.  */
+       case R_PPC64_ENTRY:
          break;
 
          /* This relocation describes the C++ object vtable hierarchy.
@@ -5430,9 +4894,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:
-         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;
 
@@ -5466,27 +4928,56 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            if (dest != sec)
              ppc64_elf_section_data (sec)->has_14bit_branch = 1;
          }
+         goto rel24;
+
+       case R_PPC64_PLTCALL:
+       case R_PPC64_PLTCALL_NOTOC:
+         ppc64_elf_section_data (sec)->has_pltcall = 1;
          /* Fall through.  */
 
        case R_PPC64_REL24:
-         if (h != NULL && ifunc == NULL)
+       case R_PPC64_REL24_NOTOC:
+       rel24:
+         plt_list = ifunc;
+         if (h != NULL)
            {
-             /* We may need a .plt entry if the function this reloc
-                refers to is in a shared lib.  */
-             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
-               return FALSE;
              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)
-               sec->has_tls_reloc = 1;
+               {
+                 sec->has_tls_reloc = 1;
+                 if (rel != relocs
+                     && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
+                         || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
+                   /* We have a new-style __tls_get_addr call with
+                      a marker reloc.  */
+                   ;
+                 else
+                   /* Mark this section as having an old-style call.  */
+                   sec->nomark_tls_get_addr = 1;
+               }
+             plt_list = &h->plt.plist;
            }
+
+         /* We may need a .plt entry if the function this reloc
+            refers to is in a shared lib.  */
+         if (plt_list
+             && !update_plt_info (abfd, plt_list, rel->r_addend))
+           return FALSE;
          break;
 
+       case R_PPC64_ADDR14:
+       case R_PPC64_ADDR14_BRNTAKEN:
+       case R_PPC64_ADDR14_BRTAKEN:
+       case R_PPC64_ADDR24:
+         goto dodyn;
+
        case R_PPC64_TPREL64:
          tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
-         if (!info->executable)
+         if (bfd_link_dll (info))
            info->flags |= DF_STATIC_TLS;
          goto dotlstoc;
 
@@ -5511,11 +5002,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        dotlstoc:
          sec->has_tls_reloc = 1;
          if (h != NULL)
-           {
-             struct ppc_link_hash_entry *eh;
-             eh = (struct ppc_link_hash_entry *) h;
-             eh->tls_mask |= tls_type;
-           }
+           ppc_elf_hash_entry (h)->tls_mask |= tls_type & 0xff;
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                        rel->r_addend, tls_type))
@@ -5562,42 +5049,18 @@ 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:
-         if (info->shared)
-           {
-             if (!info->executable)
-               info->flags |= DF_STATIC_TLS;
-             goto dodyn;
-           }
-         break;
+       case R_PPC64_TPREL34:
+         if (bfd_link_dll (info))
+           info->flags |= DF_STATIC_TLS;
+         goto dodyn;
 
        case R_PPC64_ADDR64:
-         if (opd_sym_map != NULL
+         if (is_opd
              && rel + 1 < rel_end
              && ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC)
            {
              if (h != NULL)
-               {
-                 if (h->root.root.string[0] == '.'
-                     && h->root.root.string[1] != 0
-                     && lookup_fdh ((struct ppc_link_hash_entry *) h, htab))
-                   ;
-                 else
-                   ((struct ppc_link_hash_entry *) h)->is_func = 1;
-               }
-             else
-               {
-                 asection *s;
-                 Elf_Internal_Sym *isym;
-
-                 isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-                                               abfd, r_symndx);
-                 if (isym == NULL)
-                   return FALSE;
-
-                 s = bfd_section_from_elf_index (abfd, isym->st_shndx);
-                 if (s != NULL && s != sec)
-                   opd_sym_map[rel->r_offset / 8] = s;
-               }
+               ppc_elf_hash_entry (h)->is_func = 1;
            }
          /* Fall through.  */
 
@@ -5613,12 +5076,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:
-         if (h != NULL && !info->shared && abiversion (abfd) == 2
+       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 (!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;
            }
@@ -5627,21 +5099,17 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_REL30:
        case R_PPC64_REL32:
        case R_PPC64_REL64:
-       case R_PPC64_ADDR14:
-       case R_PPC64_ADDR14_BRNTAKEN:
-       case R_PPC64_ADDR14_BRTAKEN:
-       case R_PPC64_ADDR24:
        case R_PPC64_ADDR32:
        case R_PPC64_UADDR16:
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
        case R_PPC64_TOC:
-         if (h != NULL && !info->shared)
+         if (h != NULL && bfd_link_executable (info))
            /* We may need a copy reloc.  */
            h->non_got_ref = 1;
 
          /* Don't propagate .opd relocs.  */
-         if (NO_OPD_RELOCS && opd_sym_map != NULL)
+         if (NO_OPD_RELOCS && is_opd)
            break;
 
          /* If we are creating a shared library, and this is a reloc
@@ -5666,18 +5134,15 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
        dodyn:
-         if ((info->shared
-              && (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
-                 && !info->shared
-                 && h != NULL
-                 && (h->root.type == bfd_link_hash_defweak
-                     || !h->def_regular))
-             || (!info->shared
+         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))
            {
              /* We must copy these reloc types into the output file.
@@ -5699,7 +5164,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  struct elf_dyn_relocs *p;
                  struct elf_dyn_relocs **head;
 
-                 head = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+                 head = &h->dyn_relocs;
                  p = *head;
                  if (p == NULL || p->sec != sec)
                    {
@@ -5771,8 +5236,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
    object file when linking.  */
 
 static bfd_boolean
-ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+ppc64_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
+  bfd *obfd = info->output_bfd;
   unsigned long iflags, oflags;
 
   if ((ibfd->flags & BFD_LINKER_CREATED) != 0)
@@ -5781,39 +5247,35 @@ ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   if (!is_ppc64_elf (ibfd) || !is_ppc64_elf (obfd))
     return TRUE;
 
-  if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+  if (!_bfd_generic_verify_endian_match (ibfd, info))
     return FALSE;
 
   iflags = elf_elfheader (ibfd)->e_flags;
   oflags = elf_elfheader (obfd)->e_flags;
 
-  if (!elf_flags_init (obfd) || oflags == 0)
+  if (iflags & ~EF_PPC64_ABI)
     {
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = iflags;
-    }
-  else if (iflags == oflags || iflags == 0)
-    ;
-  else if (iflags & ~EF_PPC64_ABI)
-    {
-      (*_bfd_error_handler)
-       (_("%B uses unknown e_flags 0x%lx"), ibfd, iflags);
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%pB uses unknown e_flags 0x%lx"), ibfd, iflags);
       bfd_set_error (bfd_error_bad_value);
       return FALSE;
     }
-  else
+  else if (iflags != oflags && iflags != 0)
     {
-      (*_bfd_error_handler)
-       (_("%B: ABI version %ld is not compatible with ABI version %ld output"),
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%pB: ABI version %ld is not compatible with ABI version %ld output"),
         ibfd, iflags, oflags);
       bfd_set_error (bfd_error_bad_value);
       return FALSE;
     }
 
-  /* Merge Tag_compatibility attributes and any common GNU ones.  */
-  _bfd_elf_merge_object_attributes (ibfd, obfd);
+  if (!_bfd_elf_ppc_merge_fp_attributes (ibfd, info))
+    return FALSE;
 
-  return TRUE;
+  /* Merge Tag_compatibility attributes and any common GNU ones.  */
+  return _bfd_elf_merge_object_attributes (ibfd, info);
 }
 
 static bfd_boolean
@@ -5826,7 +5288,6 @@ ppc64_elf_print_private_bfd_data (bfd *abfd, void *ptr)
     {
       FILE *file = ptr;
 
-      /* xgettext:c-format */
       fprintf (file, _("private flags = 0x%lx:"),
               elf_elfheader (abfd)->e_flags);
 
@@ -5840,7 +5301,8 @@ ppc64_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 }
 
 /* OFFSET in OPD_SEC specifies a function descriptor.  Return the address
-   of the code entry point, and its section.  */
+   of the code entry point, and its section, which must be in the same
+   object as OPD_SEC.  Returns (bfd_vma) -1 on error.  */
 
 static bfd_vma
 opd_entry_value (asection *opd_sec,
@@ -5867,6 +5329,10 @@ opd_entry_value (asection *opd_sec,
          ppc64_elf_tdata (opd_bfd)->opd.contents = contents;
        }
 
+      /* PR 17512: file: 64b9dfbb.  */
+      if (offset + 7 >= opd_sec->size || offset + 7 < offset)
+       return (bfd_vma) -1;
+
       val = bfd_get_64 (opd_bfd, contents + offset);
       if (code_sec != NULL)
        {
@@ -5902,10 +5368,12 @@ opd_entry_value (asection *opd_sec,
   relocs = ppc64_elf_tdata (opd_bfd)->opd.relocs;
   if (relocs == NULL)
     relocs = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
+  /* PR 17512: file: df8e1fd6.  */
+  if (relocs == NULL)
+    return (bfd_vma) -1;
 
   /* Go find the opd reloc at the sym address.  */
   lo = relocs;
-  BFD_ASSERT (lo != NULL);
   hi = lo + opd_sec->reloc_count - 1; /* ignore last reloc */
   val = (bfd_vma) -1;
   while (lo < hi)
@@ -5923,32 +5391,10 @@ opd_entry_value (asection *opd_sec,
              && ELF64_R_TYPE ((look + 1)->r_info) == R_PPC64_TOC)
            {
              unsigned long symndx = ELF64_R_SYM (look->r_info);
-             asection *sec;
-
-             if (symndx < symtab_hdr->sh_info
-                 || elf_sym_hashes (opd_bfd) == NULL)
-               {
-                 Elf_Internal_Sym *sym;
-
-                 sym = (Elf_Internal_Sym *) symtab_hdr->contents;
-                 if (sym == NULL)
-                   {
-                     size_t symcnt = symtab_hdr->sh_info;
-                     if (elf_sym_hashes (opd_bfd) == NULL)
-                       symcnt = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
-                     sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, symcnt,
-                                                 0, NULL, NULL, NULL);
-                     if (sym == NULL)
-                       break;
-                     symtab_hdr->contents = (bfd_byte *) sym;
-                   }
+             asection *sec = NULL;
 
-                 sym += symndx;
-                 val = sym->st_value;
-                 sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
-                 BFD_ASSERT ((sec->flags & SEC_MERGE) == 0);
-               }
-             else
+             if (symndx >= symtab_hdr->sh_info
+                 && elf_sym_hashes (opd_bfd) != NULL)
                {
                  struct elf_link_hash_entry **sym_hashes;
                  struct elf_link_hash_entry *rh;
@@ -5958,28 +5404,51 @@ opd_entry_value (asection *opd_sec,
                  if (rh != NULL)
                    {
                      rh = elf_follow_link (rh);
-                     BFD_ASSERT (rh->root.type == bfd_link_hash_defined
-                                 || rh->root.type == bfd_link_hash_defweak);
-                     val = rh->root.u.def.value;
-                     sec = rh->root.u.def.section;
+                     if (rh->root.type != bfd_link_hash_defined
+                         && rh->root.type != bfd_link_hash_defweak)
+                       break;
+                     if (rh->root.u.def.section->owner == opd_bfd)
+                       {
+                         val = rh->root.u.def.value;
+                         sec = rh->root.u.def.section;
+                       }
+                   }
+               }
+
+             if (sec == NULL)
+               {
+                 Elf_Internal_Sym *sym;
+
+                 if (symndx < symtab_hdr->sh_info)
+                   {
+                     sym = (Elf_Internal_Sym *) symtab_hdr->contents;
+                     if (sym == NULL)
+                       {
+                         size_t symcnt = symtab_hdr->sh_info;
+                         sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+                                                     symcnt, 0,
+                                                     NULL, NULL, NULL);
+                         if (sym == NULL)
+                           break;
+                         symtab_hdr->contents = (bfd_byte *) sym;
+                       }
+                     sym += symndx;
                    }
                  else
                    {
-                     /* Handle the odd case where we can be called
-                        during bfd_elf_link_add_symbols before the
-                        symbol hashes have been fully populated.  */
-                     Elf_Internal_Sym *sym;
-
-                     sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, 1,
-                                                 symndx, NULL, NULL, NULL);
+                     sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+                                                 1, symndx,
+                                                 NULL, NULL, NULL);
                      if (sym == NULL)
                        break;
-
-                     val = sym->st_value;
-                     sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
-                     free (sym);
                    }
+                 sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
+                 if (sec == NULL)
+                   break;
+                 BFD_ASSERT ((sec->flags & SEC_MERGE) == 0);
+                 val = sym->st_value;
                }
+
              val += look->r_addend;
              if (code_off != NULL)
                *code_off = val;
@@ -5990,7 +5459,7 @@ opd_entry_value (asection *opd_sec,
                  else
                    *code_sec = sec;
                }
-             if (sec != NULL && sec->output_section != NULL)
+             if (sec->output_section != NULL)
                val += sec->output_section->vma + sec->output_offset;
            }
          break;
@@ -6020,7 +5489,23 @@ ppc64_elf_maybe_function_sym (const asymbol *sym, asection *sec,
 
   if (strcmp (sym->section->name, ".opd") == 0)
     {
-      if (opd_entry_value (sym->section, sym->value,
+      struct _opd_sec_data *opd = get_opd_info (sym->section);
+      bfd_vma symval = sym->value;
+
+      if (opd != NULL
+         && opd->adjust != NULL
+         && elf_section_data (sym->section)->relocs != NULL)
+       {
+         /* opd_entry_value will use cached relocs that have been
+            adjusted, but with raw symbols.  That means both local
+            and global symbols need adjusting.  */
+         long adjust = opd->adjust[OPD_NDX (symval)];
+         if (adjust == -1)
+           return 0;
+         symval += adjust;
+       }
+
+      if (opd_entry_value (sym->section, symval,
                           &sec, code_off, TRUE) == (bfd_vma) -1)
        return 0;
       /* An old ABI binary with dot-syms has a size of 24 on the .opd
@@ -6049,6 +5534,22 @@ ppc64_elf_maybe_function_sym (const asymbol *sym, asection *sec,
   return size;
 }
 
+/* Return true if symbol is a strong function defined in an ELFv2
+   object with st_other localentry bits of zero, ie. its local entry
+   point coincides with its global entry point.  */
+
+static bfd_boolean
+is_elfv2_localentry0 (struct elf_link_hash_entry *h)
+{
+  return (h != NULL
+         && h->type == STT_FUNC
+         && h->root.type == bfd_link_hash_defined
+         && (STO_PPC64_LOCAL_MASK & h->other) == 0
+         && !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);
+}
+
 /* Return true if symbol is defined in a regular object file.  */
 
 static bfd_boolean
@@ -6093,6 +5594,44 @@ defined_func_desc (struct ppc_link_hash_entry *fh)
   return NULL;
 }
 
+/* Given H is a symbol that satisfies is_static_defined, return the
+   value in the output file.  */
+
+static bfd_vma
+defined_sym_val (struct elf_link_hash_entry *h)
+{
+  return (h->root.u.def.section->output_section->vma
+         + h->root.u.def.section->output_offset
+         + h->root.u.def.value);
+}
+
+/* Return true if H matches __tls_get_addr or one of its variants.  */
+
+static bfd_boolean
+is_tls_get_addr (struct elf_link_hash_entry *h,
+                struct ppc_link_hash_table *htab)
+{
+  return (h == &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
+ppc64_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+
+  if (htab != NULL && htab->need_func_desc_adj)
+    {
+      elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
+      htab->need_func_desc_adj = 0;
+    }
+  return bfd_elf_gc_sections (abfd, info);
+}
+
 /* Mark all our entry sym sections, both opd and code section.  */
 
 static void
@@ -6109,8 +5648,8 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
       struct ppc_link_hash_entry *eh, *fh;
       asection *sec;
 
-      eh = (struct ppc_link_hash_entry *)
-       elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, TRUE);
+      eh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym->name,
+                                                    FALSE, FALSE, TRUE));
       if (eh == NULL)
        continue;
       if (eh->elf.root.type != bfd_link_hash_defined
@@ -6142,8 +5681,9 @@ static bfd_boolean
 ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info = (struct bfd_link_info *) inf;
-  struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
+  struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h);
   struct ppc_link_hash_entry *fdh;
+  struct bfd_elf_dynamic_list *d = info->dynamic_list;
 
   /* Dynamic linking info is on the func descriptor sym.  */
   fdh = defined_func_desc (eh);
@@ -6152,12 +5692,18 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
 
   if ((eh->elf.root.type == bfd_link_hash_defined
        || eh->elf.root.type == bfd_link_hash_defweak)
-      && (eh->elf.ref_dynamic
-         || (!info->executable
-             && eh->elf.def_regular
+      && ((eh->elf.ref_dynamic && !eh->elf.forced_local)
+         || ((eh->elf.def_regular || ELF_COMMON_DEF_P (&eh->elf))
              && ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
              && ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN
-             && (strchr (eh->elf.root.root.string, ELF_VER_CHR) != NULL
+             && (!bfd_link_executable (info)
+                 || info->gc_keep_exported
+                 || info->export_dynamic
+                 || (eh->elf.dynamic
+                     && d != NULL
+                     && (*d->match) (&d->head, NULL,
+                                     eh->elf.root.root.string)))
+             && (eh->elf.versioned >= versioned
                  || !bfd_hide_sym_by_version (info->version_info,
                                               eh->elf.root.root.string)))))
     {
@@ -6219,10 +5765,18 @@ ppc64_elf_gc_mark_hook (asection *sec,
            {
            case bfd_link_hash_defined:
            case bfd_link_hash_defweak:
-             eh = (struct ppc_link_hash_entry *) h;
+             eh = ppc_elf_hash_entry (h);
              fdh = defined_func_desc (eh);
              if (fdh != NULL)
-               eh = fdh;
+               {
+                 /* -mcall-aixdesc code references the dot-symbol on
+                    a call reloc.  Mark the function descriptor too
+                    against garbage collection.  */
+                 fdh->elf.mark = 1;
+                 if (fdh->elf.is_weakalias)
+                   weakdef (&fdh->elf)->mark = 1;
+                 eh = fdh;
+               }
 
              /* Function descriptor syms cause the associated
                 function code sym section to be marked.  */
@@ -6262,238 +5816,101 @@ ppc64_elf_gc_mark_hook (asection *sec,
        {
          rsec->gc_mark = 1;
 
-         rsec = opd->func_sec[(sym->st_value + rel->r_addend) / 8];
+         rsec = opd->func_sec[OPD_NDX (sym->st_value + rel->r_addend)];
        }
     }
 
   return rsec;
 }
 
-/* Update the .got, .plt. and dynamic reloc reference counts for the
-   section being removed.  */
+/* The maximum size of .sfpr.  */
+#define SFPR_MAX (218*4)
 
-static bfd_boolean
-ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
-                        asection *sec, const Elf_Internal_Rela *relocs)
+struct sfpr_def_parms
 {
-  struct ppc_link_hash_table *htab;
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  struct got_entry **local_got_ents;
-  const Elf_Internal_Rela *rel, *relend;
-
-  if (info->relocatable)
-    return TRUE;
+  const char name[12];
+  unsigned char lo, hi;
+  bfd_byte *(*write_ent) (bfd *, bfd_byte *, int);
+  bfd_byte *(*write_tail) (bfd *, bfd_byte *, int);
+};
 
-  if ((sec->flags & SEC_ALLOC) == 0)
-    return TRUE;
+/* Auto-generate _save*, _rest* functions in .sfpr.
+   If STUB_SEC is non-null, define alias symbols in STUB_SEC
+   instead.  */
 
-  elf_section_data (sec)->local_dynrel = NULL;
+static bfd_boolean
+sfpr_define (struct bfd_link_info *info,
+            const struct sfpr_def_parms *parm,
+            asection *stub_sec)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  unsigned int i;
+  size_t len = strlen (parm->name);
+  bfd_boolean writing = FALSE;
+  char sym[16];
 
-  htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
-  symtab_hdr = &elf_symtab_hdr (abfd);
-  sym_hashes = elf_sym_hashes (abfd);
-  local_got_ents = elf_local_got_ents (abfd);
+  memcpy (sym, parm->name, len);
+  sym[len + 2] = 0;
 
-  relend = relocs + sec->reloc_count;
-  for (rel = relocs; rel < relend; rel++)
+  for (i = parm->lo; i <= parm->hi; i++)
     {
-      unsigned long r_symndx;
-      enum elf_ppc64_reloc_type r_type;
-      struct elf_link_hash_entry *h = NULL;
-      unsigned char tls_type = 0;
-
-      r_symndx = ELF64_R_SYM (rel->r_info);
-      r_type = ELF64_R_TYPE (rel->r_info);
-      if (r_symndx >= symtab_hdr->sh_info)
-       {
-         struct ppc_link_hash_entry *eh;
-         struct elf_dyn_relocs **pp;
-         struct elf_dyn_relocs *p;
-
-         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-         h = elf_follow_link (h);
-         eh = (struct ppc_link_hash_entry *) h;
-
-         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-           if (p->sec == sec)
-             {
-               /* Everything must go for SEC.  */
-               *pp = p->next;
-               break;
-             }
-       }
+      struct ppc_link_hash_entry *h;
 
-      if (is_branch_reloc (r_type))
+      sym[len + 0] = i / 10 + '0';
+      sym[len + 1] = i % 10 + '0';
+      h = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym,
+                                                   writing, TRUE, TRUE));
+      if (stub_sec != NULL)
        {
-         struct plt_entry **ifunc = NULL;
-         if (h != NULL)
+         if (h != NULL
+             && h->elf.root.type == bfd_link_hash_defined
+             && h->elf.root.u.def.section == htab->sfpr)
            {
-             if (h->type == STT_GNU_IFUNC)
-               ifunc = &h->plt.plist;
-           }
-         else if (local_got_ents != NULL)
-           {
-             struct plt_entry **local_plt = (struct plt_entry **)
-               (local_got_ents + symtab_hdr->sh_info);
-             unsigned char *local_got_tls_masks = (unsigned char *)
-               (local_plt + symtab_hdr->sh_info);
-             if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
-               ifunc = local_plt + r_symndx;
-           }
-         if (ifunc != NULL)
-           {
-             struct plt_entry *ent;
-
-             for (ent = *ifunc; ent != NULL; ent = ent->next)
-               if (ent->addend == rel->r_addend)
-                 break;
-             if (ent == NULL)
-               abort ();
-             if (ent->plt.refcount > 0)
-               ent->plt.refcount -= 1;
-             continue;
-           }
-       }
-
-      switch (r_type)
-       {
-       case R_PPC64_GOT_TLSLD16:
-       case R_PPC64_GOT_TLSLD16_LO:
-       case R_PPC64_GOT_TLSLD16_HI:
-       case R_PPC64_GOT_TLSLD16_HA:
-         tls_type = TLS_TLS | TLS_LD;
-         goto dogot;
-
-       case R_PPC64_GOT_TLSGD16:
-       case R_PPC64_GOT_TLSGD16_LO:
-       case R_PPC64_GOT_TLSGD16_HI:
-       case R_PPC64_GOT_TLSGD16_HA:
-         tls_type = TLS_TLS | TLS_GD;
-         goto dogot;
-
-       case R_PPC64_GOT_TPREL16_DS:
-       case R_PPC64_GOT_TPREL16_LO_DS:
-       case R_PPC64_GOT_TPREL16_HI:
-       case R_PPC64_GOT_TPREL16_HA:
-         tls_type = TLS_TLS | TLS_TPREL;
-         goto dogot;
-
-       case R_PPC64_GOT_DTPREL16_DS:
-       case R_PPC64_GOT_DTPREL16_LO_DS:
-       case R_PPC64_GOT_DTPREL16_HI:
-       case R_PPC64_GOT_DTPREL16_HA:
-         tls_type = TLS_TLS | TLS_DTPREL;
-         goto dogot;
-
-       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_DS:
-       dogot:
-         {
-           struct got_entry *ent;
-
-           if (h != NULL)
-             ent = h->got.glist;
-           else
-             ent = local_got_ents[r_symndx];
-
-           for (; ent != NULL; ent = ent->next)
-             if (ent->addend == rel->r_addend
-                 && ent->owner == abfd
-                 && ent->tls_type == tls_type)
-               break;
-           if (ent == NULL)
-             abort ();
-           if (ent->got.refcount > 0)
-             ent->got.refcount -= 1;
-         }
-         break;
-
-       case R_PPC64_PLT16_HA:
-       case R_PPC64_PLT16_HI:
-       case R_PPC64_PLT16_LO:
-       case R_PPC64_PLT32:
-       case R_PPC64_PLT64:
-       case R_PPC64_REL14:
-       case R_PPC64_REL14_BRNTAKEN:
-       case R_PPC64_REL14_BRTAKEN:
-       case R_PPC64_REL24:
-         if (h != NULL)
-           {
-             struct plt_entry *ent;
-
-             for (ent = h->plt.plist; ent != NULL; ent = ent->next)
-               if (ent->addend == rel->r_addend)
-                 break;
-             if (ent != NULL && ent->plt.refcount > 0)
-               ent->plt.refcount -= 1;
+             struct elf_link_hash_entry *s;
+             char buf[32];
+             sprintf (buf, "%08x.%s", stub_sec->id & 0xffffffff, sym);
+             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;
+                 s->root.u.def.value = (stub_sec->size - htab->sfpr->size
+                                        + h->elf.root.u.def.value);
+                 s->ref_regular = 1;
+                 s->def_regular = 1;
+                 s->ref_regular_nonweak = 1;
+                 s->forced_local = 1;
+                 s->non_elf = 0;
+                 s->root.linker_def = 1;
+               }
            }
-         break;
-
-       default:
-         break;
+         continue;
        }
-    }
-  return TRUE;
-}
-
-/* The maximum size of .sfpr.  */
-#define SFPR_MAX (218*4)
-
-struct sfpr_def_parms
-{
-  const char name[12];
-  unsigned char lo, hi;
-  bfd_byte * (*write_ent) (bfd *, bfd_byte *, int);
-  bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
-};
-
-/* Auto-generate _save*, _rest* functions in .sfpr.  */
-
-static bfd_boolean
-sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
-{
-  struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  unsigned int i;
-  size_t len = strlen (parm->name);
-  bfd_boolean writing = FALSE;
-  char sym[16];
-
-  if (htab == NULL)
-    return FALSE;
-
-  memcpy (sym, parm->name, len);
-  sym[len + 2] = 0;
-
-  for (i = parm->lo; i <= parm->hi; i++)
-    {
-      struct elf_link_hash_entry *h;
-
-      sym[len + 0] = i / 10 + '0';
-      sym[len + 1] = i % 10 + '0';
-      h = elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
-      if (h != NULL
-         && !h->def_regular)
+      if (h != NULL)
        {
-         h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = htab->sfpr;
-         h->root.u.def.value = htab->sfpr->size;
-         h->type = STT_FUNC;
-         h->def_regular = 1;
-         _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
-         writing = TRUE;
-         if (htab->sfpr->contents == NULL)
+         h->save_res = 1;
+         if (!h->elf.def_regular)
            {
-             htab->sfpr->contents = bfd_alloc (htab->elf.dynobj, SFPR_MAX);
+             h->elf.root.type = bfd_link_hash_defined;
+             h->elf.root.u.def.section = htab->sfpr;
+             h->elf.root.u.def.value = htab->sfpr->size;
+             h->elf.type = STT_FUNC;
+             h->elf.def_regular = 1;
+             h->elf.non_elf = 0;
+             _bfd_elf_link_hash_hide_symbol (info, &h->elf, TRUE);
+             writing = TRUE;
              if (htab->sfpr->contents == NULL)
-               return FALSE;
+               {
+                 htab->sfpr->contents
+                   = bfd_alloc (htab->elf.dynobj, SFPR_MAX);
+                 if (htab->sfpr->contents == NULL)
+                   return FALSE;
+               }
            }
        }
       if (writing)
@@ -6672,6 +6089,84 @@ restvr_tail (bfd *abfd, bfd_byte *p, int r)
   return p + 4;
 }
 
+#define STDU_R1_0R1    0xf8210001
+#define ADDI_R1_R1     0x38210000
+
+/* Emit prologue of wrapper preserving regs around a call to
+   __tls_get_addr_opt.  */
+
+static bfd_byte *
+tls_get_addr_prologue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab)
+{
+  unsigned int i;
+
+  bfd_put_32 (obfd, MFLR_R0, p);
+  p += 4;
+  bfd_put_32 (obfd, STD_R0_0R1 + 16, p);
+  p += 4;
+
+  if (htab->opd_abi)
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd,
+                     STD_R0_0R1 | i << 21 | (-(13 - i) * 8 & 0xffff), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, STDU_R1_0R1 | (-128 & 0xffff), p);
+      p += 4;
+    }
+  else
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd,
+                     STD_R0_0R1 | i << 21 | (-(12 - i) * 8 & 0xffff), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, STDU_R1_0R1 | (-96 & 0xffff), p);
+      p += 4;
+    }
+  return p;
+}
+
+/* Emit epilogue of wrapper preserving regs around a call to
+   __tls_get_addr_opt.  */
+
+static bfd_byte *
+tls_get_addr_epilogue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab)
+{
+  unsigned int i;
+
+  if (htab->opd_abi)
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (128 - (13 - i) * 8), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, ADDI_R1_R1 | 128, p);
+      p += 4;
+    }
+  else
+    {
+      for (i = 4; i < 12; i++)
+       {
+         bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (96 - (12 - i) * 8), p);
+         p += 4;
+       }
+      bfd_put_32 (obfd, ADDI_R1_R1 | 96, p);
+      p += 4;
+    }
+  bfd_put_32 (obfd, LD_R0_0R1 | 16, p);
+  p += 4;
+  bfd_put_32 (obfd, MTLR_R0, p);
+  p += 4;
+  bfd_put_32 (obfd, BLR, p);
+  p += 4;
+  return p;
+}
+
 /* Called via elf_link_hash_traverse to transfer dynamic linking
    information on function code symbol entries to their corresponding
    function descriptor symbol entries.  */
@@ -6681,27 +6176,37 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
-  struct plt_entry *ent;
   struct ppc_link_hash_entry *fh;
   struct ppc_link_hash_entry *fdh;
   bfd_boolean force_local;
 
-  fh = (struct ppc_link_hash_entry *) h;
+  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;
 
+  /* Find the corresponding function descriptor symbol.  */
+  fdh = lookup_fdh (fh, htab);
+
   /* Resolve undefined references to dot-symbols as the value
      in the function descriptor, if we have one in a regular object.
      This is to satisfy cases like ".quad .foo".  Calls to functions
      in dynamic objects are handled elsewhere.  */
-  if (fh->elf.root.type == bfd_link_hash_undefweak
-      && fh->was_undefined
-      && (fdh = defined_func_desc (fh)) != NULL
+  if ((fh->elf.root.type == bfd_link_hash_undefined
+       || fh->elf.root.type == bfd_link_hash_undefweak)
+      && (fdh->elf.root.type == bfd_link_hash_defined
+         || fdh->elf.root.type == bfd_link_hash_defweak)
       && get_opd_info (fdh->elf.root.u.def.section) != NULL
       && opd_entry_value (fdh->elf.root.u.def.section,
                          fdh->elf.root.u.def.value,
@@ -6714,25 +6219,20 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
       fh->elf.def_dynamic = fdh->elf.def_dynamic;
     }
 
-  /* If this is a function code symbol, transfer dynamic linking
-     information to the function descriptor symbol.  */
-  if (!fh->is_func)
-    return TRUE;
-
-  for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
-    if (ent->plt.refcount > 0)
-      break;
-  if (ent == NULL
-      || fh->elf.root.root.string[0] != '.'
-      || fh->elf.root.root.string[1] == '\0')
-    return TRUE;
+  if (!fh->elf.dynamic)
+    {
+      struct plt_entry *ent;
 
-  /* Find the corresponding function descriptor symbol.  Create it
-     as undefined if necessary.  */
+      for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
+       if (ent->plt.refcount > 0)
+         break;
+      if (ent == NULL)
+       return TRUE;
+    }
 
-  fdh = lookup_fdh (fh, htab);
+  /* Create a descriptor as undefined if necessary.  */
   if (fdh == NULL
-      && !info->executable
+      && !bfd_link_executable (info)
       && (fh->elf.root.type == bfd_link_hash_undefined
          || fh->elf.root.type == bfd_link_hash_undefweak))
     {
@@ -6741,51 +6241,30 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
        return FALSE;
     }
 
-  /* Fake function descriptors are made undefweak.  If the function
-     code symbol is strong undefined, make the fake sym the same.
-     If the function code symbol is defined, then force the fake
-     descriptor local;  We can't support overriding of symbols in a
-     shared library on a fake descriptor.  */
-
+  /* We can't support overriding of symbols on a fake descriptor.  */
   if (fdh != NULL
       && fdh->fake
-      && fdh->elf.root.type == bfd_link_hash_undefweak)
-    {
-      if (fh->elf.root.type == bfd_link_hash_undefined)
-       {
-         fdh->elf.root.type = bfd_link_hash_undefined;
-         bfd_link_add_undef (&htab->elf.root, &fdh->elf.root);
-       }
-      else if (fh->elf.root.type == bfd_link_hash_defined
-              || fh->elf.root.type == bfd_link_hash_defweak)
-       {
-         _bfd_elf_link_hash_hide_symbol (info, &fdh->elf, TRUE);
-       }
-    }
+      && (fh->elf.root.type == bfd_link_hash_defined
+         || fh->elf.root.type == bfd_link_hash_defweak))
+    _bfd_elf_link_hash_hide_symbol (info, &fdh->elf, TRUE);
 
-  if (fdh != NULL
-      && !fdh->elf.forced_local
-      && (!info->executable
-         || fdh->elf.def_dynamic
-         || fdh->elf.ref_dynamic
-         || (fdh->elf.root.type == bfd_link_hash_undefweak
-             && ELF_ST_VISIBILITY (fdh->elf.other) == STV_DEFAULT)))
-    {
-      if (fdh->elf.dynindx == -1)
-       if (! bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
-         return FALSE;
+  /* Transfer dynamic linking information to the function descriptor.  */
+  if (fdh != NULL)
+    {
       fdh->elf.ref_regular |= fh->elf.ref_regular;
       fdh->elf.ref_dynamic |= fh->elf.ref_dynamic;
       fdh->elf.ref_regular_nonweak |= fh->elf.ref_regular_nonweak;
       fdh->elf.non_got_ref |= fh->elf.non_got_ref;
-      if (ELF_ST_VISIBILITY (fh->elf.other) == STV_DEFAULT)
-       {
-         move_plt_plist (fh, fdh);
-         fdh->elf.needs_plt = 1;
-       }
-      fdh->is_func_descriptor = 1;
-      fdh->oh = fh;
-      fh->oh = fdh;
+      fdh->elf.dynamic |= fh->elf.dynamic;
+      fdh->elf.needs_plt |= (fh->elf.needs_plt
+                            || fh->elf.type == STT_FUNC
+                            || fh->elf.type == STT_GNU_IFUNC);
+      move_plt_plist (fh, fdh);
+
+      if (!fdh->elf.forced_local
+         && fh->elf.dynindx != -1)
+       if (!bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
+         return FALSE;
     }
 
   /* Now that the info is on the function descriptor, clear the
@@ -6804,6 +6283,22 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
+static const struct sfpr_def_parms save_res_funcs[] =
+  {
+    { "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
+    { "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
+    { "_restgpr0_", 30, 31, restgpr0, restgpr0_tail },
+    { "_savegpr1_", 14, 31, savegpr1, savegpr1_tail },
+    { "_restgpr1_", 14, 31, restgpr1, restgpr1_tail },
+    { "_savefpr_", 14, 31, savefpr, savefpr0_tail },
+    { "_restfpr_", 14, 29, restfpr, restfpr0_tail },
+    { "_restfpr_", 30, 31, restfpr, restfpr0_tail },
+    { "._savef", 14, 31, savefpr, savefpr1_tail },
+    { "._restf", 14, 31, restfpr, restfpr1_tail },
+    { "_savevr_", 20, 31, savevr, savevr_tail },
+    { "_restvr_", 20, 31, restvr, restvr_tail }
+  };
+
 /* Called near the start of bfd_elf_size_dynamic_sections.  We use
    this hook to a) provide some gcc support functions, and b) transfer
    dynamic linking information gathered so far on function code symbol
@@ -6814,91 +6309,146 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
                            struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
-  unsigned int i;
-  static const struct sfpr_def_parms funcs[] =
-    {
-      { "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
-      { "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
-      { "_restgpr0_", 30, 31, restgpr0, restgpr0_tail },
-      { "_savegpr1_", 14, 31, savegpr1, savegpr1_tail },
-      { "_restgpr1_", 14, 31, restgpr1, restgpr1_tail },
-      { "_savefpr_", 14, 31, savefpr, savefpr0_tail },
-      { "_restfpr_", 14, 29, restfpr, restfpr0_tail },
-      { "_restfpr_", 30, 31, restfpr, restfpr0_tail },
-      { "._savef", 14, 31, savefpr, savefpr1_tail },
-      { "._restf", 14, 31, restfpr, restfpr1_tail },
-      { "_savevr_", 20, 31, savevr, savevr_tail },
-      { "_restvr_", 20, 31, restvr, restvr_tail }
-    };
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
-  if (!info->relocatable
-      && htab->elf.hgot != NULL)
-    _bfd_elf_link_hash_hide_symbol (info, htab->elf.hgot, TRUE);
+  /* Provide any missing _save* and _rest* functions.  */
+  if (htab->sfpr != NULL)
+    {
+      unsigned int i;
 
-  if (htab->sfpr == NULL)
-    /* We don't have any relocs.  */
-    return TRUE;
+      htab->sfpr->size = 0;
+      for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
+       if (!sfpr_define (info, &save_res_funcs[i], NULL))
+         return FALSE;
+      if (htab->sfpr->size == 0)
+       htab->sfpr->flags |= SEC_EXCLUDE;
+    }
 
-  /* Provide any missing _save* and _rest* functions.  */
-  htab->sfpr->size = 0;
-  if (!info->relocatable)
-    for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
-      if (!sfpr_define (info, &funcs[i]))
-       return FALSE;
+  if (bfd_link_relocatable (info))
+    return TRUE;
 
-  elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
+  if (htab->elf.hgot != NULL)
+    {
+      _bfd_elf_link_hash_hide_symbol (info, htab->elf.hgot, TRUE);
+      /* Make .TOC. defined so as to prevent it being made dynamic.
+        The wrong value here is fixed later in ppc64_elf_set_toc.  */
+      if (!htab->elf.hgot->def_regular
+         || htab->elf.hgot->root.type != bfd_link_hash_defined)
+       {
+         htab->elf.hgot->root.type = bfd_link_hash_defined;
+         htab->elf.hgot->root.u.def.value = 0;
+         htab->elf.hgot->root.u.def.section = bfd_abs_section_ptr;
+         htab->elf.hgot->def_regular = 1;
+         htab->elf.hgot->root.linker_def = 1;
+       }
+      htab->elf.hgot->type = STT_OBJECT;
+      htab->elf.hgot->other
+       = (htab->elf.hgot->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
+    }
 
-  if (htab->sfpr->size == 0)
-    htab->sfpr->flags |= SEC_EXCLUDE;
+  if (htab->need_func_desc_adj)
+    {
+      elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
+      htab->need_func_desc_adj = 0;
+    }
 
   return TRUE;
 }
 
-/* Return true if we have dynamic relocs that apply to read-only 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.  */
 
 static bfd_boolean
-readonly_dynrelocs (struct elf_link_hash_entry *h)
+alias_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)
+  struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h);
+  do
     {
-      asection *s = p->sec->output_section;
-
-      if (s != NULL && (s->flags & SEC_READONLY) != 0)
+      if (_bfd_elf_readonly_dynrelocs (&eh->elf))
        return TRUE;
+      eh = ppc_elf_hash_entry (eh->elf.u.alias);
     }
+  while (eh != NULL && &eh->elf != h);
+
   return FALSE;
 }
 
-/* Adjust a symbol defined by a dynamic object and referenced by a
-   regular object.  The current definition is in some section of the
-   dynamic object, but we're not including those sections.  We have to
-   change the definition to something the rest of the link can
-   understand.  */
+/* Return whether EH has pc-relative dynamic relocs.  */
 
 static bfd_boolean
-ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
-                                struct elf_link_hash_entry *h)
+pc_dynrelocs (struct ppc_link_hash_entry *eh)
 {
-  struct ppc_link_hash_table *htab;
-  asection *s;
+  struct elf_dyn_relocs *p;
 
-  htab = ppc_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
+  for (p = eh->elf.dyn_relocs; p != NULL; p = p->next)
+    if (p->pc_count != 0)
+      return TRUE;
+  return FALSE;
+}
 
-  /* Deal with function syms.  */
+/* Return true if a global entry stub will be created for H.  Valid
+   for ELFv2 before plt entries have been allocated.  */
+
+static bfd_boolean
+global_entry_stub (struct elf_link_hash_entry *h)
+{
+  struct plt_entry *pent;
+
+  if (!h->pointer_equality_needed
+      || h->def_regular)
+    return FALSE;
+
+  for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+    if (pent->plt.refcount > 0
+       && pent->addend == 0)
+      return TRUE;
+
+  return FALSE;
+}
+
+/* Adjust a symbol defined by a dynamic object and referenced by a
+   regular object.  The current definition is in some section of the
+   dynamic object, but we're not including those sections.  We have to
+   change the definition to something the rest of the link can
+   understand.  */
+
+static bfd_boolean
+ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+                                struct elf_link_hash_entry *h)
+{
+  struct ppc_link_hash_table *htab;
+  asection *s, *srel;
+
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  /* Deal with function syms.  */
   if (h->type == STT_FUNC
       || h->type == STT_GNU_IFUNC
       || h->needs_plt)
     {
+      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
+        function symbol is local and not an ifunc.  We keep dynamic
+        relocs for ifuncs when local rather than always emitting a
+        plt call stub for them and defining the symbol on the call
+        stub.  We can't do that for ELFv1 anyway (a function symbol
+        is defined on a descriptor, not code) and it can be faster at
+        run-time due to not needing to bounce through a stub.  The
+        dyn_relocs for ifuncs will be applied even in a static
+        executable.  */
+      if (!bfd_link_pic (info)
+         && h->type != STT_GNU_IFUNC
+         && local)
+       h->dyn_relocs = NULL;
+
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
       struct plt_entry *ent;
@@ -6907,31 +6457,50 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          break;
       if (ent == NULL
          || (h->type != STT_GNU_IFUNC
-             && (SYMBOL_CALLS_LOCAL (info, h)
-                 || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
-                     && h->root.type == bfd_link_hash_undefweak))))
+             && local
+             && (htab->can_convert_all_inline_plt
+                 || (ppc_elf_hash_entry (h)->tls_mask
+                     & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)))
        {
          h->plt.plist = NULL;
          h->needs_plt = 0;
+         h->pointer_equality_needed = 0;
        }
-      else if (abiversion (info->output_bfd) == 2)
-       {
-         /* After adjust_dynamic_symbol, non_got_ref set in the
-            non-shared case means that we have allocated space in
-            .dynbss for the symbol and thus dyn_relocs for this
-            symbol should be discarded.
-            If we get here we know we are making a PLT entry for this
-            symbol, and in an executable we'd normally resolve
-            relocations against this symbol to the PLT entry.  Allow
-            dynamic relocs if the reference is weak, and the dynamic
-            relocs will not cause text relocation.  */
-         if (!h->ref_regular_nonweak
-             && h->non_got_ref
-             && h->type != STT_GNU_IFUNC
-             && !readonly_dynrelocs (h))
-           h->non_got_ref = 0;
-
-         /* If making a plt entry, then we don't need copy relocs.  */
+      else if (abiversion (info->output_bfd) >= 2)
+       {
+         /* Taking a function's address in a read/write section
+            doesn't require us to define the function symbol in the
+            executable on a global entry stub.  A dynamic reloc can
+            be used instead.  The reason we prefer a few more dynamic
+            relocs is that calling via a global entry stub costs a
+            few more instructions, and pointer_equality_needed causes
+            extra work in ld.so when resolving these symbols.  */
+         if (global_entry_stub (h))
+           {
+             if (!_bfd_elf_readonly_dynrelocs (h))
+               {
+                 h->pointer_equality_needed = 0;
+                 /* If we haven't seen a branch reloc and the symbol
+                    isn't an ifunc then we don't need a plt entry.  */
+                 if (!h->needs_plt)
+                   h->plt.plist = NULL;
+               }
+             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.  */
+               h->dyn_relocs = NULL;
+           }
+
+         /* ELFv2 function symbols can't have copy relocs.  */
+         return TRUE;
+       }
+      else if (!h->needs_plt
+              && !_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.  */
+         h->plt.plist = NULL;
+         h->pointer_equality_needed = 0;
          return TRUE;
        }
     }
@@ -6941,14 +6510,15 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
-  if (h->u.weakdef != NULL)
-    {
-      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
-                 || h->u.weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->u.weakdef->root.u.def.section;
-      h->root.u.def.value = h->u.weakdef->root.u.def.value;
-      if (ELIMINATE_COPY_RELOCS)
-       h->non_got_ref = h->u.weakdef->non_got_ref;
+  if (h->is_weakalias)
+    {
+      struct elf_link_hash_entry *def = weakdef (h);
+      BFD_ASSERT (def->root.type == bfd_link_hash_defined);
+      h->root.u.def.section = def->root.u.def.section;
+      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->dyn_relocs = NULL;
       return TRUE;
     }
 
@@ -6956,7 +6526,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      only references to the symbol are via the global offset table.
      For such cases we need not do anything here; the relocations will
      be handled correctly by relocate_section.  */
-  if (info->shared)
+  if (!bfd_link_executable (info))
     return TRUE;
 
   /* If there are no references to this symbol that do not use the
@@ -6965,26 +6535,43 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
     return TRUE;
 
   /* Don't generate a copy reloc for symbols defined in the executable.  */
-  if (!h->def_dynamic || !h->ref_regular || h->def_regular)
+  if (!h->def_dynamic || !h->ref_regular || h->def_regular
+
+      /* If -z nocopyreloc was given, don't generate them either.  */
+      || info->nocopyreloc
+
+      /* 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
+         && !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
+        definition for the variable.  Text relocations are preferable
+        to an incorrect program.  */
+      || h->protected_def)
     return TRUE;
 
-  /* If we didn't find any dynamic relocs in read-only sections, then
-     we'll be keeping the dynamic relocs and avoiding the copy reloc.  */
-  if (ELIMINATE_COPY_RELOCS && !readonly_dynrelocs (h))
+  if (h->type == STT_FUNC
+      || h->type == STT_GNU_IFUNC)
     {
-      h->non_got_ref = 0;
-      return TRUE;
-    }
+      /* .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;
 
-  if (h->plt.plist != NULL)
-    {
-      /* 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.  */
+      /* 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 `%T' requires lazy plt linking; "
+       (_("%P: copy reloc against `%pT' requires lazy plt linking; "
           "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"),
         h->root.root.string);
     }
@@ -7001,20 +6588,28 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      determine the address it must put in the global offset table, so
      both the dynamic object and the regular object will refer to the
      same memory location for the variable.  */
-
-  /* We must generate a R_PPC64_COPY reloc to tell the dynamic linker
-     to copy the initial value out of the dynamic object and into the
-     runtime process image.  We need to remember the offset into the
-     .rela.bss section we are going to use.  */
+  if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+    {
+      s = htab->elf.sdynrelro;
+      srel = htab->elf.sreldynrelro;
+    }
+  else
+    {
+      s = htab->elf.sdynbss;
+      srel = htab->elf.srelbss;
+    }
   if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
-      htab->relbss->size += sizeof (Elf64_External_Rela);
+      /* We must generate a R_PPC64_COPY reloc to tell the dynamic
+        linker to copy the initial value out of the dynamic object
+        and into the runtime process image.  */
+      srel->size += sizeof (Elf64_External_Rela);
       h->needs_copy = 1;
     }
 
-  s = htab->dynbss;
-
-  return _bfd_elf_adjust_dynamic_copy (h, s);
+  /* We no longer want dyn_relocs.  */
+  h->dyn_relocs = NULL;
+  return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
 /* If given a function descriptor symbol, hide both the function code
@@ -7027,7 +6622,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);
 
-  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;
@@ -7035,7 +6633,7 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
       if (fh == NULL)
        {
          const char *p, *q;
-         struct ppc_link_hash_table *htab;
+         struct elf_link_hash_table *htab = elf_hash_table (info);
          char save;
 
          /* We aren't supposed to use alloca in BFD because on
@@ -7050,12 +6648,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
          p = eh->elf.root.root.string - 1;
          save = *p;
          *(char *) p = '.';
-         htab = ppc_hash_table (info);
-         if (htab == NULL)
-           return;
-
-         fh = (struct ppc_link_hash_entry *)
-           elf_link_hash_lookup (&htab->elf, 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
@@ -7068,8 +6662,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
              while (q >= eh->elf.root.root.string && *q == *p)
                --q, --p;
              if (q < eh->elf.root.root.string && *p == '.')
-               fh = (struct ppc_link_hash_entry *)
-                 elf_link_hash_lookup (&htab->elf, p, FALSE, FALSE, FALSE);
+               fh = ppc_elf_hash_entry (elf_link_hash_lookup (htab, p, FALSE,
+                                                              FALSE, FALSE));
            }
          if (fh != NULL)
            {
@@ -7117,12 +6711,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
        }
 
       if (tls_maskp != NULL)
-       {
-         struct ppc_link_hash_entry *eh;
-
-         eh = (struct ppc_link_hash_entry *) h;
-         *tls_maskp = &eh->tls_mask;
-       }
+       *tls_maskp = &ppc_elf_hash_entry (h)->tls_mask;
     }
   else
     {
@@ -7195,7 +6784,9 @@ get_tls_mask (unsigned char **tls_maskp,
   if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
     return 0;
 
-  if ((*tls_maskp != NULL && **tls_maskp != 0)
+  if ((*tls_maskp != NULL
+       && (**tls_maskp & TLS_TLS) != 0
+       && **tls_maskp != (TLS_TLS | TLS_MARK))
       || sec == NULL
       || ppc64_elf_section_data (sec) == NULL
       || ppc64_elf_section_data (sec)->sec_type != sec_toc)
@@ -7246,8 +6837,8 @@ tocsave_find (struct ppc_link_hash_table *htab,
     return NULL;
   if (ent.sec == NULL || ent.sec->output_section == NULL)
     {
-      (*_bfd_error_handler)
-       (_("%B: undefined symbol on R_PPC64_TOCSAVE relocation"));
+      _bfd_error_handler
+       (_("%pB: undefined symbol on R_PPC64_TOCSAVE relocation"), ibfd);
       return NULL;
     }
 
@@ -7291,7 +6882,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   if (eh->adjust_done)
     return TRUE;
 
@@ -7299,7 +6890,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
   opd = get_opd_info (sym_sec);
   if (opd != NULL && opd->adjust != NULL)
     {
-      long adjust = opd->adjust[eh->elf.root.u.def.value / 8];
+      long adjust = opd->adjust[OPD_NDX (eh->elf.root.u.def.value)];
       if (adjust == -1)
        {
          /* This entry has been deleted.  */
@@ -7346,6 +6937,16 @@ dec_dynrel_count (bfd_vma r_info,
     default:
       return TRUE;
 
+    case R_PPC64_TOC16:
+    case R_PPC64_TOC16_DS:
+    case R_PPC64_TOC16_LO:
+    case R_PPC64_TOC16_HI:
+    case R_PPC64_TOC16_HA:
+    case R_PPC64_TOC16_LO_DS:
+      if (h == NULL)
+       return TRUE;
+      break;
+
     case R_PPC64_TPREL16:
     case R_PPC64_TPREL16_LO:
     case R_PPC64_TPREL16_HI:
@@ -7358,10 +6959,8 @@ dec_dynrel_count (bfd_vma r_info,
     case R_PPC64_TPREL16_HIGHERA:
     case R_PPC64_TPREL16_HIGHEST:
     case R_PPC64_TPREL16_HIGHESTA:
-      if (!info->shared)
-       return TRUE;
-
     case R_PPC64_TPREL64:
+    case R_PPC64_TPREL34:
     case R_PPC64_DTPMOD64:
     case R_PPC64_DTPREL64:
     case R_PPC64_ADDR64:
@@ -7389,6 +6988,15 @@ dec_dynrel_count (bfd_vma r_info,
     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;
     }
 
@@ -7402,17 +7010,18 @@ dec_dynrel_count (bfd_vma r_info,
        return FALSE;
     }
 
-  if ((info->shared
-       && (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
-         && !info->shared
-         && 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;
@@ -7421,7 +7030,7 @@ dec_dynrel_count (bfd_vma r_info,
     {
       struct elf_dyn_relocs *p;
       struct elf_dyn_relocs **pp;
-      pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+      pp = &h->dyn_relocs;
 
       /* elf_gc_sweep may have already removed all dyn relocs associated
         with local syms for a given section.  Also, symbol flags are
@@ -7476,8 +7085,9 @@ dec_dynrel_count (bfd_vma r_info,
        }
     }
 
-  info->callbacks->einfo (_("%P: dynreloc miscount for %B, section %A\n"),
-                         sec->owner, sec);
+  /* xgettext:c-format */
+  _bfd_error_handler (_("dynreloc miscount for %pB, section %pA"),
+                     sec->owner, sec);
   bfd_set_error (bfd_error_bad_value);
   return FALSE;
 }
@@ -7489,21 +7099,25 @@ dec_dynrel_count (bfd_vma r_info,
    applications.  */
 
 bfd_boolean
-ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
+ppc64_elf_edit_opd (struct bfd_link_info *info)
 {
   bfd *ibfd;
   bfd_boolean some_edited = FALSE;
   asection *need_pad = NULL;
+  struct ppc_link_hash_table *htab;
+
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       asection *sec;
       Elf_Internal_Rela *relstart, *rel, *relend;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      bfd_vma offset;
       struct _opd_sec_data *opd;
-      bfd_boolean need_edit, add_aux_fields;
+      bfd_boolean need_edit, add_aux_fields, broken;
       bfd_size_type cnt_16b = 0;
 
       if (!is_ppc64_elf (ibfd))
@@ -7535,8 +7149,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
       /* First run through the relocs to check they are sane, and to
         determine whether we need to edit this opd section.  */
       need_edit = FALSE;
+      broken = FALSE;
       need_pad = sec;
-      offset = 0;
       relend = relstart + sec->reloc_count;
       for (rel = relstart; rel < relend; )
        {
@@ -7545,13 +7159,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
          asection *sym_sec;
          struct elf_link_hash_entry *h;
          Elf_Internal_Sym *sym;
+         bfd_vma offset;
 
-         /* .opd contains a regular array of 16 or 24 byte entries.  We're
+         /* .opd contains an array of 16 or 24 byte entries.  We're
             only interested in the reloc pointing to a function entry
             point.  */
-         if (rel->r_offset != offset
-             || rel + 1 >= relend
-             || (rel + 1)->r_offset != offset + 8)
+         offset = rel->r_offset;
+         if (rel + 1 == relend
+             || rel[1].r_offset != offset + 8)
            {
              /* If someone messes with .opd alignment then after a
                 "ld -r" we might have padding in the middle of .opd.
@@ -7559,19 +7174,20 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
                 something silly in .opd with the assembler.  No .opd
                 optimization for them!  */
            broken_opd:
-             (*_bfd_error_handler)
-               (_("%B: .opd is not a regular array of opd entries"), ibfd);
-             need_edit = FALSE;
+             _bfd_error_handler
+               (_("%pB: .opd is not a regular array of opd entries"), ibfd);
+             broken = TRUE;
              break;
            }
 
          if ((r_type = ELF64_R_TYPE (rel->r_info)) != R_PPC64_ADDR64
              || (r_type = ELF64_R_TYPE ((rel + 1)->r_info)) != R_PPC64_TOC)
            {
-             (*_bfd_error_handler)
-               (_("%B: unexpected reloc type %u in .opd section"),
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: unexpected reloc type %u in .opd section"),
                 ibfd, r_type);
-             need_edit = FALSE;
+             broken = TRUE;
              break;
            }
 
@@ -7589,10 +7205,11 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
                sym_name = bfd_elf_sym_name (ibfd, symtab_hdr, sym,
                                             sym_sec);
 
-             (*_bfd_error_handler)
-               (_("%B: undefined sym `%s' in .opd section"),
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: undefined sym `%s' in .opd section"),
                 ibfd, sym_name);
-             need_edit = FALSE;
+             broken = TRUE;
              break;
            }
 
@@ -7608,63 +7225,54 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
            need_edit = TRUE;
 
          rel += 2;
-         if (rel == relend
-             || (rel + 1 == relend && rel->r_offset == offset + 16))
+         if (rel + 1 == relend
+             || (rel + 2 < relend
+                 && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
+           ++rel;
+
+         if (rel == relend)
            {
              if (sec->size == offset + 24)
                {
                  need_pad = NULL;
                  break;
                }
-             if (rel == relend && sec->size == offset + 16)
+             if (sec->size == offset + 16)
                {
                  cnt_16b++;
                  break;
                }
              goto broken_opd;
            }
-
-         if (rel->r_offset == offset + 24)
-           offset += 24;
-         else if (rel->r_offset != offset + 16)
-           goto broken_opd;
          else if (rel + 1 < relend
                   && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
                   && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
            {
-             offset += 16;
-             cnt_16b++;
-           }
-         else if (rel + 2 < relend
-                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
-                  && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
-           {
-             offset += 24;
-             rel += 1;
+             if (rel[0].r_offset == offset + 16)
+               cnt_16b++;
+             else if (rel[0].r_offset != offset + 24)
+               goto broken_opd;
            }
          else
            goto broken_opd;
        }
 
-      add_aux_fields = non_overlapping && cnt_16b > 0;
+      add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0;
 
-      if (need_edit || add_aux_fields)
+      if (!broken && (need_edit || add_aux_fields))
        {
          Elf_Internal_Rela *write_rel;
          Elf_Internal_Shdr *rel_hdr;
          bfd_byte *rptr, *wptr;
          bfd_byte *new_contents;
-         bfd_boolean skip;
-         long opd_ent_size;
          bfd_size_type amt;
 
          new_contents = NULL;
-         amt = sec->size * sizeof (long) / 8;
+         amt = OPD_NDX (sec->size) * sizeof (long);
          opd = &ppc64_elf_section_data (sec)->u.opd;
          opd->adjust = bfd_zalloc (sec->owner, amt);
          if (opd->adjust == NULL)
            return FALSE;
-         ppc64_elf_section_data (sec)->sec_type = sec_opd;
 
          /* This seems a waste of time as input .opd sections are all
             zeros as generated by gcc, but I suppose there's no reason
@@ -7675,11 +7283,9 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
              bfd_byte *loc;
              if (!bfd_malloc_and_get_section (ibfd, sec, &loc))
                {
-                 if (loc != NULL)
-                   free (loc);
+                 free (loc);
                error_ret:
-                 if (local_syms != NULL
-                     && symtab_hdr->contents != (unsigned char *) local_syms)
+                 if (symtab_hdr->contents != (unsigned char *) local_syms)
                    free (local_syms);
                  if (elf_section_data (sec)->relocs != relstart)
                    free (relstart);
@@ -7697,128 +7303,137 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
              new_contents = bfd_malloc (sec->size + cnt_16b * 8);
              if (new_contents == NULL)
                return FALSE;
-             need_pad = FALSE;
+             need_pad = NULL;
            }
          wptr = new_contents;
          rptr = sec->contents;
-
          write_rel = relstart;
-         skip = FALSE;
-         offset = 0;
-         opd_ent_size = 0;
-         for (rel = relstart; rel < relend; rel++)
+         for (rel = relstart; rel < relend; )
            {
              unsigned long r_symndx;
              asection *sym_sec;
              struct elf_link_hash_entry *h;
+             struct ppc_link_hash_entry *fdh = NULL;
              Elf_Internal_Sym *sym;
+             long opd_ent_size;
+             Elf_Internal_Rela *next_rel;
+             bfd_boolean skip;
 
              r_symndx = ELF64_R_SYM (rel->r_info);
              if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
                              r_symndx, ibfd))
                goto error_ret;
 
-             if (rel->r_offset == offset)
+             next_rel = rel + 2;
+             if (next_rel + 1 == relend
+                 || (next_rel + 2 < relend
+                     && ELF64_R_TYPE (next_rel[2].r_info) == R_PPC64_TOC))
+               ++next_rel;
+
+             /* See if the .opd entry is full 24 byte or
+                16 byte (with fd_aux entry overlapped with next
+                fd_func).  */
+             opd_ent_size = 24;
+             if (next_rel == relend)
                {
-                 struct ppc_link_hash_entry *fdh = NULL;
-
-                 /* See if the .opd entry is full 24 byte or
-                    16 byte (with fd_aux entry overlapped with next
-                    fd_func).  */
-                 opd_ent_size = 24;
-                 if ((rel + 2 == relend && sec->size == offset + 16)
-                     || (rel + 3 < relend
-                         && rel[2].r_offset == offset + 16
-                         && rel[3].r_offset == offset + 24
-                         && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
-                         && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+                 if (sec->size == rel->r_offset + 16)
                    opd_ent_size = 16;
+               }
+             else if (next_rel->r_offset == rel->r_offset + 16)
+               opd_ent_size = 16;
 
-                 if (h != NULL
-                     && h->root.root.string[0] == '.')
+             if (h != NULL
+                 && h->root.root.string[0] == '.')
+               {
+                 fdh = ppc_elf_hash_entry (h)->oh;
+                 if (fdh != NULL)
                    {
-                     struct ppc_link_hash_table *htab;
-
-                     htab = ppc_hash_table (info);
-                     if (htab != NULL)
-                       fdh = lookup_fdh ((struct ppc_link_hash_entry *) h,
-                                         htab);
-                     if (fdh != NULL
-                         && fdh->elf.root.type != bfd_link_hash_defined
+                     fdh = ppc_follow_link (fdh);
+                     if (fdh->elf.root.type != bfd_link_hash_defined
                          && fdh->elf.root.type != bfd_link_hash_defweak)
                        fdh = NULL;
                    }
+               }
 
-                 skip = (sym_sec->owner != ibfd
-                         || sym_sec->output_section == bfd_abs_section_ptr);
-                 if (skip)
+             skip = (sym_sec->owner != ibfd
+                     || sym_sec->output_section == bfd_abs_section_ptr);
+             if (skip)
+               {
+                 if (fdh != NULL && sym_sec->owner == ibfd)
                    {
-                     if (fdh != NULL && sym_sec->owner == ibfd)
-                       {
-                         /* Arrange for the function descriptor sym
-                            to be dropped.  */
-                         fdh->elf.root.u.def.value = 0;
-                         fdh->elf.root.u.def.section = sym_sec;
-                       }
-                     opd->adjust[rel->r_offset / 8] = -1;
+                     /* Arrange for the function descriptor sym
+                        to be dropped.  */
+                     fdh->elf.root.u.def.value = 0;
+                     fdh->elf.root.u.def.section = sym_sec;
                    }
-                 else
-                   {
-                     /* We'll be keeping this opd entry.  */
+                 opd->adjust[OPD_NDX (rel->r_offset)] = -1;
 
-                     if (fdh != NULL)
-                       {
-                         /* Redefine the function descriptor symbol to
-                            this location in the opd section.  It is
-                            necessary to update the value here rather
-                            than using an array of adjustments as we do
-                            for local symbols, because various places
-                            in the generic ELF code use the value
-                            stored in u.def.value.  */
-                         fdh->elf.root.u.def.value = wptr - new_contents;
-                         fdh->adjust_done = 1;
-                       }
+                 if (NO_OPD_RELOCS || bfd_link_relocatable (info))
+                   rel = next_rel;
+                 else
+                   while (1)
+                     {
+                       if (!dec_dynrel_count (rel->r_info, sec, info,
+                                              NULL, h, sym))
+                         goto error_ret;
 
-                     /* Local syms are a bit tricky.  We could
-                        tweak them as they can be cached, but
-                        we'd need to look through the local syms
-                        for the function descriptor sym which we
-                        don't have at the moment.  So keep an
-                        array of adjustments.  */
-                     opd->adjust[rel->r_offset / 8]
-                       = (wptr - new_contents) - (rptr - sec->contents);
-
-                     if (wptr != rptr)
-                       memcpy (wptr, rptr, opd_ent_size);
-                     wptr += opd_ent_size;
-                     if (add_aux_fields && opd_ent_size == 16)
-                       {
-                         memset (wptr, '\0', 8);
-                         wptr += 8;
-                       }
-                   }
-                 rptr += opd_ent_size;
-                 offset += opd_ent_size;
-               }
+                       if (++rel == next_rel)
+                         break;
 
-             if (skip)
-               {
-                 if (!NO_OPD_RELOCS
-                     && !info->relocatable
-                     && !dec_dynrel_count (rel->r_info, sec, info,
-                                           NULL, h, sym))
-                   goto error_ret;
+                       r_symndx = ELF64_R_SYM (rel->r_info);
+                       if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                                       r_symndx, ibfd))
+                         goto error_ret;
+                     }
                }
              else
                {
+                 /* We'll be keeping this opd entry.  */
+                 long adjust;
+
+                 if (fdh != NULL)
+                   {
+                     /* Redefine the function descriptor symbol to
+                        this location in the opd section.  It is
+                        necessary to update the value here rather
+                        than using an array of adjustments as we do
+                        for local symbols, because various places
+                        in the generic ELF code use the value
+                        stored in u.def.value.  */
+                     fdh->elf.root.u.def.value = wptr - new_contents;
+                     fdh->adjust_done = 1;
+                   }
+
+                 /* Local syms are a bit tricky.  We could
+                    tweak them as they can be cached, but
+                    we'd need to look through the local syms
+                    for the function descriptor sym which we
+                    don't have at the moment.  So keep an
+                    array of adjustments.  */
+                 adjust = (wptr - new_contents) - (rptr - sec->contents);
+                 opd->adjust[OPD_NDX (rel->r_offset)] = adjust;
+
+                 if (wptr != rptr)
+                   memcpy (wptr, rptr, opd_ent_size);
+                 wptr += opd_ent_size;
+                 if (add_aux_fields && opd_ent_size == 16)
+                   {
+                     memset (wptr, '\0', 8);
+                     wptr += 8;
+                   }
+
                  /* We need to adjust any reloc offsets to point to the
-                    new opd entries.  While we're at it, we may as well
-                    remove redundant relocs.  */
-                 rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8];
-                 if (write_rel != rel)
-                   memcpy (write_rel, rel, sizeof (*rel));
-                 ++write_rel;
+                    new opd entries.  */
+                 for ( ; rel != next_rel; ++rel)
+                   {
+                     rel->r_offset += adjust;
+                     if (write_rel != rel)
+                       memcpy (write_rel, rel, sizeof (*rel));
+                     ++write_rel;
+                   }
                }
+
+             rptr += opd_ent_size;
            }
 
          sec->size = wptr - new_contents;
@@ -7853,7 +7468,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
 
   /* If we are doing a final link and the last .opd entry is just 16 byte
      long, add a 8 byte padding after it.  */
-  if (need_pad != NULL && !info->relocatable)
+  if (need_pad != NULL && !bfd_link_relocatable (info))
     {
       bfd_byte *p;
 
@@ -7865,8 +7480,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
          if (p == NULL)
            return FALSE;
 
-         if (! bfd_get_section_contents (need_pad->owner, need_pad,
-                                         p, 0, need_pad->size))
+         if (!bfd_get_section_contents (need_pad->owner, need_pad,
+                                        p, 0, need_pad->size))
            return FALSE;
 
          need_pad->contents = p;
@@ -7888,14 +7503,164 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
   return TRUE;
 }
 
+/* Analyze inline PLT call relocations to see whether calls to locally
+   defined functions can be converted to direct calls.  */
+
+bfd_boolean
+ppc64_elf_inline_plt (struct bfd_link_info *info)
+{
+  struct ppc_link_hash_table *htab;
+  bfd *ibfd;
+  asection *sec;
+  bfd_vma low_vma, high_vma, limit;
+
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  /* A bl insn can reach -0x2000000 to 0x1fffffc.  The limit is
+     reduced somewhat to cater for possible stubs that might be added
+     between the call and its destination.  */
+  if (htab->params->group_size < 0)
+    {
+      limit = -htab->params->group_size;
+      if (limit == 1)
+       limit = 0x1e00000;
+    }
+  else
+    {
+      limit = htab->params->group_size;
+      if (limit == 1)
+       limit = 0x1c00000;
+    }
+
+  low_vma = -1;
+  high_vma = 0;
+  for (sec = info->output_bfd->sections; sec != NULL; sec = sec->next)
+    if ((sec->flags & (SEC_ALLOC | SEC_CODE)) == (SEC_ALLOC | SEC_CODE))
+      {
+       if (low_vma > sec->vma)
+         low_vma = sec->vma;
+       if (high_vma < sec->vma + sec->size)
+         high_vma = sec->vma + sec->size;
+      }
+
+  /* If a "bl" can reach anywhere in local code sections, then we can
+     convert all inline PLT sequences to direct calls when the symbol
+     is local.  */
+  if (high_vma - low_vma < limit)
+    {
+      htab->can_convert_all_inline_plt = 1;
+      return TRUE;
+    }
+
+  /* Otherwise, go looking through relocs for cases where a direct
+     call won't reach.  Mark the symbol on any such reloc to disable
+     the optimization and keep the PLT entry as it seems likely that
+     this will be better than creating trampolines.  Note that this
+     will disable the optimization for all inline PLT calls to a
+     particular symbol, not just those that won't reach.  The
+     difficulty in doing a more precise optimization is that the
+     linker needs to make a decision depending on whether a
+     particular R_PPC64_PLTCALL insn can be turned into a direct
+     call, for each of the R_PPC64_PLTSEQ and R_PPC64_PLT16* insns in
+     the sequence, and there is nothing that ties those relocs
+     together except their symbol.  */
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Sym *local_syms;
+
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
+      local_syms = NULL;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       if (ppc64_elf_section_data (sec)->has_pltcall
+           && !bfd_is_abs_section (sec->output_section))
+         {
+           Elf_Internal_Rela *relstart, *rel, *relend;
+
+           /* Read the relocations.  */
+           relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                                 info->keep_memory);
+           if (relstart == NULL)
+             return FALSE;
+
+           relend = relstart + sec->reloc_count;
+           for (rel = relstart; rel < relend; rel++)
+             {
+               enum elf_ppc64_reloc_type r_type;
+               unsigned long r_symndx;
+               asection *sym_sec;
+               struct elf_link_hash_entry *h;
+               Elf_Internal_Sym *sym;
+               unsigned char *tls_maskp;
+
+               r_type = ELF64_R_TYPE (rel->r_info);
+               if (r_type != R_PPC64_PLTCALL
+                   && r_type != R_PPC64_PLTCALL_NOTOC)
+                 continue;
+
+               r_symndx = ELF64_R_SYM (rel->r_info);
+               if (!get_sym_h (&h, &sym, &sym_sec, &tls_maskp, &local_syms,
+                               r_symndx, ibfd))
+                 {
+                   if (elf_section_data (sec)->relocs != relstart)
+                     free (relstart);
+                   if (symtab_hdr->contents != (bfd_byte *) local_syms)
+                     free (local_syms);
+                   return FALSE;
+                 }
+
+               if (sym_sec != NULL && sym_sec->output_section != NULL)
+                 {
+                   bfd_vma from, to;
+                   if (h != NULL)
+                     to = h->root.u.def.value;
+                   else
+                     to = sym->st_value;
+                   to += (rel->r_addend
+                          + sym_sec->output_offset
+                          + sym_sec->output_section->vma);
+                   from = (rel->r_offset
+                           + sec->output_offset
+                           + sec->output_section->vma);
+                   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;
+                 }
+             }
+           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;
+}
+
 /* Set htab->tls_get_addr and call the generic ELF tls_setup function.  */
 
 asection *
-ppc64_elf_tls_setup (struct bfd_link_info *info,
-                    int no_tls_get_addr_opt,
-                    int *no_multi_toc)
+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)
@@ -7904,23 +7669,57 @@ ppc64_elf_tls_setup (struct bfd_link_info *info,
   if (abiversion (info->output_bfd) == 1)
     htab->opd_abi = 1;
 
-  if (*no_multi_toc)
+  if (htab->params->no_multi_toc)
     htab->do_multi_toc = 0;
   else if (!htab->do_multi_toc)
-    *no_multi_toc = 1;
+    htab->params->no_multi_toc = 1;
+
+  /* Default to --no-plt-localentry, as this option can cause problems
+     with symbol interposition.  For example, glibc libpthread.so and
+     libc.so duplicate many pthread symbols, with a fallback
+     implementation in libc.so.  In some cases the fallback does more
+     work than the pthread implementation.  __pthread_condattr_destroy
+     is one such symbol: the libpthread.so implementation is
+     localentry:0 while the libc.so implementation is localentry:8.
+     An app that "cleverly" uses dlopen to only load necessary
+     libraries at runtime may omit loading libpthread.so when not
+     running multi-threaded, which then results in the libc.so
+     fallback symbols being used and ld.so complaining.  Now there
+     are workarounds in ld (see non_zero_localentry) to detect the
+     pthread situation, but that may not be the only case where
+     --plt-localentry can cause trouble.  */
+  if (htab->params->plt_localentry0 < 0)
+    htab->params->plt_localentry0 = 0;
+  if (htab->params->plt_localentry0
+      && elf_link_hash_lookup (&htab->elf, "GLIBC_2.26",
+                              FALSE, FALSE, FALSE) == NULL)
+    _bfd_error_handler
+      (_("warning: --plt-localentry is especially dangerous without "
+        "ld.so support to detect ABI violations"));
+
+  tga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
+                             FALSE, FALSE, TRUE);
+  htab->tls_get_addr = ppc_elf_hash_entry (tga);
 
-  htab->tls_get_addr = ((struct ppc_link_hash_entry *)
-                       elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
-                                             FALSE, FALSE, TRUE));
   /* Move dynamic linking info to the function descriptor sym.  */
-  if (htab->tls_get_addr != NULL)
-    func_desc_adjust (&htab->tls_get_addr->elf, info);
-  htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *)
-                          elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
-                                                FALSE, FALSE, TRUE));
-  if (!no_tls_get_addr_opt)
+  if (tga != NULL)
+    func_desc_adjust (tga, info);
+  tga_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
+                                FALSE, FALSE, TRUE);
+  htab->tls_get_addr_fd = ppc_elf_hash_entry (tga_fd);
+
+  desc = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_desc",
+                              FALSE, FALSE, TRUE);
+  htab->tga_desc = ppc_elf_hash_entry (desc);
+  if (desc != NULL)
+    func_desc_adjust (desc, info);
+  desc_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_desc",
+                                 FALSE, FALSE, TRUE);
+  htab->tga_desc_fd = ppc_elf_hash_entry (desc_fd);
+
+  if (htab->params->tls_get_addr_opt)
     {
-      struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd;
+      struct elf_link_hash_entry *opt, *opt_fd;
 
       opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt",
                                  FALSE, FALSE, TRUE);
@@ -7936,25 +7735,50 @@ ppc64_elf_tls_setup (struct bfd_link_info *info,
             signalled by the presence of __tls_get_addr_opt, and we'll
             be calling __tls_get_addr via a plt call stub, then
             make __tls_get_addr point to __tls_get_addr_opt.  */
-         tga_fd = &htab->tls_get_addr_fd->elf;
-         if (htab->elf.dynamic_sections_created
-             && tga_fd != NULL
-             && (tga_fd->type == STT_FUNC
-                 || tga_fd->needs_plt)
-             && !(SYMBOL_CALLS_LOCAL (info, tga_fd)
-                  || (ELF_ST_VISIBILITY (tga_fd->other) != STV_DEFAULT
-                      && tga_fd->root.type == bfd_link_hash_undefweak)))
-           {
-             struct plt_entry *ent;
-
-             for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next)
-               if (ent->plt.refcount > 0)
-                 break;
+         if (!(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)
                {
-                 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)
                    {
                      /* Use __tls_get_addr_opt in dynamic relocations.  */
@@ -7964,42 +7788,76 @@ ppc64_elf_tls_setup (struct bfd_link_info *info,
                      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);
-                     _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;
+                       }
                    }
                }
            }
        }
-      else
-       no_tls_get_addr_opt = TRUE;
+      else if (htab->params->tls_get_addr_opt < 0)
+       htab->params->tls_get_addr_opt = 0;
     }
-  htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
+
+  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
-   HASH1 or HASH2.  */
+   any of HASH1, HASH2, HASH3, or HASH4.  */
 
 static bfd_boolean
 branch_reloc_hash_match (const bfd *ibfd,
                         const Elf_Internal_Rela *rel,
                         const struct ppc_link_hash_entry *hash1,
-                        const struct ppc_link_hash_entry *hash2)
+                        const struct ppc_link_hash_entry *hash2,
+                        const struct ppc_link_hash_entry *hash3,
+                        const struct ppc_link_hash_entry *hash4)
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
   enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
@@ -8012,7 +7870,8 @@ branch_reloc_hash_match (const bfd *ibfd,
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       h = elf_follow_link (h);
-      if (h == &hash1->elf || h == &hash2->elf)
+      if (h == &hash1->elf || h == &hash2->elf
+         || h == &hash3->elf || h == &hash4->elf)
        return TRUE;
     }
   return FALSE;
@@ -8034,7 +7893,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
   unsigned char *toc_ref;
   int pass;
 
-  if (info->relocatable || !info->executable)
+  if (!bfd_link_executable (info))
     return TRUE;
 
   htab = ppc_hash_table (info);
@@ -8050,7 +7909,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
      and plt refcounts.  */
   toc_ref = NULL;
   for (pass = 0; pass < 2; ++pass)
-    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
       {
        Elf_Internal_Sym *locsyms = NULL;
        asection *toc = bfd_get_section_by_name (ibfd, ".toc");
@@ -8065,7 +7924,10 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
              relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
                                                    info->keep_memory);
              if (relstart == NULL)
-               return FALSE;
+               {
+                 free (toc_ref);
+                 return FALSE;
+               }
 
              relend = relstart + sec->reloc_count;
              for (rel = relstart; rel < relend; rel++)
@@ -8076,7 +7938,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  Elf_Internal_Sym *sym;
                  asection *sym_sec;
                  unsigned char *tls_mask;
-                 unsigned char tls_set, tls_clear, tls_type = 0;
+                 unsigned int tls_set, tls_clear, tls_type = 0;
                  bfd_vma value;
                  bfd_boolean ok_tprel, is_local;
                  long toc_ref_index = 0;
@@ -8090,11 +7952,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    err_free_rel:
                      if (elf_section_data (sec)->relocs != relstart)
                        free (relstart);
-                     if (toc_ref != NULL)
-                       free (toc_ref);
-                     if (locsyms != NULL
-                         && (elf_symtab_hdr (ibfd).contents
-                             != (unsigned char *) locsyms))
+                     free (toc_ref);
+                     if (elf_symtab_hdr (ibfd).contents
+                         != (unsigned char *) locsyms)
                        free (locsyms);
                      return ret;
                    }
@@ -8118,21 +7978,25 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    value = sym->st_value;
 
                  ok_tprel = FALSE;
-                 is_local = FALSE;
-                 if (h == NULL
-                     || !h->def_dynamic)
+                 is_local = SYMBOL_REFERENCES_LOCAL (info, h);
+                 if (is_local)
                    {
-                     is_local = TRUE;
                      if (h != NULL
                          && h->root.type == bfd_link_hash_undefweak)
                        ok_tprel = TRUE;
-                     else
+                     else if (sym_sec != NULL
+                              && sym_sec->output_section != NULL)
                        {
                          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;
                        }
                    }
 
@@ -8144,10 +8008,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                     setup insn.  If we don't find matching arg setup
                     relocs, don't do any tls optimization.  */
                  if (pass == 0
-                     && sec->has_tls_get_addr_call
+                     && sec->nomark_tls_get_addr
                      && h != NULL
-                     && (h == &htab->tls_get_addr->elf
-                         || h == &htab->tls_get_addr_fd->elf)
+                     && is_tls_get_addr (h, htab)
                      && !found_tls_get_addr_arg
                      && is_branch_reloc (r_type))
                    {
@@ -8163,9 +8026,10 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    {
                    case R_PPC64_GOT_TLSLD16:
                    case R_PPC64_GOT_TLSLD16_LO:
+                   case R_PPC64_GOT_TLSLD_PCREL34:
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
-                     /* Fall thru */
+                     /* Fall through.  */
 
                    case R_PPC64_GOT_TLSLD16_HI:
                    case R_PPC64_GOT_TLSLD16_HA:
@@ -8183,9 +8047,10 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
 
                    case R_PPC64_GOT_TLSGD16:
                    case R_PPC64_GOT_TLSGD16_LO:
+                   case R_PPC64_GOT_TLSGD_PCREL34:
                      expecting_tls_get_addr = 1;
                      found_tls_get_addr_arg = 1;
-                     /* Fall thru */
+                     /* Fall through. */
 
                    case R_PPC64_GOT_TLSGD16_HI:
                    case R_PPC64_GOT_TLSGD16_HA:
@@ -8194,11 +8059,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        tls_set = 0;
                      else
                        /* GD -> IE */
-                       tls_set = TLS_TLS | TLS_TPRELGD;
+                       tls_set = TLS_TLS | TLS_GDIE;
                      tls_clear = TLS_GD;
                      tls_type = TLS_TLS | TLS_GD;
                      break;
 
+                   case R_PPC64_GOT_TPREL_PCREL34:
                    case R_PPC64_GOT_TPREL16_DS:
                    case R_PPC64_GOT_TPREL16_LO_DS:
                    case R_PPC64_GOT_TPREL16_HI:
@@ -8213,10 +8079,43 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                      continue;
 
-                   case R_PPC64_TLSGD:
                    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)
+                                 != 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, ibfd))
+                               goto err_free_rel;
+                             if (h != NULL)
+                               {
+                                 struct plt_entry *ent = NULL;
+
+                                 for (ent = h->plt.plist;
+                                      ent != NULL;
+                                      ent = ent->next)
+                                   if (ent->addend == rel[1].r_addend)
+                                     break;
+
+                                 if (ent != NULL
+                                     && ent->plt.refcount > 0)
+                                   ent->plt.refcount -= 1;
+                               }
+                           }
+                         continue;
+                       }
                      found_tls_get_addr_arg = 1;
-                     /* Fall thru */
+                     /* Fall through.  */
 
                    case R_PPC64_TLS:
                    case R_PPC64_TOC16:
@@ -8229,7 +8128,8 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                         case of R_PPC64_TLS, and after checking for
                         tls_get_addr for the TOC16 relocs.  */
                      if (toc_ref == NULL)
-                       toc_ref = bfd_zmalloc (toc->output_section->rawsize / 8);
+                       toc_ref
+                         = bfd_zmalloc (toc->output_section->rawsize / 8);
                      if (toc_ref == NULL)
                        goto err_free_rel;
 
@@ -8238,7 +8138,10 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                      else
                        value = sym->st_value;
                      value += rel->r_addend;
-                     BFD_ASSERT (value < toc->size && value % 8 == 0);
+                     if (value % 8 != 0)
+                       continue;
+                     BFD_ASSERT (value < toc->size
+                                 && toc->output_offset % 8 == 0);
                      toc_ref_index = (value + toc->output_offset) / 8;
                      if (r_type == R_PPC64_TLS
                          || r_type == R_PPC64_TLSGD
@@ -8287,7 +8190,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                            tls_set = TLS_EXPLICIT | TLS_GD;
                          else
                            /* GD -> IE */
-                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_TPRELGD;
+                           tls_set = TLS_EXPLICIT | TLS_GD | TLS_GDIE;
                          tls_clear = TLS_GD;
                        }
                      else
@@ -8308,13 +8211,15 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                  if (pass == 0)
                    {
                      if (!expecting_tls_get_addr
-                         || !sec->has_tls_get_addr_call)
+                         || !sec->nomark_tls_get_addr)
                        continue;
 
                      if (rel + 1 < relend
                          && branch_reloc_hash_match (ibfd, rel + 1,
+                                                     htab->tls_get_addr_fd,
+                                                     htab->tga_desc_fd,
                                                      htab->tls_get_addr,
-                                                     htab->tls_get_addr_fd))
+                                                     htab->tga_desc))
                        {
                          if (expecting_tls_get_addr == 2)
                            {
@@ -8329,7 +8234,8 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                                goto err_free_rel;
                              if (toc_tls != NULL)
                                {
-                                 if ((*toc_tls & (TLS_GD | TLS_LD)) != 0)
+                                 if ((*toc_tls & TLS_TLS) != 0
+                                     && ((*toc_tls & (TLS_GD | TLS_LD)) != 0))
                                    found_tls_get_addr_arg = 1;
                                  if (retval > 1)
                                    toc_ref[toc_ref_index] = 1;
@@ -8338,13 +8244,11 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                          continue;
                        }
 
-                     if (expecting_tls_get_addr != 1)
-                       continue;
-
                      /* Uh oh, we didn't find the expected call.  We
                         could just mark this symbol to exclude it
                         from tls optimization but it's safer to skip
                         the entire optimization.  */
+                     /* xgettext:c-format */
                      info->callbacks->minfo (_("%H arg lost __tls_get_addr, "
                                                "TLS optimization disabled\n"),
                                              ibfd, sec, rel->r_offset);
@@ -8352,35 +8256,55 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                      goto err_free_rel;
                    }
 
-                 if (expecting_tls_get_addr && htab->tls_get_addr != NULL)
+                 /* If we don't have old-style __tls_get_addr calls
+                    without TLSGD/TLSLD marker relocs, and we haven't
+                    found a new-style __tls_get_addr call with a
+                    marker for this symbol, then we either have a
+                    broken object file or an -mlongcall style
+                    indirect call to __tls_get_addr without a marker.
+                    Disable optimization in this case.  */
+                 if ((tls_clear & (TLS_GD | TLS_LD)) != 0
+                     && (tls_set & TLS_EXPLICIT) == 0
+                     && !sec->nomark_tls_get_addr
+                     && ((*tls_mask & (TLS_TLS | TLS_MARK))
+                         != (TLS_TLS | TLS_MARK)))
+                   continue;
+
+                 if (expecting_tls_get_addr == 1 + !sec->nomark_tls_get_addr)
                    {
-                     struct plt_entry *ent;
-                     for (ent = htab->tls_get_addr->elf.plt.plist;
-                          ent != NULL;
-                          ent = ent->next)
-                       if (ent->addend == 0)
-                         {
-                           if (ent->plt.refcount > 0)
-                             {
-                               ent->plt.refcount -= 1;
-                               expecting_tls_get_addr = 0;
-                             }
+                     struct plt_entry *ent = 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 (expecting_tls_get_addr && htab->tls_get_addr_fd != NULL)
-                   {
-                     struct plt_entry *ent;
-                     for (ent = htab->tls_get_addr_fd->elf.plt.plist;
-                          ent != NULL;
-                          ent = ent->next)
-                       if (ent->addend == 0)
-                         {
-                           if (ent->plt.refcount > 0)
-                             ent->plt.refcount -= 1;
+                     if (ent == NULL && htab->tga_desc_fd != NULL)
+                       for (ent = htab->tga_desc_fd->elf.plt.plist;
+                            ent != NULL;
+                            ent = ent->next)
+                         if (ent->addend == 0)
                            break;
-                         }
+
+                     if (ent == NULL && htab->tls_get_addr != NULL)
+                       for (ent = htab->tls_get_addr->elf.plt.plist;
+                            ent != NULL;
+                            ent = ent->next)
+                         if (ent->addend == 0)
+                           break;
+
+                     if (ent == NULL && htab->tga_desc != NULL)
+                       for (ent = htab->tga_desc->elf.plt.plist;
+                            ent != NULL;
+                            ent = ent->next)
+                         if (ent->addend == 0)
+                           break;
+
+                     if (ent != NULL
+                         && ent->plt.refcount > 0)
+                       ent->plt.refcount -= 1;
                    }
 
                  if (tls_clear == 0)
@@ -8427,7 +8351,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                        }
                    }
 
-                 *tls_mask |= tls_set;
+                 *tls_mask |= tls_set & 0xff;
                  *tls_mask &= ~tls_clear;
                }
 
@@ -8445,8 +8369,8 @@ 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;
 }
 
@@ -8454,7 +8378,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
    the values of any global symbols in a toc section that has been
    edited.  Globals in toc sections should be a rarity, so this function
    sets a flag if any are found in toc sections other than the one just
-   edited, so that futher hash table traversals can be avoided.  */
+   edited, so that further hash table traversals can be avoided.  */
 
 struct adjust_toc_info
 {
@@ -8476,7 +8400,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
       && h->root.type != bfd_link_hash_defweak)
     return TRUE;
 
-  eh = (struct ppc_link_hash_entry *) h;
+  eh = ppc_elf_hash_entry (h);
   if (eh->adjust_done)
     return TRUE;
 
@@ -8489,7 +8413,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 
       if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0)
        {
-         (*_bfd_error_handler)
+         _bfd_error_handler
            (_("%s defined on removed toc entry"), eh->elf.root.root.string);
          do
            ++i;
@@ -8506,37 +8430,185 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
-/* Return TRUE iff INSN is one we expect on a _LO variety toc/got reloc.  */
+/* Return TRUE iff INSN with a relocation of R_TYPE is one we expect
+   on a _LO variety toc/got reloc.  */
 
 static bfd_boolean
-ok_lo_toc_insn (unsigned int insn)
-{
-  return ((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)) == 58u << 26 /* lwa,ld,lmd */
-             && (insn & 3) != 1)
-         || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
-             && ((insn & 3) == 0 || (insn & 3) == 3))
-         || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type)
+{
+  return ((insn & (0x3fu << 26)) == 12u << 26 /* addic */
+         || (insn & (0x3fu << 26)) == 14u << 26 /* addi */
+         || (insn & (0x3fu << 26)) == 32u << 26 /* lwz */
+         || (insn & (0x3fu << 26)) == 34u << 26 /* lbz */
+         || (insn & (0x3fu << 26)) == 36u << 26 /* stw */
+         || (insn & (0x3fu << 26)) == 38u << 26 /* stb */
+         || (insn & (0x3fu << 26)) == 40u << 26 /* lhz */
+         || (insn & (0x3fu << 26)) == 42u << 26 /* lha */
+         || (insn & (0x3fu << 26)) == 44u << 26 /* sth */
+         || (insn & (0x3fu << 26)) == 46u << 26 /* lmw */
+         || (insn & (0x3fu << 26)) == 47u << 26 /* stmw */
+         || (insn & (0x3fu << 26)) == 48u << 26 /* lfs */
+         || (insn & (0x3fu << 26)) == 50u << 26 /* lfd */
+         || (insn & (0x3fu << 26)) == 52u << 26 /* stfs */
+         || (insn & (0x3fu << 26)) == 54u << 26 /* stfd */
+         || (insn & (0x3fu << 26)) == 56u << 26 /* lq,lfq */
+         || ((insn & (0x3fu << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+             /* Exclude lfqu by testing reloc.  If relocs are ever
+                defined for the reduced D field in psq_lu then those
+                will need testing too.  */
+             && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+         || ((insn & (0x3fu << 26)) == 58u << 26 /* ld,lwa */
+             && (insn & 1) == 0)
+         || (insn & (0x3fu << 26)) == 60u << 26 /* stfq */
+         || ((insn & (0x3fu << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+             /* Exclude stfqu.  psq_stu as above for psq_lu.  */
+             && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+         || ((insn & (0x3fu << 26)) == 62u << 26 /* std,stq */
+             && (insn & 1) == 0));
 }
 
-/* Examine all relocs referencing .toc sections in order to remove
-   unused .toc entries.  */
+/* 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.  */
 
-bfd_boolean
-ppc64_elf_edit_toc (struct bfd_link_info *info)
+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.  */
+
+bfd_boolean
+ppc64_elf_edit_toc (struct bfd_link_info *info)
 {
   bfd *ibfd;
   struct adjust_toc_info toc_inf;
@@ -8544,7 +8616,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
   htab->do_toc_opt = 1;
   toc_inf.global_toc_syms = TRUE;
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       asection *toc, *sec;
       Elf_Internal_Shdr *symtab_hdr;
@@ -8684,10 +8756,11 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                goto error_ret;
 
              if (sym_sec == NULL
+                 || sym_sec->output_section == NULL
                  || discarded_section (sym_sec))
                continue;
 
-             if (!SYMBOL_CALLS_LOCAL (info, h))
+             if (!SYMBOL_REFERENCES_LOCAL (info, h))
                continue;
 
              if (h != NULL)
@@ -8736,18 +8809,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       if (used == NULL)
        {
        error_ret:
-         if (local_syms != NULL
-             && symtab_hdr->contents != (unsigned char *) local_syms)
+         if (symtab_hdr->contents != (unsigned char *) local_syms)
            free (local_syms);
          if (sec != NULL
-             && relstart != NULL
              && elf_section_data (sec)->relocs != relstart)
            free (relstart);
-         if (toc_relocs != NULL
-             && elf_section_data (toc)->relocs != toc_relocs)
+         if (elf_section_data (toc)->relocs != toc_relocs)
            free (toc_relocs);
-         if (skip != NULL)
-           free (skip);
+         free (skip);
          return FALSE;
        }
 
@@ -8773,7 +8842,10 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
          relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
                                                info->keep_memory);
          if (relstart == NULL)
-           goto error_ret;
+           {
+             free (used);
+             goto error_ret;
+           }
 
          /* Mark toc entries referenced as used.  */
          do
@@ -8787,64 +8859,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  struct elf_link_hash_entry *h;
                  Elf_Internal_Sym *sym;
                  bfd_vma val;
-                 enum {no_check, check_lo, check_ha} insn_check;
 
                  r_type = ELF64_R_TYPE (rel->r_info);
-                 switch (r_type)
-                   {
-                   default:
-                     insn_check = no_check;
-                     break;
-
-                   case R_PPC64_GOT_TLSLD16_HA:
-                   case R_PPC64_GOT_TLSGD16_HA:
-                   case R_PPC64_GOT_TPREL16_HA:
-                   case R_PPC64_GOT_DTPREL16_HA:
-                   case R_PPC64_GOT16_HA:
-                   case R_PPC64_TOC16_HA:
-                     insn_check = check_ha;
-                     break;
-
-                   case R_PPC64_GOT_TLSLD16_LO:
-                   case R_PPC64_GOT_TLSGD16_LO:
-                   case R_PPC64_GOT_TPREL16_LO_DS:
-                   case R_PPC64_GOT_DTPREL16_LO_DS:
-                   case R_PPC64_GOT16_LO:
-                   case R_PPC64_GOT16_LO_DS:
-                   case R_PPC64_TOC16_LO:
-                   case R_PPC64_TOC16_LO_DS:
-                     insn_check = check_lo;
-                     break;
-                   }
-
-                 if (insn_check != no_check)
-                   {
-                     bfd_vma off = rel->r_offset & ~3;
-                     unsigned char buf[4];
-                     unsigned int insn;
-
-                     if (!bfd_get_section_contents (ibfd, sec, buf, off, 4))
-                       {
-                         free (used);
-                         goto error_ret;
-                       }
-                     insn = bfd_get_32 (ibfd, buf);
-                     if (insn_check == check_lo
-                         ? !ok_lo_toc_insn (insn)
-                         : ((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
-                           (_("%P: %H: toc optimization is not supported for"
-                              " %s instruction.\n"),
-                            ibfd, sec, rel->r_offset & ~3, str);
-                       }
-                   }
-
                  switch (r_type)
                    {
                    case R_PPC64_TOC16:
@@ -8902,7 +8918,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                            }
                          if ((opc & (0x3f << 2)) == (58u << 2))
                            break;
-                         /* Fall thru */
+                         /* Fall through.  */
 
                        default:
                          /* Wrong sort of reloc, or not a ld.  We may
@@ -8934,7 +8950,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
       /* Merge the used and skip arrays.  Assume that TOC
         doublewords not appearing as either used or unused belong
-        to to an entry more than one doubleword in size.  */
+        to an entry more than one doubleword in size.  */
       for (drop = skip, keep = used, last = 0, some_unused = 0;
           drop < skip + (toc->size + 7) / 8;
           ++drop, ++keep)
@@ -9067,7 +9083,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                          if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
                            ppc_howto_init ();
                          info->callbacks->einfo
-                           (_("%P: %H: %s references "
+                           /* xgettext:c-format */
+                           (_("%H: %s references "
                               "optimized away TOC entry\n"),
                             ibfd, sec, rel->r_offset,
                             ppc64_elf_howto_table[r_type]->name);
@@ -9109,7 +9126,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  if ((skip[i] & (ref_from_discarded | can_optimize)) != 0)
                    {
                      if (local_toc_syms)
-                       (*_bfd_error_handler)
+                       _bfd_error_handler
                          (_("%s defined on removed toc entry"),
                           bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
                      do
@@ -9166,8 +9183,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              rel_hdr->sh_size = toc->reloc_count * sz;
            }
        }
-      else if (toc_relocs != NULL
-              && elf_section_data (toc)->relocs != toc_relocs)
+      else if (elf_section_data (toc)->relocs != toc_relocs)
        free (toc_relocs);
 
       if (local_syms != NULL
@@ -9181,6 +9197,245 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       free (skip);
     }
 
+  /* 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;
+
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
+      if (!ppc64_elf_tdata (ibfd)->has_optrel)
+       continue;
+
+      sec = ppc64_elf_tdata (ibfd)->got;
+      got = 0;
+      if (sec != NULL)
+       got = sec->output_section->vma + sec->output_offset + 0x8000;
+
+      local_syms = NULL;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+
+      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;
+
+         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;
 }
 
@@ -9202,8 +9457,7 @@ allocate_got (struct elf_link_hash_entry *h,
              struct got_entry *gent)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  bfd_boolean dyn;
-  struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
+  struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h);
   int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)
                 ? 16 : 8);
   int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD
@@ -9213,16 +9467,19 @@ allocate_got (struct elf_link_hash_entry *h,
   gent->got.offset = got->size;
   got->size += entsize;
 
-  dyn = htab->elf.dynamic_sections_created;
   if (h->type == STT_GNU_IFUNC)
     {
       htab->elf.irelplt->size += rentsize;
       htab->got_reli_size += rentsize;
     }
-  else if ((info->shared
-           || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
-          && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-              || h->root.type != bfd_link_hash_undefweak))
+  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;
@@ -9249,6 +9506,25 @@ merge_got_entries (struct got_entry **pent)
          }
 }
 
+/* If H is undefined, make it dynamic if that makes sense.  */
+
+static bfd_boolean
+ensure_undef_dynamic (struct bfd_link_info *info,
+                     struct elf_link_hash_entry *h)
+{
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+
+  if (htab->dynamic_sections_created
+      && ((info->dynamic_undefined_weak != 0
+          && h->root.type == bfd_link_hash_undefweak)
+         || h->root.type == bfd_link_hash_undefined)
+      && h->dynindx == -1
+      && !h->forced_local
+      && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+    return bfd_elf_link_record_dynamic_symbol (info, h);
+  return TRUE;
+}
+
 /* Allocate space in .plt, .got and associated reloc sections for
    dynamic relocs.  */
 
@@ -9259,7 +9535,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct ppc_link_hash_table *htab;
   asection *s;
   struct ppc_link_hash_entry *eh;
-  struct elf_dyn_relocs *p;
   struct got_entry **pgent, *gent;
 
   if (h->root.type == bfd_link_hash_indirect)
@@ -9270,90 +9545,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (htab == NULL)
     return FALSE;
 
-  if ((htab->elf.dynamic_sections_created
-       && h->dynindx != -1
-       && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h))
-      || h->type == STT_GNU_IFUNC)
-    {
-      struct plt_entry *pent;
-      bfd_boolean doneone = FALSE;
-      for (pent = h->plt.plist; pent != NULL; pent = pent->next)
-       if (pent->plt.refcount > 0)
-         {
-           if (!htab->elf.dynamic_sections_created
-               || h->dynindx == -1)
+  eh = ppc_elf_hash_entry (h);
+  /* Run through the TLS GD got entries first if we're changing them
+     to TPREL.  */
+  if ((eh->tls_mask & (TLS_TLS | TLS_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)
+       {
+         /* This was a GD entry that has been converted to TPREL.  If
+            there happens to be a TPREL entry we can use that one.  */
+         struct got_entry *ent;
+         for (ent = h->got.glist; ent != NULL; ent = ent->next)
+           if (ent->got.refcount > 0
+               && (ent->tls_type & TLS_TPREL) != 0
+               && ent->addend == gent->addend
+               && ent->owner == gent->owner)
              {
-               s = htab->elf.iplt;
-               pent->plt.offset = s->size;
-               s->size += PLT_ENTRY_SIZE (htab);
-               s = htab->elf.irelplt;
-             }
-           else
-             {
-               /* If this is the first .plt entry, make room for the special
-                  first entry.  */
-               s = htab->elf.splt;
-               if (s->size == 0)
-                 s->size += PLT_INITIAL_ENTRY_SIZE (htab);
-
-               pent->plt.offset = s->size;
-
-               /* Make room for this entry.  */
-               s->size += PLT_ENTRY_SIZE (htab);
-
-               /* Make room for the .glink code.  */
-               s = htab->glink;
-               if (s->size == 0)
-                 s->size += GLINK_CALL_STUB_SIZE;
-               if (htab->opd_abi)
-                 {
-                   /* We need bigger stubs past index 32767.  */
-                   if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
-                     s->size += 4;
-                   s->size += 2*4;
-                 }
-               else
-                 s->size += 4;
-
-               /* We also need to make an entry in the .rela.plt section.  */
-               s = htab->elf.srelplt;
-             }
-           s->size += sizeof (Elf64_External_Rela);
-           doneone = TRUE;
-         }
-       else
-         pent->plt.offset = (bfd_vma) -1;
-      if (!doneone)
-       {
-         h->plt.plist = NULL;
-         h->needs_plt = 0;
-       }
-    }
-  else
-    {
-      h->plt.plist = NULL;
-      h->needs_plt = 0;
-    }
-
-  eh = (struct ppc_link_hash_entry *) h;
-  /* Run through the TLS GD got entries first if we're changing them
-     to TPREL.  */
-  if ((eh->tls_mask & TLS_TPRELGD) != 0)
-    for (gent = h->got.glist; gent != NULL; gent = gent->next)
-      if (gent->got.refcount > 0
-         && (gent->tls_type & TLS_GD) != 0)
-       {
-         /* This was a GD entry that has been converted to TPREL.  If
-            there happens to be a TPREL entry we can use that one.  */
-         struct got_entry *ent;
-         for (ent = h->got.glist; ent != NULL; ent = ent->next)
-           if (ent->got.refcount > 0
-               && (ent->tls_type & TLS_TPREL) != 0
-               && ent->addend == gent->addend
-               && ent->owner == gent->owner)
-             {
-               gent->got.refcount = 0;
-               break;
+               gent->got.refcount = 0;
+               break;
              }
 
          /* If not, then we'll be using our own TPREL entry.  */
@@ -9369,7 +9579,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     if (gent->got.refcount > 0)
       {
        if ((gent->tls_type & TLS_LD) != 0
-           && !h->def_dynamic)
+           && SYMBOL_REFERENCES_LOCAL (info, h))
          {
            ppc64_tlsld_got (gent->owner)->got.refcount += 1;
            *pgent = gent->next;
@@ -9386,17 +9596,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   for (gent = h->got.glist; gent != NULL; gent = gent->next)
     if (!gent->is_indirect)
       {
-       /* Make sure this symbol is output as a dynamic symbol.
-          Undefined weak syms won't yet be marked as dynamic,
-          nor will all TLS symbols.  */
-       if (h->dynindx == -1
-           && !h->forced_local
-           && h->type != STT_GNU_IFUNC
-           && htab->elf.dynamic_sections_created)
-         {
-           if (! bfd_elf_link_record_dynamic_symbol (info, h))
-             return FALSE;
-         }
+       /* Ensure we catch all the cases where this symbol should
+          be made dynamic.  */
+       if (!ensure_undef_dynamic (info, h))
+         return FALSE;
 
        if (!is_ppc64_elf (gent->owner))
          abort ();
@@ -9404,105 +9607,189 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        allocate_got (h, info, gent);
       }
 
-  if (eh->dyn_relocs == NULL
-      || (!htab->elf.dynamic_sections_created
-         && (h->type != STT_GNU_IFUNC
-             || !htab->opd_abi)))
-    return TRUE;
+  /* If no dynamic sections we can't have dynamic relocs, except for
+     IFUNCs which are handled even in static executables.  */
+  if (!htab->elf.dynamic_sections_created
+      && h->type != STT_GNU_IFUNC)
+    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)
+    h->dyn_relocs = NULL;
 
-  /* In the shared -Bsymbolic case, discard space allocated for
-     dynamic pc-relative relocs against symbols which turn out to be
-     defined in regular objects.  For the normal shared case, discard
-     space for relocs that have become local due to symbol visibility
-     changes.  */
+  /* 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))
+    h->dyn_relocs = NULL;
 
-  if (info->shared)
+  if (h->dyn_relocs != NULL)
     {
-      /* Relocs that use pc_count are those that appear on a call insn,
-        or certain REL relocs (see must_be_dyn_reloc) that can be
-        generated via assembly.  We want calls to protected symbols to
-        resolve directly to the function rather than going via the plt.
-        If people want function pointer comparisons to work as expected
-        then they should avoid writing weird assembly.  */
-      if (SYMBOL_CALLS_LOCAL (info, h))
+      struct elf_dyn_relocs *p, **pp;
+
+      /* In the shared -Bsymbolic case, discard space allocated for
+        dynamic pc-relative relocs against symbols which turn out to
+        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))
        {
-         struct elf_dyn_relocs **pp;
-
-         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+         /* Relocs that use pc_count are those that appear on a call
+            insn, or certain REL relocs (see must_be_dyn_reloc) that
+            can be generated via assembly.  We want calls to
+            protected symbols to resolve directly to the function
+            rather than going via the plt.  If people want function
+            pointer comparisons to work as expected then they should
+            avoid writing weird assembly.  */
+         if (SYMBOL_CALLS_LOCAL (info, h))
            {
-             p->count -= p->pc_count;
-             p->pc_count = 0;
-             if (p->count == 0)
-               *pp = p->next;
-             else
-               pp = &p->next;
+             for (pp = &h->dyn_relocs; (p = *pp) != NULL; )
+               {
+                 p->count -= p->pc_count;
+                 p->pc_count = 0;
+                 if (p->count == 0)
+                   *pp = p->next;
+                 else
+                   pp = &p->next;
+               }
            }
-       }
-
-      /* Also discard relocs on undefined weak syms with non-default
-        visibility.  */
-      if (eh->dyn_relocs != NULL
-         && h->root.type == bfd_link_hash_undefweak)
-       {
-         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-           eh->dyn_relocs = NULL;
 
-         /* Make sure this symbol is output as a dynamic symbol.
-            Undefined weak syms won't yet be marked as dynamic.  */
-         else if (h->dynindx == -1
-                  && !h->forced_local)
+         if (h->dyn_relocs != NULL)
            {
-             if (! bfd_elf_link_record_dynamic_symbol (info, h))
+             /* Ensure we catch all the cases where this symbol
+                should be made dynamic.  */
+             if (!ensure_undef_dynamic (info, h))
                return FALSE;
            }
        }
-    }
-  else if (h->type == STT_GNU_IFUNC)
-    {
-      if (!h->non_got_ref)
-       eh->dyn_relocs = NULL;
-    }
-  else if (ELIMINATE_COPY_RELOCS)
-    {
-      /* For the non-shared case, discard space for relocs against
-        symbols which turn out to need copy relocs or are not
-        dynamic.  */
 
-      if (!h->non_got_ref
-         && !h->def_regular)
+      /* For a fixed position executable, discard space for
+        relocs against symbols which are not dynamic.  */
+      else if (h->type != STT_GNU_IFUNC)
        {
-         /* Make sure this symbol is output as a dynamic symbol.
-            Undefined weak syms won't yet be marked as dynamic.  */
-         if (h->dynindx == -1
-             && !h->forced_local)
+         if (h->dynamic_adjusted
+             && !h->def_regular
+             && !ELF_COMMON_DEF_P (h))
            {
-             if (! bfd_elf_link_record_dynamic_symbol (info, h))
+             /* Ensure we catch all the cases where this symbol
+                should be made dynamic.  */
+             if (!ensure_undef_dynamic (info, h))
                return FALSE;
+
+             /* But if that didn't work out, discard dynamic relocs.  */
+             if (h->dynindx == -1)
+               h->dyn_relocs = NULL;
            }
+         else
+           h->dyn_relocs = NULL;
+       }
 
-         /* If that succeeded, we know we'll be keeping all the
-            relocs.  */
-         if (h->dynindx != -1)
-           goto keep;
+      /* Finally, allocate space.  */
+      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)
+           sreloc = htab->elf.irelplt;
+         sreloc->size += p->count * sizeof (Elf64_External_Rela);
        }
+    }
+
+  /* We might need a PLT entry when the symbol
+     a) is dynamic, or
+     b) is an ifunc, or
+     c) has plt16 relocs and has been processed by adjust_dynamic_symbol, or
+     d) has plt16 relocs and we are linking statically.  */
+  if ((htab->elf.dynamic_sections_created && h->dynindx != -1)
+      || h->type == STT_GNU_IFUNC
+      || (h->needs_plt && h->dynamic_adjusted)
+      || (h->needs_plt
+         && h->def_regular
+         && !htab->elf.dynamic_sections_created
+         && !htab->can_convert_all_inline_plt
+         && (ppc_elf_hash_entry (h)->tls_mask
+             & (TLS_TLS | PLT_KEEP)) == PLT_KEEP))
+    {
+      struct plt_entry *pent;
+      bfd_boolean doneone = FALSE;
+      for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+       if (pent->plt.refcount > 0)
+         {
+           if (!htab->elf.dynamic_sections_created
+               || h->dynindx == -1)
+             {
+               if (h->type == STT_GNU_IFUNC)
+                 {
+                   s = htab->elf.iplt;
+                   pent->plt.offset = s->size;
+                   s->size += PLT_ENTRY_SIZE (htab);
+                   s = htab->elf.irelplt;
+                 }
+               else
+                 {
+                   s = htab->pltlocal;
+                   pent->plt.offset = s->size;
+                   s->size += LOCAL_PLT_ENTRY_SIZE (htab);
+                   s = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+                 }
+             }
+           else
+             {
+               /* If this is the first .plt entry, make room for the special
+                  first entry.  */
+               s = htab->elf.splt;
+               if (s->size == 0)
+                 s->size += PLT_INITIAL_ENTRY_SIZE (htab);
 
-      eh->dyn_relocs = NULL;
+               pent->plt.offset = s->size;
 
-    keep: ;
-    }
+               /* Make room for this entry.  */
+               s->size += PLT_ENTRY_SIZE (htab);
 
-  /* Finally, allocate space.  */
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+               /* Make room for the .glink code.  */
+               s = htab->glink;
+               if (s->size == 0)
+                 s->size += GLINK_PLTRESOLVE_SIZE (htab);
+               if (htab->opd_abi)
+                 {
+                   /* We need bigger stubs past index 32767.  */
+                   if (s->size >= GLINK_PLTRESOLVE_SIZE (htab) + 32768*2*4)
+                     s->size += 4;
+                   s->size += 2*4;
+                 }
+               else
+                 s->size += 4;
+
+               /* We also need to make an entry in the .rela.plt section.  */
+               s = htab->elf.srelplt;
+             }
+           if (s != NULL)
+             s->size += sizeof (Elf64_External_Rela);
+           doneone = TRUE;
+         }
+       else
+         pent->plt.offset = (bfd_vma) -1;
+      if (!doneone)
+       {
+         h->plt.plist = NULL;
+         h->needs_plt = 0;
+       }
+    }
+  else
     {
-      asection *sreloc = elf_section_data (p->sec)->sreloc;
-      if (eh->elf.type == STT_GNU_IFUNC)
-       sreloc = htab->elf.irelplt;
-      sreloc->size += p->count * sizeof (Elf64_External_Rela);
+      h->plt.plist = NULL;
+      h->needs_plt = 0;
     }
 
   return TRUE;
 }
 
+#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,
    after the branch table.  */
@@ -9513,7 +9800,7 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
   struct plt_entry *pent;
-  asection *s;
+  asection *s, *plt;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -9529,37 +9816,54 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
   if (htab == NULL)
     return FALSE;
 
-  s = htab->glink;
+  s = htab->global_entry;
+  plt = htab->elf.splt;
   for (pent = h->plt.plist; pent != NULL; pent = pent->next)
     if (pent->plt.offset != (bfd_vma) -1
        && pent->addend == 0)
       {
-       s->size = (s->size + 15) & -16;
-       s->size += 16;
+       /* For ELFv2, if this symbol is not defined in a regular file
+          and we are not generating a shared library or pie, then we
+          need to define the symbol in the executable on a call stub.
+          This is to avoid text relocations.  */
+       bfd_vma off, stub_align, stub_off, stub_size;
+       unsigned int align_power;
+
+       stub_size = 16;
+       stub_off = s->size;
+       if (htab->params->plt_stub_align >= 0)
+         align_power = htab->params->plt_stub_align;
+       else
+         align_power = -htab->params->plt_stub_align;
+       /* Setting section alignment is delayed until we know it is
+          non-empty.  Otherwise the .text output section will be
+          aligned at least to plt_stub_align even when no global
+          entry stubs are needed.  */
+       if (s->alignment_power < align_power)
+         s->alignment_power = align_power;
+       stub_align = (bfd_vma) 1 << align_power;
+       if (htab->params->plt_stub_align >= 0
+           || ((((stub_off + stub_size - 1) & -stub_align)
+                - (stub_off & -stub_align))
+               > ((stub_size - 1) & -stub_align)))
+         stub_off = (stub_off + stub_align - 1) & -stub_align;
+       off = pent->plt.offset + plt->output_offset + plt->output_section->vma;
+       off -= stub_off + s->output_offset + s->output_section->vma;
+       /* Note that for --plt-stub-align negative we have a possible
+          dependency between stub offset and size.  Break that
+          dependency by assuming the max stub size when calculating
+          the stub offset.  */
+       if (PPC_HA (off) == 0)
+         stub_size -= 4;
+       h->root.type = bfd_link_hash_defined;
+       h->root.u.def.section = s;
+       h->root.u.def.value = stub_off;
+       s->size = stub_off + stub_size;
        break;
       }
   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 *info)
-{
-  if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
-
-  if (readonly_dynrelocs (h))
-    {
-      ((struct bfd_link_info *) info)->flags |= DF_TEXTREL;
-
-      /* Not an error, just cut short the traversal.  */
-      return FALSE;
-    }
-  return TRUE;
-}
-
 /* Set the sizes of the dynamic sections.  */
 
 static bfd_boolean
@@ -9584,7 +9888,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
   if (htab->elf.dynamic_sections_created)
     {
       /* Set the contents of the .interp section to the interpreter.  */
-      if (info->executable)
+      if (bfd_link_executable (info) && !info->nointerp)
        {
          s = bfd_get_linker_section (dynobj, ".interp");
          if (s == NULL)
@@ -9596,7 +9900,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
 
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       struct got_entry **lgot_ents;
       struct got_entry **end_lgot_ents;
@@ -9671,12 +9975,14 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
                        rel_size *= 2;
                      }
                    s->size += ent_size;
-                   if ((*lgot_masks & PLT_IFUNC) != 0)
+                   if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
                      {
                        htab->elf.irelplt->size += rel_size;
                        htab->got_reli_size += rel_size;
                      }
-                   else if (info->shared)
+                   else if (bfd_link_pic (info)
+                            && !(ent->tls_type != 0
+                                 && bfd_link_executable (info)))
                      {
                        asection *srel = ppc64_elf_tdata (ibfd)->relgot;
                        srel->size += rel_size;
@@ -9688,19 +9994,33 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
              *pent = ent->next;
        }
 
-      /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt.  */
-      for (; local_plt < end_local_plt; ++local_plt)
+      /* Allocate space for plt calls to local syms.  */
+      lgot_masks = (unsigned char *) end_local_plt;
+      for (; local_plt < end_local_plt; ++local_plt, ++lgot_masks)
        {
          struct plt_entry *ent;
 
          for (ent = *local_plt; ent != NULL; ent = ent->next)
            if (ent->plt.refcount > 0)
              {
-               s = htab->elf.iplt;
-               ent->plt.offset = s->size;
-               s->size += PLT_ENTRY_SIZE (htab);
-
-               htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+               if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
+                 {
+                   s = htab->elf.iplt;
+                   ent->plt.offset = s->size;
+                   s->size += PLT_ENTRY_SIZE (htab);
+                   htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+                 }
+               else if (htab->can_convert_all_inline_plt
+                        || (*lgot_masks & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)
+                 ent->plt.offset = (bfd_vma) -1;
+               else
+                 {
+                   s = htab->pltlocal;
+                   ent->plt.offset = s->size;
+                   s->size += LOCAL_PLT_ENTRY_SIZE (htab);
+                   if (bfd_link_pic (info))
+                     htab->relpltlocal->size += sizeof (Elf64_External_Rela);
+                 }
              }
            else
              ent->plt.offset = (bfd_vma) -1;
@@ -9710,15 +10030,12 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
   /* Allocate global sym .plt and .got entries, and space for global
      sym dynamic relocs.  */
   elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
-  /* Stash the end of glink branch table.  */
-  if (htab->glink != NULL)
-    htab->glink->rawsize = htab->glink->size;
 
-  if (!htab->opd_abi && !info->shared)
+  if (!htab->opd_abi && !bfd_link_pic (info))
     elf_link_hash_traverse (&htab->elf, size_global_entry_stubs, info);
 
   first_tlsld = NULL;
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       struct got_entry *ent;
 
@@ -9741,7 +10058,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
              ent->got.offset = s->size;
              ent->owner = ibfd;
              s->size += 16;
-             if (info->shared)
+             if (bfd_link_dll (info))
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
                  srel->size += sizeof (Elf64_External_Rela);
@@ -9766,8 +10083,11 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
       else if (s == htab->elf.sgot
               || s == htab->elf.splt
               || s == htab->elf.iplt
+              || s == htab->pltlocal
               || s == htab->glink
-              || s == htab->dynbss)
+              || s == htab->global_entry
+              || s == htab->elf.sdynbss
+              || s == htab->elf.sdynrelro)
        {
          /* Strip this section if we don't need it; see the
             comment below.  */
@@ -9811,6 +10131,10 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
          continue;
        }
 
+      if (bfd_is_abs_section (s->output_section))
+       _bfd_error_handler (_("warning: discarding dynamic section %s"),
+                           s->name);
+
       if ((s->flags & SEC_HAS_CONTENTS) == 0)
        continue;
 
@@ -9820,13 +10144,13 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
         but this way if it does we get a R_PPC64_NONE reloc in .rela
         sections instead of garbage.
         We also rely on the section contents being zero when writing
-        the GOT.  */
+        the GOT and .dynrelro.  */
       s->contents = bfd_zalloc (dynobj, s->size);
       if (s->contents == NULL)
        return FALSE;
     }
 
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       if (!is_ppc64_elf (ibfd))
        continue;
@@ -9871,7 +10195,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
 #define add_dynamic_entry(TAG, VAL) \
   _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (info->executable)
+      if (bfd_link_executable (info))
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -9894,9 +10218,11 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
            return FALSE;
        }
 
-      tls_opt = (!htab->no_tls_get_addr_opt
-                && htab->tls_get_addr_fd != NULL
-                && htab->tls_get_addr_fd->elf.plt.plist != NULL);
+      tls_opt = (htab->params->tls_get_addr_opt
+                && ((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))
@@ -9913,7 +10239,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
          /* If any dynamic relocs apply to a read-only section,
             then we need a DT_TEXTREL entry.  */
          if ((info->flags & DF_TEXTREL) == 0)
-           elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info);
+           elf_link_hash_traverse (&htab->elf,
+                                   _bfd_elf_maybe_set_textrel, info);
 
          if ((info->flags & DF_TEXTREL) != 0)
            {
@@ -10014,7 +10341,9 @@ ppc_type_of_stub (asection *input_sec,
 
   /* Determine if a long branch stub is needed.  */
   max_branch_offset = 1 << 25;
-  if (r_type != R_PPC64_REL24)
+  if (r_type == R_PPC64_REL14
+      || r_type == R_PPC64_REL14_BRTAKEN
+      || r_type == R_PPC64_REL14_BRNTAKEN)
     max_branch_offset = 1 << 15;
 
   if (branch_offset + max_branch_offset >= 2 * max_branch_offset - local_off)
@@ -10025,75 +10354,497 @@ ppc_type_of_stub (asection *input_sec,
   return ppc_stub_none;
 }
 
-/* With power7 weakly ordered memory model, it is possible for ld.so
-   to update a plt entry in one thread and have another thread see a
-   stale zero toc entry.  To avoid this we need some sort of acquire
-   barrier in the call stub.  One solution is to make the load of the
-   toc word seem to appear to depend on the load of the function entry
-   word.  Another solution is to test for r2 being zero, and branch to
-   the appropriate glink entry if so.
-
-   .   fake dep barrier        compare
-   .   ld 12,xxx(2)            ld 12,xxx(2)
-   .   mtctr 12                mtctr 12
-   .   xor 11,12,12            ld 2,xxx+8(2)
-   .   add 2,2,11              cmpldi 2,0
-   .   ld 2,xxx+8(2)           bnectr+
-   .   bctr                    b <glink_entry>
-
-   The solution involving the compare turns out to be faster, so
-   that's what we use unless the branch won't reach.  */
-
-#define ALWAYS_USE_FAKE_DEP 0
-#define ALWAYS_EMIT_R2SAVE 0
-
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+/* Gets the address of a label (1:) in r11 and builds an offset in r12,
+   then adds it to r11 (LOAD false) or loads r12 from r11+r12 (LOAD true).
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .1: mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,%r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@high
+   .   ori     %r12,%r12,xxx-1b@l
+   .   add/ldx %r12,%r11,%r12  */
 
-static inline unsigned int
-plt_stub_size (struct ppc_link_hash_table *htab,
-              struct ppc_stub_hash_entry *stub_entry,
-              bfd_vma off)
+static bfd_byte *
+build_offset (bfd *abfd, bfd_byte *p, bfd_vma off, bfd_boolean load)
 {
-  unsigned size = 12;
-
-  if (ALWAYS_EMIT_R2SAVE
-      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-    size += 4;
-  if (PPC_HA (off) != 0)
-    size += 4;
-  if (htab->opd_abi)
+  bfd_put_32 (abfd, MFLR_R12, p);
+  p += 4;
+  bfd_put_32 (abfd, BCL_20_31, p);
+  p += 4;
+  bfd_put_32 (abfd, MFLR_R11, p);
+  p += 4;
+  bfd_put_32 (abfd, MTLR_R12, p);
+  p += 4;
+  if (off + 0x8000 < 0x10000)
     {
-      size += 4;
-      if (htab->plt_static_chain)
-       size += 4;
-      if (htab->plt_thread_safe)
-       size += 8;
-      if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
-       size += 4;
+      if (load)
+       bfd_put_32 (abfd, LD_R12_0R11 + PPC_LO (off), p);
+      else
+       bfd_put_32 (abfd, ADDI_R12_R11 + PPC_LO (off), p);
+      p += 4;
     }
-  if (stub_entry->h != NULL
-      && (stub_entry->h == htab->tls_get_addr_fd
-         || stub_entry->h == htab->tls_get_addr)
-      && !htab->no_tls_get_addr_opt)
-    size += 13 * 4;
-  return size;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      bfd_put_32 (abfd, ADDIS_R12_R11 + PPC_HA (off), p);
+      p += 4;
+      if (load)
+       bfd_put_32 (abfd, LD_R12_0R12 + PPC_LO (off), p);
+      else
+       bfd_put_32 (abfd, ADDI_R12_R12 + PPC_LO (off), p);
+      p += 4;
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       {
+         bfd_put_32 (abfd, LI_R12_0 + ((off >> 32) & 0xffff), p);
+         p += 4;
+       }
+      else
+       {
+         bfd_put_32 (abfd, LIS_R12 + ((off >> 48) & 0xffff), p);
+         p += 4;
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             bfd_put_32 (abfd, ORI_R12_R12_0 + ((off >> 32) & 0xffff), p);
+             p += 4;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       {
+         bfd_put_32 (abfd, SLDI_R12_R12_32, p);
+         p += 4;
+       }
+      if (PPC_HI (off) != 0)
+       {
+         bfd_put_32 (abfd, ORIS_R12_R12_0 + PPC_HI (off), p);
+         p += 4;
+       }
+      if (PPC_LO (off) != 0)
+       {
+         bfd_put_32 (abfd, ORI_R12_R12_0 + PPC_LO (off), 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;
 }
 
-/* If this stub would cross fewer 2**plt_stub_align boundaries if we align,
-   then return the padding needed to do so.  */
-static inline unsigned int
-plt_stub_pad (struct ppc_link_hash_table *htab,
-             struct ppc_stub_hash_entry *stub_entry,
-             bfd_vma plt_off)
+static unsigned int
+size_offset (bfd_vma off)
 {
-  int stub_align = 1 << htab->plt_stub_align;
-  unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
-  bfd_vma stub_off = stub_entry->stub_sec->size;
-
-  if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
-      > (stub_size & -stub_align))
+  unsigned int size;
+  if (off + 0x8000 < 0x10000)
+    size = 4;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    size = 8;
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       size = 4;
+      else
+       {
+         size = 4;
+         if (((off >> 32) & 0xffff) != 0)
+           size += 4;
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       size += 4;
+      if (PPC_HI (off) != 0)
+       size += 4;
+      if (PPC_LO (off) != 0)
+       size += 4;
+      size += 4;
+    }
+  return size + 16;
+}
+
+static unsigned int
+num_relocs_for_offset (bfd_vma off)
+{
+  unsigned int num_rel;
+  if (off + 0x8000 < 0x10000)
+    num_rel = 1;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    num_rel = 2;
+  else
+    {
+      num_rel = 1;
+      if (off + 0x800000000000ULL >= 0x1000000000000ULL
+         && ((off >> 32) & 0xffff) != 0)
+       num_rel += 1;
+      if (PPC_HI (off) != 0)
+       num_rel += 1;
+      if (PPC_LO (off) != 0)
+       num_rel += 1;
+    }
+  return num_rel;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
+                       bfd_vma roff, bfd_vma targ, bfd_vma off)
+{
+  bfd_vma relative_targ = targ - (roff - 8);
+  if (bfd_big_endian (info->output_bfd))
+    roff += 2;
+  r->r_offset = roff;
+  r->r_addend = relative_targ + roff;
+  if (off + 0x8000 < 0x10000)
+    r->r_info = ELF64_R_INFO (0, R_PPC64_REL16);
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HA);
+      ++r;
+      roff += 4;
+      r->r_offset = roff;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+      r->r_addend = relative_targ + roff;
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+      else
+       {
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHEST);
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             ++r;
+             roff += 4;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+             r->r_addend = relative_targ + roff;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       roff += 4;
+      if (PPC_HI (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGH);
+         r->r_addend = relative_targ + roff;
+       }
+      if (PPC_LO (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+         r->r_addend = relative_targ + roff;
+       }
+    }
+  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 *
+eh_advance (bfd *abfd, bfd_byte *eh, unsigned int delta)
+{
+  delta /= 4;
+  if (delta < 64)
+    *eh++ = DW_CFA_advance_loc + delta;
+  else if (delta < 256)
+    {
+      *eh++ = DW_CFA_advance_loc1;
+      *eh++ = delta;
+    }
+  else if (delta < 65536)
+    {
+      *eh++ = DW_CFA_advance_loc2;
+      bfd_put_16 (abfd, delta, eh);
+      eh += 2;
+    }
+  else
+    {
+      *eh++ = DW_CFA_advance_loc4;
+      bfd_put_32 (abfd, delta, eh);
+      eh += 4;
+    }
+  return eh;
+}
+
+/* Size of required .eh_frame opcode to advance pc by DELTA.  */
+
+static unsigned int
+eh_advance_size (unsigned int delta)
+{
+  if (delta < 64 * 4)
+    /* DW_CFA_advance_loc+[1..63].  */
+    return 1;
+  if (delta < 256 * 4)
+    /* DW_CFA_advance_loc1, byte.  */
+    return 2;
+  if (delta < 65536 * 4)
+    /* DW_CFA_advance_loc2, 2 bytes.  */
+    return 3;
+  /* DW_CFA_advance_loc4, 4 bytes.  */
+  return 5;
+}
+
+/* With power7 weakly ordered memory model, it is possible for ld.so
+   to update a plt entry in one thread and have another thread see a
+   stale zero toc entry.  To avoid this we need some sort of acquire
+   barrier in the call stub.  One solution is to make the load of the
+   toc word seem to appear to depend on the load of the function entry
+   word.  Another solution is to test for r2 being zero, and branch to
+   the appropriate glink entry if so.
+
+   .   fake dep barrier        compare
+   .   ld 12,xxx(2)            ld 12,xxx(2)
+   .   mtctr 12                mtctr 12
+   .   xor 11,12,12            ld 2,xxx+8(2)
+   .   add 2,2,11              cmpldi 2,0
+   .   ld 2,xxx+8(2)           bnectr+
+   .   bctr                    b <glink_entry>
+
+   The solution involving the compare turns out to be faster, so
+   that's what we use unless the branch won't reach.  */
+
+#define ALWAYS_USE_FAKE_DEP 0
+#define ALWAYS_EMIT_R2SAVE 0
+
+static inline unsigned int
+plt_stub_size (struct ppc_link_hash_table *htab,
+              struct ppc_stub_hash_entry *stub_entry,
+              bfd_vma off)
+{
+  unsigned size;
+
+  if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+    {
+      if (htab->power10_stubs)
+       {
+         bfd_vma start = (stub_entry->stub_offset
+                          + stub_entry->group->stub_sec->output_offset
+                          + stub_entry->group->stub_sec->output_section->vma);
+         if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
+           start += 4;
+         size = 8 + size_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;
+    }
+
+  size = 12;
+  if (ALWAYS_EMIT_R2SAVE
+      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+    size += 4;
+  if (PPC_HA (off) != 0)
+    size += 4;
+  if (htab->opd_abi)
+    {
+      size += 4;
+      if (htab->params->plt_static_chain)
+       size += 4;
+      if (htab->params->plt_thread_safe
+         && htab->elf.dynamic_sections_created
+         && stub_entry->h != NULL
+         && stub_entry->h->elf.dynindx != -1)
+       size += 8;
+      if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off))
+       size += 4;
+    }
+  if (stub_entry->h != NULL
+      && is_tls_get_addr (&stub_entry->h->elf, htab)
+      && htab->params->tls_get_addr_opt)
+    {
+      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;
+}
+
+/* Depending on the sign of plt_stub_align:
+   If positive, return the padding to align to a 2**plt_stub_align
+   boundary.
+   If negative, if this stub would cross fewer 2**plt_stub_align
+   boundaries if we align, then return the padding needed to do so.  */
+
+static inline unsigned int
+plt_stub_pad (struct ppc_link_hash_table *htab,
+             struct ppc_stub_hash_entry *stub_entry,
+             bfd_vma plt_off)
+{
+  int stub_align;
+  unsigned stub_size;
+  bfd_vma stub_off = stub_entry->group->stub_sec->size;
+
+  if (htab->params->plt_stub_align >= 0)
+    {
+      stub_align = 1 << htab->params->plt_stub_align;
+      if ((stub_off & (stub_align - 1)) != 0)
+       return stub_align - (stub_off & (stub_align - 1));
+      return 0;
+    }
+
+  stub_align = 1 << -htab->params->plt_stub_align;
+  stub_size = plt_stub_size (htab, stub_entry, plt_off);
+  if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
+      > ((stub_size - 1) & -stub_align))
     return stub_align - (stub_off & (stub_align - 1));
   return 0;
 }
@@ -10105,25 +10856,26 @@ build_plt_stub (struct ppc_link_hash_table *htab,
                struct ppc_stub_hash_entry *stub_entry,
                bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
 {
-  bfd *obfd = htab->stub_bfd;
+  bfd *obfd = htab->params->stub_bfd;
   bfd_boolean plt_load_toc = htab->opd_abi;
-  bfd_boolean plt_static_chain = htab->plt_static_chain;
-  bfd_boolean plt_thread_safe = htab->plt_thread_safe;
+  bfd_boolean plt_static_chain = htab->params->plt_static_chain;
+  bfd_boolean plt_thread_safe = (htab->params->plt_thread_safe
+                                && htab->elf.dynamic_sections_created
+                                && stub_entry->h != NULL
+                                && stub_entry->h->elf.dynindx != -1);
   bfd_boolean use_fake_dep = plt_thread_safe;
   bfd_vma cmp_branch_off = 0;
 
   if (!ALWAYS_USE_FAKE_DEP
       && plt_load_toc
       && plt_thread_safe
-      && !(stub_entry->h != NULL
-          && (stub_entry->h == htab->tls_get_addr_fd
-              || stub_entry->h == htab->tls_get_addr)
-          && !htab->no_tls_get_addr_opt))
+      && !(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;
       bfd_vma pltindex = ((pltoff - PLT_INITIAL_ENTRY_SIZE (htab))
                          / PLT_ENTRY_SIZE (htab));
-      bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
+      bfd_vma glinkoff = GLINK_PLTRESOLVE_SIZE (htab) + pltindex * 8;
       bfd_vma to, from;
 
       if (pltindex > 32768)
@@ -10131,7 +10883,7 @@ build_plt_stub (struct ppc_link_hash_table *htab,
       to = (glinkoff
            + htab->glink->output_offset
            + htab->glink->output_section->vma);
-      from = (p - stub_entry->stub_sec->contents
+      from = (p - stub_entry->group->stub_sec->contents
              + 4 * (ALWAYS_EMIT_R2SAVE
                     || stub_entry->stub_type == ppc_stub_plt_call_r2save)
              + 4 * (PPC_HA (offset) != 0)
@@ -10139,8 +10891,8 @@ build_plt_stub (struct ppc_link_hash_table *htab,
                     != PPC_HA (offset))
              + 4 * (plt_static_chain != 0)
              + 20
-             + stub_entry->stub_sec->output_offset
-             + stub_entry->stub_sec->output_section->vma);
+             + stub_entry->group->stub_sec->output_offset
+             + stub_entry->group->stub_sec->output_section->vma);
       cmp_branch_off = to - from;
       use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
     }
@@ -10181,8 +10933,16 @@ build_plt_stub (struct ppc_link_hash_table *htab,
       if (ALWAYS_EMIT_R2SAVE
          || stub_entry->stub_type == ppc_stub_plt_call_r2save)
        bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p),      p += 4;
-      bfd_put_32 (obfd, ADDIS_R11_R2 | PPC_HA (offset), p),    p += 4;
-      bfd_put_32 (obfd, LD_R12_0R11 | PPC_LO (offset), p),     p += 4;
+      if (plt_load_toc)
+       {
+         bfd_put_32 (obfd, ADDIS_R11_R2 | PPC_HA (offset), p), p += 4;
+         bfd_put_32 (obfd, LD_R12_0R11 | PPC_LO (offset), p),  p += 4;
+       }
+      else
+       {
+         bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
+         bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (offset), p),  p += 4;
+       }
       if (plt_load_toc
          && PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
        {
@@ -10268,45 +11028,141 @@ build_plt_stub (struct ppc_link_hash_table *htab,
 
 /* Build a special .plt call stub for __tls_get_addr.  */
 
-#define LD_R11_0R3     0xe9630000
+#define LD_R0_0R3      0xe8030000
 #define LD_R12_0R3     0xe9830000
 #define MR_R0_R3       0x7c601b78
-#define CMPDI_R11_0    0x2c2b0000
+#define CMPDI_R0_0     0x2c200000
 #define ADD_R3_R12_R13 0x7c6c6a14
 #define BEQLR          0x4d820020
 #define MR_R3_R0       0x7c030378
-#define STD_R11_0R1    0xf9610000
 #define BCTRL          0x4e800421
-#define LD_R11_0R1     0xe9610000
-#define MTLR_R11       0x7d6803a6
 
 static inline bfd_byte *
 build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
                         struct ppc_stub_hash_entry *stub_entry,
                         bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
 {
-  bfd *obfd = htab->stub_bfd;
+  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, CMPDI_R0_0, p),            p += 4;
   bfd_put_32 (obfd, MR_R0_R3, p),              p += 4;
-  bfd_put_32 (obfd, CMPDI_R11_0, p),           p += 4;
   bfd_put_32 (obfd, ADD_R3_R12_R13, p),                p += 4;
   bfd_put_32 (obfd, BEQLR, p),                 p += 4;
   bfd_put_32 (obfd, MR_R3_R0, p),              p += 4;
-  bfd_put_32 (obfd, MFLR_R11, p),              p += 4;
-  bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4;
+  if (htab->params->no_tls_get_addr_regsave)
+    {
+      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_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);
+
+      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;
 
-  if (r != NULL)
-    r[0].r_offset += 9 * 4;
-  p = build_plt_stub (htab, stub_entry, p, offset, r);
-  bfd_put_32 (obfd, BCTRL, p - 4);
+      p = build_plt_stub (htab, stub_entry, p, offset, r);
+      bfd_put_32 (obfd, BCTRL, p - 4);
 
-  bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p),        p += 4;
-  bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p),    p += 4;
-  bfd_put_32 (obfd, MTLR_R11, p),              p += 4;
-  bfd_put_32 (obfd, BLR, p),                   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;
+
+      base = htab->glink_eh_frame->contents + stub_entry->group->eh_base + 17;
+      eh = base + stub_entry->group->eh_size;
+      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;
+    }
   return p;
 }
 
@@ -10340,12 +11196,69 @@ get_relocs (asection *sec, int count)
   return relocs;
 }
 
+/* Convert the relocs R[0] thru R[-NUM_REL+1], which are all no-symbol
+   forms, to the equivalent relocs against the global symbol given by
+   STUB_ENTRY->H.  */
+
+static bfd_boolean
+use_global_in_relocs (struct ppc_link_hash_table *htab,
+                     struct ppc_stub_hash_entry *stub_entry,
+                     Elf_Internal_Rela *r, unsigned int num_rel)
+{
+  struct elf_link_hash_entry **hashes;
+  unsigned long symndx;
+  struct ppc_link_hash_entry *h;
+  bfd_vma symval;
+
+  /* Relocs are always against symbols in their own object file.  Fake
+     up global sym hashes for the stub bfd (which has no symbols).  */
+  hashes = elf_sym_hashes (htab->params->stub_bfd);
+  if (hashes == NULL)
+    {
+      bfd_size_type hsize;
+
+      /* When called the first time, stub_globals will contain the
+        total number of symbols seen during stub sizing.  After
+        allocating, stub_globals is used as an index to fill the
+        hashes array.  */
+      hsize = (htab->stub_globals + 1) * sizeof (*hashes);
+      hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
+      if (hashes == NULL)
+       return FALSE;
+      elf_sym_hashes (htab->params->stub_bfd) = hashes;
+      htab->stub_globals = 1;
+    }
+  symndx = htab->stub_globals++;
+  h = stub_entry->h;
+  hashes[symndx] = &h->elf;
+  if (h->oh != NULL && h->oh->is_func)
+    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 = defined_sym_val (&h->elf);
+  while (num_rel-- != 0)
+    {
+      r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info));
+      if (h->elf.root.u.def.section != stub_entry->target_section)
+       {
+         /* H is an opd symbol.  The addend must be zero, and the
+            branch reloc is the only one we can convert.  */
+         r->r_addend = 0;
+         break;
+       }
+      else
+       r->r_addend -= symval;
+      --r;
+    }
+  return TRUE;
+}
+
 static bfd_vma
 get_r2off (struct bfd_link_info *info,
           struct ppc_stub_hash_entry *stub_entry)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  bfd_vma r2off = htab->stub_group[stub_entry->target_section->id].toc_off;
+  bfd_vma r2off = htab->sec_info[stub_entry->target_section->id].toc_off;
 
   if (r2off == 0)
     {
@@ -10360,17 +11273,18 @@ get_r2off (struct bfd_link_info *info,
       if (strcmp (opd->name, ".opd") != 0
          || opd->reloc_count != 0)
        {
-         info->callbacks->einfo (_("%P: cannot find opd entry toc for `%T'\n"),
-                                 stub_entry->h->elf.root.root.string);
+         info->callbacks->einfo
+           (_("%P: cannot find opd entry toc for `%pT'\n"),
+            stub_entry->h->elf.root.root.string);
          bfd_set_error (bfd_error_bad_value);
-         return 0;
+         return (bfd_vma) -1;
        }
       if (!bfd_get_section_contents (opd->owner, opd, buf, opd_off + 8, 8))
-       return 0;
+       return (bfd_vma) -1;
       r2off = bfd_get_64 (opd->owner, buf);
       r2off -= elf_gp (info->output_bfd);
     }
-  r2off -= htab->stub_group[stub_entry->id_sec->id].toc_off;
+  r2off -= htab->sec_info[stub_entry->group->link_sec->id].toc_off;
   return r2off;
 }
 
@@ -10382,23 +11296,42 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
   bfd_byte *loc;
-  bfd_byte *p;
-  bfd_vma dest, off;
-  int size;
+  bfd_byte *p, *relp;
+  bfd_vma targ, off;
   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;
 
+  /* 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;
 
-  /* Make a note of the offset within the stubs for this entry.  */
-  stub_entry->stub_offset = stub_entry->stub_sec->size;
-  loc = stub_entry->stub_sec->contents + stub_entry->stub_offset;
+  BFD_ASSERT (stub_entry->stub_offset >= stub_entry->group->stub_sec->size);
+  loc = stub_entry->group->stub_sec->contents + stub_entry->stub_offset;
 
   htab->stub_count[stub_entry->stub_type - 1] += 1;
   switch (stub_entry->stub_type)
@@ -10406,46 +11339,50 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
     case ppc_stub_long_branch:
     case ppc_stub_long_branch_r2off:
       /* Branches are relative.  This is where we are going to.  */
-      dest = (stub_entry->target_value
+      targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
-      dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
-      off = dest;
+      targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
 
       /* And this is where we are coming from.  */
-      off -= (stub_entry->stub_offset
-             + stub_entry->stub_sec->output_offset
-             + stub_entry->stub_sec->output_section->vma);
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
+      off = targ - off;
 
-      size = 4;
+      p = loc;
       if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
        {
          bfd_vma r2off = get_r2off (info, stub_entry);
 
-         if (r2off == 0)
+         if (r2off == (bfd_vma) -1)
            {
              htab->stub_error = TRUE;
              return FALSE;
            }
-         bfd_put_32 (htab->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
-         loc += 4;
-         size = 12;
+         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
          if (PPC_HA (r2off) != 0)
            {
-             size = 16;
-             bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
-             loc += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+             p += 4;
+           }
+         if (PPC_LO (r2off) != 0)
+           {
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDI_R2_R2 | PPC_LO (r2off), p);
+             p += 4;
            }
-         bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
-         loc += 4;
-         off -= size - 4;
+         off -= p - loc;
        }
-      bfd_put_32 (htab->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
+      bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p);
+      p += 4;
 
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
        {
-         info->callbacks->einfo
-           (_("%P: long branch stub `%s' offset overflow\n"),
+         _bfd_error_handler
+           (_("long branch stub `%s' offset overflow"),
             stub_entry->root.string);
          htab->stub_error = TRUE;
          return FALSE;
@@ -10453,47 +11390,15 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
        {
-         r = get_relocs (stub_entry->stub_sec, 1);
+         r = get_relocs (stub_entry->group->stub_sec, 1);
          if (r == NULL)
            return FALSE;
-         r->r_offset = loc - stub_entry->stub_sec->contents;
+         r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
          r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
-         r->r_addend = dest;
-         if (stub_entry->h != NULL)
-           {
-             struct elf_link_hash_entry **hashes;
-             unsigned long symndx;
-             struct ppc_link_hash_entry *h;
-
-             hashes = elf_sym_hashes (htab->stub_bfd);
-             if (hashes == NULL)
-               {
-                 bfd_size_type hsize;
-
-                 hsize = (htab->stub_globals + 1) * sizeof (*hashes);
-                 hashes = bfd_zalloc (htab->stub_bfd, hsize);
-                 if (hashes == NULL)
-                   return FALSE;
-                 elf_sym_hashes (htab->stub_bfd) = hashes;
-                 htab->stub_globals = 1;
-               }
-             symndx = htab->stub_globals++;
-             h = stub_entry->h;
-             hashes[symndx] = &h->elf;
-             r->r_info = ELF64_R_INFO (symndx, R_PPC64_REL24);
-             if (h->oh != NULL && h->oh->is_func)
-               h = ppc_follow_link (h->oh);
-             if (h->elf.root.u.def.section != stub_entry->target_section)
-               /* H is an opd symbol.  The addend must be zero.  */
-               r->r_addend = 0;
-             else
-               {
-                 off = (h->elf.root.u.def.value
-                        + h->elf.root.u.def.section->output_offset
-                        + h->elf.root.u.def.section->output_section->vma);
-                 r->r_addend -= off;
-               }
-           }
+         r->r_addend = targ;
+         if (stub_entry->h != NULL
+             && !use_global_in_relocs (htab, stub_entry, r, 1))
+           return FALSE;
        }
       break;
 
@@ -10504,19 +11409,19 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                                         FALSE, FALSE);
       if (br_entry == NULL)
        {
-         info->callbacks->einfo (_("%P: can't find branch stub `%s'\n"),
-                                 stub_entry->root.string);
+         _bfd_error_handler (_("can't find branch stub `%s'"),
+                             stub_entry->root.string);
          htab->stub_error = TRUE;
          return FALSE;
        }
 
-      dest = (stub_entry->target_value
+      targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
       if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
-       dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+       targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
 
-      bfd_put_64 (htab->brlt->owner, dest,
+      bfd_put_64 (htab->brlt->owner, targ,
                  htab->brlt->contents + br_entry->offset);
 
       if (br_entry->iter == htab->stub_iteration)
@@ -10533,7 +11438,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                               + htab->brlt->output_offset
                               + htab->brlt->output_section->vma);
              rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-             rela.r_addend = dest;
+             rela.r_addend = targ;
 
              rl = htab->relbrlt->contents;
              rl += (htab->relbrlt->reloc_count++
@@ -10553,22 +11458,22 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                             + htab->brlt->output_offset
                             + htab->brlt->output_section->vma);
              r->r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-             r->r_addend = dest;
+             r->r_addend = targ;
            }
        }
 
-      dest = (br_entry->offset
+      targ = (br_entry->offset
              + htab->brlt->output_offset
              + htab->brlt->output_section->vma);
 
-      off = (dest
-            - elf_gp (htab->brlt->output_section->owner)
-            - htab->stub_group[stub_entry->id_sec->id].toc_off);
+      off = (elf_gp (info->output_bfd)
+            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = targ - off;
 
       if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
        {
          info->callbacks->einfo
-           (_("%P: linkage table error against `%T'\n"),
+           (_("%P: linkage table error against `%pT'\n"),
             stub_entry->root.string);
          bfd_set_error (bfd_error_bad_value);
          htab->stub_error = TRUE;
@@ -10577,17 +11482,16 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
        {
-         r = get_relocs (stub_entry->stub_sec, 1 + (PPC_HA (off) != 0));
+         r = get_relocs (stub_entry->group->stub_sec, 1 + (PPC_HA (off) != 0));
          if (r == NULL)
            return FALSE;
-         r[0].r_offset = loc - stub_entry->stub_sec->contents;
+         r[0].r_offset = loc - stub_entry->group->stub_sec->contents;
          if (bfd_big_endian (info->output_bfd))
            r[0].r_offset += 2;
-         if (stub_entry->stub_type == ppc_stub_plt_branch_r2off
-             && htab->opd_abi)
+         if (stub_entry->stub_type == ppc_stub_plt_branch_r2off)
            r[0].r_offset += 4;
          r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
-         r[0].r_addend = dest;
+         r[0].r_addend = targ;
          if (PPC_HA (off) != 0)
            {
              r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA);
@@ -10597,61 +11501,202 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            }
        }
 
-      if (stub_entry->stub_type != ppc_stub_plt_branch_r2off
-         || !htab->opd_abi)
+      p = loc;
+      if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
        {
          if (PPC_HA (off) != 0)
            {
-             size = 16;
-             bfd_put_32 (htab->stub_bfd, ADDIS_R11_R2 | PPC_HA (off), loc);
-             loc += 4;
-             bfd_put_32 (htab->stub_bfd, LD_R12_0R11 | PPC_LO (off), loc);
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDIS_R12_R2 | PPC_HA (off), p);
+             p += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         LD_R12_0R12 | PPC_LO (off), p);
            }
          else
-           {
-             size = 12;
-             bfd_put_32 (htab->stub_bfd, LD_R12_0R2 | PPC_LO (off), loc);
-           }
+           bfd_put_32 (htab->params->stub_bfd,
+                       LD_R12_0R2 | PPC_LO (off), p);
        }
       else
        {
          bfd_vma r2off = get_r2off (info, stub_entry);
 
-         if (r2off == 0)
+         if (r2off == (bfd_vma) -1)
            {
              htab->stub_error = TRUE;
              return FALSE;
            }
 
-         bfd_put_32 (htab->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
-         loc += 4;
-         size = 20;
+         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
          if (PPC_HA (off) != 0)
            {
-             size += 4;
-             bfd_put_32 (htab->stub_bfd, ADDIS_R11_R2 | PPC_HA (off), loc);
-             loc += 4;
-             bfd_put_32 (htab->stub_bfd, LD_R12_0R11 | PPC_LO (off), loc);
-             loc += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDIS_R12_R2 | PPC_HA (off), p);
+             p += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         LD_R12_0R12 | PPC_LO (off), p);
            }
          else
+           bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p);
+
+         if (PPC_HA (r2off) != 0)
+           {
+             p += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+           }
+         if (PPC_LO (r2off) != 0)
+           {
+             p += 4;
+             bfd_put_32 (htab->params->stub_bfd,
+                         ADDI_R2_R2 | PPC_LO (r2off), p);
+           }
+       }
+      p += 4;
+      bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+      p += 4;
+      bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+      p += 4;
+      break;
+
+    case ppc_stub_long_branch_notoc:
+    case ppc_stub_long_branch_both:
+    case ppc_stub_plt_branch_notoc:
+    case ppc_stub_plt_branch_both:
+    case ppc_stub_plt_call_notoc:
+    case ppc_stub_plt_call_both:
+      p = loc;
+      off = (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_long_branch_both
+         || stub_entry->stub_type == ppc_stub_plt_branch_both
+         || stub_entry->stub_type == ppc_stub_plt_call_both)
+       {
+         off += 4;
+         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
+       }
+      if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+       {
+         targ = stub_entry->plt_ent->plt.offset & ~1;
+         if (targ >= (bfd_vma) -2)
+           abort ();
+
+         plt = htab->elf.splt;
+         if (!htab->elf.dynamic_sections_created
+             || stub_entry->h == NULL
+             || stub_entry->h->elf.dynindx == -1)
            {
-             bfd_put_32 (htab->stub_bfd, LD_R12_0R2 | PPC_LO (off), loc);
-             loc += 4;
+             if (stub_entry->symtype == STT_GNU_IFUNC)
+               plt = htab->elf.iplt;
+             else
+               plt = htab->pltlocal;
            }
+         targ += plt->output_offset + plt->output_section->vma;
+       }
+      else
+       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;
+      if (htab->power10_stubs)
+       {
+         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 (PPC_HA (r2off) != 0)
+      if (stub_entry->stub_type <= ppc_stub_long_branch_both)
+       {
+         bfd_vma from;
+         num_rel = 1;
+         from = (stub_entry->stub_offset
+                 + stub_entry->group->stub_sec->output_offset
+                 + stub_entry->group->stub_sec->output_section->vma
+                 + (p - loc));
+         bfd_put_32 (htab->params->stub_bfd,
+                     B_DOT | ((targ - from) & 0x3fffffc), p);
+       }
+      else
+       {
+         bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+         p += 4;
+         bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+       }
+      p += 4;
+
+      if (info->emitrelocations)
+       {
+         bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
+         if (htab->power10_stubs)
+           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;
+         if (htab->power10_stubs)
+           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)
            {
-             size += 4;
-             bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
-             loc += 4;
+             ++r;
+             roff = p - 4 - stub_entry->group->stub_sec->contents;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
+             r->r_addend = targ;
+             if (stub_entry->h != NULL
+                 && !use_global_in_relocs (htab, stub_entry, r, num_rel))
+               return FALSE;
            }
-         bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
        }
-      loc += 4;
-      bfd_put_32 (htab->stub_bfd, MTCTR_R12, loc);
-      loc += 4;
-      bfd_put_32 (htab->stub_bfd, BCTR, loc);
+
+      if (!htab->power10_stubs
+         && htab->glink_eh_frame != NULL
+         && htab->glink_eh_frame->size != 0)
+       {
+         bfd_byte *base, *eh;
+         unsigned int lr_used, delta;
+
+         base = (htab->glink_eh_frame->contents
+                 + stub_entry->group->eh_base + 17);
+         eh = base + stub_entry->group->eh_size;
+         lr_used = stub_entry->stub_offset + 8;
+         if (stub_entry->stub_type == ppc_stub_long_branch_both
+             || stub_entry->stub_type == ppc_stub_plt_branch_both
+             || stub_entry->stub_type == ppc_stub_plt_call_both)
+           lr_used += 4;
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->lr_restore = lr_used + 8;
+         eh = eh_advance (htab->elf.dynobj, eh, delta);
+         *eh++ = DW_CFA_register;
+         *eh++ = 65;
+         *eh++ = 12;
+         *eh++ = DW_CFA_advance_loc + 2;
+         *eh++ = DW_CFA_restore_extended;
+         *eh++ = 65;
+         stub_entry->group->eh_size = eh - base;
+       }
       break;
 
     case ppc_stub_plt_call:
@@ -10663,60 +11708,39 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          struct ppc_link_hash_entry *fh = ppc_follow_link (stub_entry->h->oh);
 
          /* If the old-ABI "dot-symbol" is undefined make it weak so
-            we don't get a link error from RELOC_FOR_GLOBAL_SYMBOL.
-            FIXME: We used to define the symbol on one of the call
-            stubs instead, which is why we test symbol section id
-            against htab->top_id in various places.  Likely all
-            these checks could now disappear.  */
-         if (fh->elf.root.type == bfd_link_hash_undefined)
+            we don't get a link error from RELOC_FOR_GLOBAL_SYMBOL.  */
+         if (fh->elf.root.type == bfd_link_hash_undefined
+             && (stub_entry->h->elf.root.type == bfd_link_hash_defined
+                 || stub_entry->h->elf.root.type == bfd_link_hash_defweak))
            fh->elf.root.type = bfd_link_hash_undefweak;
-         /* Stop undo_symbol_twiddle changing it back to undefined.  */
-         fh->was_undefined = 0;
        }
 
       /* Now build the stub.  */
-      dest = stub_entry->plt_ent->plt.offset & ~1;
-      if (dest >= (bfd_vma) -2)
+      targ = stub_entry->plt_ent->plt.offset & ~1;
+      if (targ >= (bfd_vma) -2)
        abort ();
 
       plt = htab->elf.splt;
       if (!htab->elf.dynamic_sections_created
          || stub_entry->h == NULL
          || stub_entry->h->elf.dynindx == -1)
-       plt = htab->elf.iplt;
-
-      dest += plt->output_offset + plt->output_section->vma;
-
-      if (stub_entry->h == NULL
-         && (stub_entry->plt_ent->plt.offset & 1) == 0)
        {
-         Elf_Internal_Rela rela;
-         bfd_byte *rl;
-
-         rela.r_offset = dest;
-         if (htab->opd_abi)
-           rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+         if (stub_entry->symtype == STT_GNU_IFUNC)
+           plt = htab->elf.iplt;
          else
-           rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
-         rela.r_addend = (stub_entry->target_value
-                          + stub_entry->target_section->output_offset
-                          + stub_entry->target_section->output_section->vma);
-
-         rl = (htab->elf.irelplt->contents
-               + (htab->elf.irelplt->reloc_count++
-                  * sizeof (Elf64_External_Rela)));
-         bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl);
-         stub_entry->plt_ent->plt.offset |= 1;
+           plt = htab->pltlocal;
        }
+      targ += plt->output_offset + plt->output_section->vma;
 
-      off = (dest
-            - elf_gp (plt->output_section->owner)
-            - htab->stub_group[stub_entry->id_sec->id].toc_off);
+      off = (elf_gp (info->output_bfd)
+            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = targ - off;
 
       if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
        {
          info->callbacks->einfo
-           (_("%P: linkage table error against `%T'\n"),
+           /* xgettext:c-format */
+           (_("%P: linkage table error against `%pT'\n"),
             stub_entry->h != NULL
             ? stub_entry->h->elf.root.root.string
             : "<local sym>");
@@ -10725,56 +11749,55 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          return FALSE;
        }
 
-      if (htab->plt_stub_align != 0)
-       {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
-
-         stub_entry->stub_sec->size += pad;
-         stub_entry->stub_offset = stub_entry->stub_sec->size;
-         loc += pad;
-       }
-
       r = NULL;
       if (info->emitrelocations)
        {
-         r = get_relocs (stub_entry->stub_sec,
-                         (2
-                          + (PPC_HA (off) != 0)
-                          + (htab->plt_static_chain
-                             && PPC_HA (off + 16) == PPC_HA (off))));
+         r = get_relocs (stub_entry->group->stub_sec,
+                         ((PPC_HA (off) != 0)
+                          + (htab->opd_abi
+                             ? 2 + (htab->params->plt_static_chain
+                                    && PPC_HA (off + 16) == PPC_HA (off))
+                             : 1)));
          if (r == NULL)
            return FALSE;
-         r[0].r_offset = loc - stub_entry->stub_sec->contents;
+         r[0].r_offset = loc - stub_entry->group->stub_sec->contents;
          if (bfd_big_endian (info->output_bfd))
            r[0].r_offset += 2;
-         r[0].r_addend = dest;
+         r[0].r_addend = targ;
        }
       if (stub_entry->h != NULL
-         && (stub_entry->h == htab->tls_get_addr_fd
-             || stub_entry->h == htab->tls_get_addr)
-         && !htab->no_tls_get_addr_opt)
+         && 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
        p = build_plt_stub (htab, stub_entry, loc, off, r);
-      size = p - loc;
       break;
 
+    case ppc_stub_save_res:
+      return TRUE;
+
     default:
       BFD_FAIL ();
       return FALSE;
     }
 
-  stub_entry->stub_sec->size += size;
+  stub_entry->group->stub_sec->size = stub_entry->stub_offset + (p - loc);
 
-  if (htab->emit_stub_syms)
+  if (htab->params->emit_stub_syms)
     {
       struct elf_link_hash_entry *h;
       size_t len1, len2;
       char *name;
       const char *const stub_str[] = { "long_branch",
-                                      "long_branch_r2off",
+                                      "long_branch",
+                                      "long_branch",
+                                      "long_branch",
                                       "plt_branch",
-                                      "plt_branch_r2off",
+                                      "plt_branch",
+                                      "plt_branch",
+                                      "plt_branch",
+                                      "plt_call",
+                                      "plt_call",
                                       "plt_call",
                                       "plt_call" };
 
@@ -10792,13 +11815,14 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (h->root.type == bfd_link_hash_new)
        {
          h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = stub_entry->stub_sec;
+         h->root.u.def.section = stub_entry->group->stub_sec;
          h->root.u.def.value = stub_entry->stub_offset;
          h->ref_regular = 1;
          h->def_regular = 1;
          h->ref_regular_nonweak = 1;
          h->forced_local = 1;
          h->non_elf = 0;
+         h->root.linker_def = 1;
        }
     }
 
@@ -10815,8 +11839,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_stub_hash_entry *stub_entry;
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
-  bfd_vma off;
-  int size;
+  asection *plt;
+  bfd_vma targ, off, r2off;
+  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;
@@ -10824,80 +11849,85 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
-    return FALSE;
-
-  if (stub_entry->stub_type == ppc_stub_plt_call
-      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-    {
-      asection *plt;
-      off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
-      if (off >= (bfd_vma) -2)
-       abort ();
-      plt = htab->elf.splt;
-      if (!htab->elf.dynamic_sections_created
-         || stub_entry->h == NULL
-         || stub_entry->h->elf.dynindx == -1)
-       plt = htab->elf.iplt;
-      off += (plt->output_offset
-             + plt->output_section->vma
-             - elf_gp (plt->output_section->owner)
-             - htab->stub_group[stub_entry->id_sec->id].toc_off);
+    return FALSE;
 
-      size = plt_stub_size (htab, stub_entry, off);
-      if (htab->plt_stub_align)
-       size += plt_stub_pad (htab, stub_entry, off);
-      if (info->emitrelocations)
-       {
-         stub_entry->stub_sec->reloc_count
-           += ((PPC_HA (off) != 0)
-               + (htab->opd_abi
-                  ? 2 + (htab->plt_static_chain
-                         && PPC_HA (off + 16) == PPC_HA (off))
-                  : 1));
-         stub_entry->stub_sec->flags |= SEC_RELOC;
-       }
-    }
-  else
-    {
-      /* ppc_stub_long_branch or ppc_stub_plt_branch, or their r2off
-        variants.  */
-      bfd_vma r2off = 0;
-      bfd_vma local_off = 0;
+  /* 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);
 
-      off = (stub_entry->target_value
-            + stub_entry->target_section->output_offset
-            + stub_entry->target_section->output_section->vma);
-      off -= (stub_entry->stub_sec->size
-             + stub_entry->stub_sec->output_offset
-             + stub_entry->stub_sec->output_section->vma);
+  /* Make a note of the offset within the stubs for this entry.  */
+  stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+
+  if (stub_entry->h != NULL
+      && stub_entry->h->save_res
+      && stub_entry->h->elf.root.type == bfd_link_hash_defined
+      && stub_entry->h->elf.root.u.def.section == htab->sfpr)
+    {
+      /* Don't make stubs to out-of-line register save/restore
+        functions.  Instead, emit copies of the functions.  */
+      stub_entry->group->needs_save_res = 1;
+      stub_entry->stub_type = ppc_stub_save_res;
+      return TRUE;
+    }
 
-      /* Reset the stub type from the plt variant in case we now
+  switch (stub_entry->stub_type)
+    {
+    case ppc_stub_plt_branch:
+    case ppc_stub_plt_branch_r2off:
+      /* Reset the stub type from the plt branch variant in case we now
         can reach with a shorter stub.  */
-      if (stub_entry->stub_type >= ppc_stub_plt_branch)
-       stub_entry->stub_type += ppc_stub_long_branch - ppc_stub_plt_branch;
+      stub_entry->stub_type += ppc_stub_long_branch - ppc_stub_plt_branch;
+      /* Fall through.  */
+    case ppc_stub_long_branch:
+    case ppc_stub_long_branch_r2off:
+      targ = (stub_entry->target_value
+             + stub_entry->target_section->output_offset
+             + stub_entry->target_section->output_section->vma);
+      targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
 
       size = 4;
+      r2off = 0;
       if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
        {
          r2off = get_r2off (info, stub_entry);
-         if (r2off == 0 && htab->opd_abi)
+         if (r2off == (bfd_vma) -1)
            {
              htab->stub_error = TRUE;
              return FALSE;
            }
-         size = 12;
+         size = 8;
          if (PPC_HA (r2off) != 0)
-           size = 16;
-         off -= size - 4;
+           size += 4;
+         if (PPC_LO (r2off) != 0)
+           size += 4;
+         off += size - 4;
        }
+      off = targ - off;
 
-      local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
-
-      /* If the branch offset if too big, use a ppc_stub_plt_branch.
+      /* If the branch offset is too big, use a ppc_stub_plt_branch.
         Do the same for -R objects without function descriptors.  */
-      if (off + (1 << 25) >= (bfd_vma) (1 << 26) - local_off
-         || (stub_entry->stub_type == ppc_stub_long_branch_r2off
-             && r2off == 0))
+      if ((stub_entry->stub_type == ppc_stub_long_branch_r2off
+          && r2off == 0
+          && htab->sec_info[stub_entry->target_section->id].toc_off == 0)
+         || off + (1 << 25) >= (bfd_vma) (1 << 26))
        {
          struct ppc_branch_hash_entry *br_entry;
 
@@ -10906,8 +11936,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                                             TRUE, FALSE);
          if (br_entry == NULL)
            {
-             info->callbacks->einfo (_("%P: can't build branch stub `%s'\n"),
-                                     stub_entry->root.string);
+             _bfd_error_handler (_("can't build branch stub `%s'"),
+                                 stub_entry->root.string);
              htab->stub_error = TRUE;
              return FALSE;
            }
@@ -10927,21 +11957,22 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                }
            }
 
-         stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
-         off = (br_entry->offset
-                + htab->brlt->output_offset
-                + htab->brlt->output_section->vma
-                - elf_gp (htab->brlt->output_section->owner)
-                - htab->stub_group[stub_entry->id_sec->id].toc_off);
+         targ = (br_entry->offset
+                 + htab->brlt->output_offset
+                 + htab->brlt->output_section->vma);
+         off = (elf_gp (info->output_bfd)
+                + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+         off = targ - off;
 
          if (info->emitrelocations)
            {
-             stub_entry->stub_sec->reloc_count += 1 + (PPC_HA (off) != 0);
-             stub_entry->stub_sec->flags |= SEC_RELOC;
+             stub_entry->group->stub_sec->reloc_count
+               += 1 + (PPC_HA (off) != 0);
+             stub_entry->group->stub_sec->flags |= SEC_RELOC;
            }
 
-         if (stub_entry->stub_type != ppc_stub_plt_branch_r2off
-             || !htab->opd_abi)
+         stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
+         if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
            {
              size = 12;
              if (PPC_HA (off) != 0)
@@ -10949,22 +11980,227 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            }
          else
            {
-             size = 20;
+             size = 16;
              if (PPC_HA (off) != 0)
                size += 4;
 
              if (PPC_HA (r2off) != 0)
                size += 4;
+             if (PPC_LO (r2off) != 0)
+               size += 4;
            }
        }
       else if (info->emitrelocations)
        {
-         stub_entry->stub_sec->reloc_count += 1;
-         stub_entry->stub_sec->flags |= SEC_RELOC;
+         stub_entry->group->stub_sec->reloc_count += 1;
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+      break;
+
+    case ppc_stub_plt_branch_notoc:
+    case ppc_stub_plt_branch_both:
+      stub_entry->stub_type += ppc_stub_long_branch - ppc_stub_plt_branch;
+      /* Fall through.  */
+    case ppc_stub_long_branch_notoc:
+    case ppc_stub_long_branch_both:
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
+      size = 0;
+      if (stub_entry->stub_type == ppc_stub_long_branch_both)
+       size = 4;
+      off += size;
+      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)
+       {
+         unsigned int num_rel;
+         if (htab->power10_stubs)
+           num_rel = num_relocs_for_power10_offset (off, odd);
+         else
+           num_rel = num_relocs_for_offset (off - 8);
+         stub_entry->group->stub_sec->reloc_count += num_rel;
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
+      if (htab->power10_stubs)
+       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
+        its offset will be "extra" bytes less that that already
+        calculated.  */
+      off -= extra;
+
+      if (!htab->power10_stubs)
+       {
+         /* 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))
+       {
+         stub_entry->stub_type += (ppc_stub_plt_branch_notoc
+                                   - ppc_stub_long_branch_notoc);
+         size += 4;
+       }
+      else if (info->emitrelocations)
+       stub_entry->group->stub_sec->reloc_count +=1;
+      break;
+
+    case ppc_stub_plt_call_notoc:
+    case ppc_stub_plt_call_both:
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
+      if (stub_entry->stub_type == ppc_stub_plt_call_both)
+       off += 4;
+      targ = stub_entry->plt_ent->plt.offset & ~1;
+      if (targ >= (bfd_vma) -2)
+       abort ();
+
+      plt = htab->elf.splt;
+      if (!htab->elf.dynamic_sections_created
+         || stub_entry->h == NULL
+         || stub_entry->h->elf.dynindx == -1)
+       {
+         if (stub_entry->symtype == STT_GNU_IFUNC)
+           plt = htab->elf.iplt;
+         else
+           plt = htab->pltlocal;
+       }
+      targ += plt->output_offset + plt->output_section->vma;
+      odd = off & 4;
+      off = targ - off;
+
+      if (htab->params->plt_stub_align != 0)
+       {
+         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+
+         stub_entry->group->stub_sec->size += pad;
+         stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+         off -= pad;
+       }
+
+      if (info->emitrelocations)
+       {
+         unsigned int num_rel;
+         if (htab->power10_stubs)
+           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);
+
+      if (!htab->power10_stubs)
+       {
+         /* 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:
+    case ppc_stub_plt_call_r2save:
+      targ = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
+      if (targ >= (bfd_vma) -2)
+       abort ();
+      plt = htab->elf.splt;
+      if (!htab->elf.dynamic_sections_created
+         || stub_entry->h == NULL
+         || stub_entry->h->elf.dynindx == -1)
+       {
+         if (stub_entry->symtype == STT_GNU_IFUNC)
+           plt = htab->elf.iplt;
+         else
+           plt = htab->pltlocal;
+       }
+      targ += plt->output_offset + plt->output_section->vma;
+
+      off = (elf_gp (info->output_bfd)
+            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = targ - off;
+
+      if (htab->params->plt_stub_align != 0)
+       {
+         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+
+         stub_entry->group->stub_sec->size += pad;
+         stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+       }
+
+      if (info->emitrelocations)
+       {
+         stub_entry->group->stub_sec->reloc_count
+           += ((PPC_HA (off) != 0)
+               + (htab->opd_abi
+                  ? 2 + (htab->params->plt_static_chain
+                         && PPC_HA (off + 16) == PPC_HA (off))
+                  : 1));
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
+      size = plt_stub_size (htab, stub_entry, off);
+
+      if (stub_entry->h != NULL
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
+         && htab->params->tls_get_addr_opt
+         && stub_entry->stub_type == ppc_stub_plt_call_r2save)
+       {
+         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;
+
+    default:
+      BFD_FAIL ();
+      return FALSE;
     }
 
-  stub_entry->stub_sec->size += size;
+  stub_entry->group->stub_sec->size += size;
   return TRUE;
 }
 
@@ -10973,65 +12209,24 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
    0 when no stubs will be needed, and 1 on success.  */
 
 int
-ppc64_elf_setup_section_lists
-  (struct bfd_link_info *info,
-   asection *(*add_stub_section) (const char *, asection *),
-   void (*layout_sections_again) (void))
-{
-  bfd *input_bfd;
-  int top_id, top_index, id;
-  asection *section;
-  asection **input_list;
-  bfd_size_type amt;
+ppc64_elf_setup_section_lists (struct bfd_link_info *info)
+{
+  unsigned int id;
+  size_t amt;
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
   if (htab == NULL)
     return -1;
-  /* Stash our params away.  */
-  htab->add_stub_section = add_stub_section;
-  htab->layout_sections_again = layout_sections_again;
 
-  /* Find the top input section id.  */
-  for (input_bfd = info->input_bfds, top_id = 3;
-       input_bfd != NULL;
-       input_bfd = input_bfd->link_next)
-    {
-      for (section = input_bfd->sections;
-          section != NULL;
-          section = section->next)
-       {
-         if (top_id < section->id)
-           top_id = section->id;
-       }
-    }
-
-  htab->top_id = top_id;
-  amt = sizeof (struct map_stub) * (top_id + 1);
-  htab->stub_group = bfd_zmalloc (amt);
-  if (htab->stub_group == NULL)
+  htab->sec_info_arr_size = _bfd_section_id;
+  amt = sizeof (*htab->sec_info) * (htab->sec_info_arr_size);
+  htab->sec_info = bfd_zmalloc (amt);
+  if (htab->sec_info == NULL)
     return -1;
 
   /* Set toc_off for com, und, abs and ind sections.  */
   for (id = 0; id < 3; id++)
-    htab->stub_group[id].toc_off = TOC_BASE_OFF;
-
-  /* We can't use output_bfd->section_count here to find the top output
-     section index as some sections may have been removed, and
-     strip_excluded_output_sections doesn't renumber the indices.  */
-  for (section = info->output_bfd->sections, top_index = 0;
-       section != NULL;
-       section = section->next)
-    {
-      if (top_index < section->index)
-       top_index = section->index;
-    }
-
-  htab->top_index = top_index;
-  amt = sizeof (asection *) * (top_index + 1);
-  input_list = bfd_zmalloc (amt);
-  htab->input_list = input_list;
-  if (input_list == NULL)
-    return -1;
+    htab->sec_info[id].toc_off = TOC_BASE_OFF;
 
   return 1;
 }
@@ -11082,6 +12277,7 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
          addr = (htab->toc_first_sec->output_offset
                  + htab->toc_first_sec->output_section->vma);
          htab->toc_curr = addr;
+         htab->toc_curr &= -TOC_BASE_ALIGN;
        }
 
       /* toc_curr is the base address of this toc group.  Set elf_gp
@@ -11089,7 +12285,7 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
         output toc base plus 0x8000.  Making the input elf_gp an
         offset allows us to move the toc as a whole without
         recalculating input elf_gp.  */
-      off = htab->toc_curr - elf_gp (isec->output_section->owner);
+      off = htab->toc_curr - elf_gp (info->output_bfd);
       off += TOC_BASE_OFF;
 
       /* Die if someone uses a linker script that doesn't keep input
@@ -11118,7 +12314,7 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
     }
   addr = (htab->toc_first_sec->output_offset
          + htab->toc_first_sec->output_section->vma);
-  off = addr - elf_gp (isec->output_section->owner) + TOC_BASE_OFF;
+  off = addr - elf_gp (info->output_bfd) + TOC_BASE_OFF;
   elf_gp (isec->owner) = off;
 
   return TRUE;
@@ -11175,7 +12371,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
   elf_link_hash_traverse (&htab->elf, merge_global_got, info);
 
   /* And tlsld_got.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       struct got_entry *ent, *ent2;
 
@@ -11186,7 +12382,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
       if (!ent->is_indirect
          && ent->got.offset != (bfd_vma) -1)
        {
-         for (ibfd2 = ibfd->link_next; ibfd2 != NULL; ibfd2 = ibfd2->link_next)
+         for (ibfd2 = ibfd->link.next; ibfd2 != NULL; ibfd2 = ibfd2->link.next)
            {
              if (!is_ppc64_elf (ibfd2))
                continue;
@@ -11208,7 +12404,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
   htab->elf.irelplt->size -= htab->got_reli_size;
   htab->got_reli_size = 0;
 
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       asection *got, *relgot;
 
@@ -11228,7 +12424,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
 
   /* Now reallocate the got, local syms first.  We don't need to
      allocate section contents again since we never increase size.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       struct got_entry **lgot_ents;
       struct got_entry **end_lgot_ents;
@@ -11269,12 +12465,14 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
                  rel_size *= 2;
                }
              s->size += ent_size;
-             if ((*lgot_masks & PLT_IFUNC) != 0)
+             if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
                {
                  htab->elf.irelplt->size += rel_size;
                  htab->got_reli_size += rel_size;
                }
-             else if (info->shared)
+             else if (bfd_link_pic (info)
+                      && !(ent->tls_type != 0
+                           && bfd_link_executable (info)))
                {
                  asection *srel = ppc64_elf_tdata (ibfd)->relgot;
                  srel->size += rel_size;
@@ -11285,7 +12483,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
 
   elf_link_hash_traverse (&htab->elf, reallocate_got, info);
 
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       struct got_entry *ent;
 
@@ -11299,7 +12497,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
          asection *s = ppc64_elf_tdata (ibfd)->got;
          ent->got.offset = s->size;
          s->size += 16;
-         if (info->shared)
+         if (bfd_link_dll (info))
            {
              asection *srel = ppc64_elf_tdata (ibfd)->relgot;
              srel->size += sizeof (Elf64_External_Rela);
@@ -11309,7 +12507,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
 
   done_something = htab->elf.irelplt->rawsize != htab->elf.irelplt->size;
   if (!done_something)
-    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
       {
        asection *got;
 
@@ -11326,7 +12524,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
       }
 
   if (done_something)
-    (*htab->layout_sections_again) ();
+    (*htab->params->layout_sections_again) ();
 
   /* Set up for second pass over toc sections to recalculate elf_gp
      on input sections.  */
@@ -11406,9 +12604,12 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 
          r_type = ELF64_R_TYPE (rel->r_info);
          if (r_type != R_PPC64_REL24
+             && r_type != R_PPC64_REL24_NOTOC
              && r_type != R_PPC64_REL14
              && r_type != R_PPC64_REL14_BRTAKEN
-             && r_type != R_PPC64_REL14_BRNTAKEN)
+             && r_type != R_PPC64_REL14_BRNTAKEN
+             && r_type != R_PPC64_PLTCALL
+             && r_type != R_PPC64_PLTCALL_NOTOC)
            continue;
 
          r_symndx = ELF64_R_SYM (rel->r_info);
@@ -11421,7 +12622,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 
          /* Calls to dynamic lib functions go through a plt call stub
             that uses r2.  */
-         eh = (struct ppc_link_hash_entry *) h;
+         eh = ppc_elf_hash_entry (h);
          if (eh != NULL
              && (eh->elf.plt.plist != NULL
                  || (eh->oh != NULL
@@ -11462,7 +12663,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
                {
                  long adjust;
 
-                 adjust = opd->adjust[sym->st_value / 8];
+                 adjust = opd->adjust[OPD_NDX (sym_value)];
                  if (adjust == -1)
                    /* Assume deleted functions won't ever be called.  */
                    continue;
@@ -11532,9 +12733,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);
@@ -11579,15 +12779,13 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec)
     return FALSE;
 
   if ((isec->output_section->flags & SEC_CODE) != 0
-      && isec->output_section->index <= htab->top_index)
+      && isec->output_section->id < htab->sec_info_arr_size)
     {
-      asection **list = htab->input_list + isec->output_section->index;
-      /* Steal the link_sec pointer for our list.  */
-#define PREV_SEC(sec) (htab->stub_group[(sec)->id].link_sec)
       /* This happens to make the list in reverse order,
         which is what we want.  */
-      PREV_SEC (isec) = *list;
-      *list = isec;
+      htab->sec_info[isec->id].u.list
+       = htab->sec_info[isec->output_section->id].u.list;
+      htab->sec_info[isec->output_section->id].u.list = isec;
     }
 
   if (htab->multi_toc_needed)
@@ -11611,7 +12809,7 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec)
        htab->toc_curr = elf_gp (isec->owner);
     }
 
-  htab->stub_group[isec->id].toc_off = htab->toc_curr;
+  htab->sec_info[isec->id].toc_off = htab->toc_curr;
   return TRUE;
 }
 
@@ -11633,8 +12831,8 @@ check_pasted_section (struct bfd_link_info *info, const char *name)
        if (i->has_toc_reloc)
          {
            if (toc_off == 0)
-             toc_off = htab->stub_group[i->id].toc_off;
-           else if (toc_off != htab->stub_group[i->id].toc_off)
+             toc_off = htab->sec_info[i->id].toc_off;
+           else if (toc_off != htab->sec_info[i->id].toc_off)
              return FALSE;
          }
 
@@ -11642,14 +12840,14 @@ check_pasted_section (struct bfd_link_info *info, const char *name)
        for (i = o->map_head.s; i != NULL; i = i->map_head.s)
          if (i->makes_toc_func_call)
            {
-             toc_off = htab->stub_group[i->id].toc_off;
+             toc_off = htab->sec_info[i->id].toc_off;
              break;
            }
 
       /* Make sure the whole pasted function uses the same toc offset.  */
       if (toc_off != 0)
        for (i = o->map_head.s; i != NULL; i = i->map_head.s)
-         htab->stub_group[i->id].toc_off = toc_off;
+         htab->sec_info[i->id].toc_off = toc_off;
     }
   return TRUE;
 }
@@ -11668,37 +12866,38 @@ ppc64_elf_check_init_fini (struct bfd_link_info *info)
    _init and _fini functions into multiple parts.  Putting a stub in
    the middle of a function is not a good idea.  */
 
-static void
-group_sections (struct ppc_link_hash_table *htab,
+static bfd_boolean
+group_sections (struct bfd_link_info *info,
                bfd_size_type stub_group_size,
                bfd_boolean stubs_always_before_branch)
 {
-  asection **list;
-  bfd_size_type stub14_group_size;
+  struct ppc_link_hash_table *htab;
+  asection *osec;
   bfd_boolean suppress_size_errors;
 
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   suppress_size_errors = FALSE;
-  stub14_group_size = stub_group_size;
   if (stub_group_size == 1)
     {
       /* Default values.  */
       if (stubs_always_before_branch)
-       {
-         stub_group_size = 0x1e00000;
-         stub14_group_size = 0x7800;
-       }
+       stub_group_size = 0x1e00000;
       else
-       {
-         stub_group_size = 0x1c00000;
-         stub14_group_size = 0x7000;
-       }
+       stub_group_size = 0x1c00000;
       suppress_size_errors = TRUE;
     }
 
-  list = htab->input_list + htab->top_index;
-  do
+  for (osec = info->output_bfd->sections; osec != NULL; osec = osec->next)
     {
-      asection *tail = *list;
+      asection *tail;
+
+      if (osec->id >= htab->sec_info_arr_size)
+       continue;
+
+      tail = htab->sec_info[osec->id].u.list;
       while (tail != NULL)
        {
          asection *curr;
@@ -11706,44 +12905,60 @@ group_sections (struct ppc_link_hash_table *htab,
          bfd_size_type total;
          bfd_boolean big_sec;
          bfd_vma curr_toc;
+         struct map_stub *group;
+         bfd_size_type group_size;
 
          curr = tail;
          total = tail->size;
-         big_sec = total > (ppc64_elf_section_data (tail) != NULL
-                            && ppc64_elf_section_data (tail)->has_14bit_branch
-                            ? stub14_group_size : stub_group_size);
+         group_size = (ppc64_elf_section_data (tail) != NULL
+                       && ppc64_elf_section_data (tail)->has_14bit_branch
+                       ? stub_group_size >> 10 : stub_group_size);
+
+         big_sec = total > group_size;
          if (big_sec && !suppress_size_errors)
-           (*_bfd_error_handler) (_("%B section %A exceeds stub group size"),
-                                    tail->owner, tail);
-         curr_toc = htab->stub_group[tail->id].toc_off;
+           /* xgettext:c-format */
+           _bfd_error_handler (_("%pB section %pA exceeds stub group size"),
+                               tail->owner, tail);
+         curr_toc = htab->sec_info[tail->id].toc_off;
 
-         while ((prev = PREV_SEC (curr)) != NULL
+         while ((prev = htab->sec_info[curr->id].u.list) != NULL
                 && ((total += curr->output_offset - prev->output_offset)
                     < (ppc64_elf_section_data (prev) != NULL
                        && ppc64_elf_section_data (prev)->has_14bit_branch
-                       ? stub14_group_size : stub_group_size))
-                && htab->stub_group[prev->id].toc_off == curr_toc)
+                       ? (group_size = stub_group_size >> 10) : group_size))
+                && htab->sec_info[prev->id].toc_off == curr_toc)
            curr = prev;
 
          /* OK, the size from the start of CURR to the end is less
-            than stub_group_size and thus can be handled by one stub
+            than group_size and thus can be handled by one stub
             section.  (or the tail section is itself larger than
-            stub_group_size, in which case we may be toast.)  We
-            should really be keeping track of the total size of stubs
-            added here, as stubs contribute to the final output
-            section size.  That's a little tricky, and this way will
-            only break if stubs added make the total size more than
-            2^25, ie. for the default stub_group_size, if stubs total
-            more than 2097152 bytes, or nearly 75000 plt call stubs.  */
+            group_size, in which case we may be toast.)  We should
+            really be keeping track of the total size of stubs added
+            here, as stubs contribute to the final output section
+            size.  That's a little tricky, and this way will only
+            break if stubs added make the total size more than 2^25,
+            ie. for the default stub_group_size, if stubs total more
+            than 2097152 bytes, or nearly 75000 plt call stubs.  */
+         group = bfd_alloc (curr->owner, sizeof (*group));
+         if (group == NULL)
+           return FALSE;
+         group->link_sec = curr;
+         group->stub_sec = NULL;
+         group->needs_save_res = 0;
+         group->lr_restore = 0;
+         group->eh_size = 0;
+         group->eh_base = 0;
+         group->next = htab->group;
+         htab->group = group;
          do
            {
-             prev = PREV_SEC (tail);
+             prev = htab->sec_info[tail->id].u.list;
              /* Set up this stub group.  */
-             htab->stub_group[tail->id].link_sec = curr;
+             htab->sec_info[tail->id].u.group = group;
            }
          while (tail != curr && (tail = prev) != NULL);
 
-         /* But wait, there's more!  Input sections up to stub_group_size
+         /* But wait, there's more!  Input sections up to group_size
             bytes before the stub section can be handled by it too.
             Don't do this if we have a really large section after the
             stubs, as adding more stubs increases the chance that
@@ -11755,20 +12970,19 @@ group_sections (struct ppc_link_hash_table *htab,
                     && ((total += tail->output_offset - prev->output_offset)
                         < (ppc64_elf_section_data (prev) != NULL
                            && ppc64_elf_section_data (prev)->has_14bit_branch
-                           ? stub14_group_size : stub_group_size))
-                    && htab->stub_group[prev->id].toc_off == curr_toc)
+                           ? (group_size = stub_group_size >> 10)
+                           : group_size))
+                    && htab->sec_info[prev->id].toc_off == curr_toc)
                {
                  tail = prev;
-                 prev = PREV_SEC (tail);
-                 htab->stub_group[tail->id].link_sec = curr;
+                 prev = htab->sec_info[tail->id].u.list;
+                 htab->sec_info[tail->id].u.group = group;
                }
            }
          tail = prev;
        }
     }
-  while (list-- != htab->input_list);
-  free (htab->input_list);
-#undef PREV_SEC
+  return TRUE;
 }
 
 static const unsigned char glink_eh_frame_cie[] =
@@ -11813,9 +13027,7 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec)
    instruction.  */
 
 bfd_boolean
-ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
-                     bfd_boolean plt_static_chain, int plt_thread_safe,
-                     int plt_stub_align)
+ppc64_elf_size_stubs (struct bfd_link_info *info)
 {
   bfd_size_type stub_group_size;
   bfd_boolean stubs_always_before_branch;
@@ -11824,13 +13036,11 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
   if (htab == NULL)
     return FALSE;
 
-  htab->plt_static_chain = plt_static_chain;
-  htab->plt_stub_align = plt_stub_align;
-  if (plt_thread_safe == -1 && !info->executable)
-    plt_thread_safe = 1;
+  if (htab->params->plt_thread_safe == -1 && !bfd_link_executable (info))
+    htab->params->plt_thread_safe = 1;
   if (!htab->opd_abi)
-    plt_thread_safe = 0;
-  else if (plt_thread_safe == -1)
+    htab->params->plt_thread_safe = 0;
+  else if (htab->params->plt_thread_safe == -1)
     {
       static const char *const thread_starter[] =
        {
@@ -11843,45 +13053,94 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
          /* libanl */
          "getaddrinfo_a",
          /* libgomp */
+         "GOMP_parallel",
          "GOMP_parallel_start",
+         "GOMP_parallel_loop_static",
          "GOMP_parallel_loop_static_start",
+         "GOMP_parallel_loop_dynamic",
          "GOMP_parallel_loop_dynamic_start",
+         "GOMP_parallel_loop_guided",
          "GOMP_parallel_loop_guided_start",
+         "GOMP_parallel_loop_runtime",
          "GOMP_parallel_loop_runtime_start",
+         "GOMP_parallel_sections",
          "GOMP_parallel_sections_start",
+         /* libgo */
+         "__go_go",
        };
       unsigned i;
 
-      for (i = 0; i < sizeof (thread_starter)/ sizeof (thread_starter[0]); i++)
+      for (i = 0; i < ARRAY_SIZE (thread_starter); i++)
        {
          struct elf_link_hash_entry *h;
          h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
                                    FALSE, FALSE, TRUE);
-         plt_thread_safe = h != NULL && h->ref_regular;
-         if (plt_thread_safe)
+         htab->params->plt_thread_safe = h != NULL && h->ref_regular;
+         if (htab->params->plt_thread_safe)
            break;
        }
     }
-  htab->plt_thread_safe = plt_thread_safe;
-  stubs_always_before_branch = group_size < 0;
-  if (group_size < 0)
-    stub_group_size = -group_size;
+  stubs_always_before_branch = htab->params->group_size < 0;
+  if (htab->params->group_size < 0)
+    stub_group_size = -htab->params->group_size;
   else
-    stub_group_size = group_size;
+    stub_group_size = htab->params->group_size;
+
+  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);
+    }
 
-  group_sections (htab, stub_group_size, stubs_always_before_branch);
+#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
+     pathological case where adding stubs on one iteration decreases
+     section gaps (perhaps due to alignment), which then requires
+     fewer or smaller stubs on the next iteration.  */
 
   while (1)
     {
       bfd *input_bfd;
       unsigned int bfd_indx;
-      asection *stub_sec;
+      struct map_stub *group;
 
       htab->stub_iteration += 1;
 
       for (input_bfd = info->input_bfds, bfd_indx = 0;
           input_bfd != NULL;
-          input_bfd = input_bfd->link_next, bfd_indx++)
+          input_bfd = input_bfd->link.next, bfd_indx++)
        {
          Elf_Internal_Shdr *symtab_hdr;
          asection *section;
@@ -11958,6 +13217,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
                  /* Only look for stubs on branch instructions.  */
                  if (r_type != R_PPC64_REL24
+                     && r_type != R_PPC64_REL24_NOTOC
                      && r_type != R_PPC64_REL14
                      && r_type != R_PPC64_REL14_BRTAKEN
                      && r_type != R_PPC64_REL14_BRNTAKEN)
@@ -11968,7 +13228,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                  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;
@@ -11976,7 +13236,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                  if (hash == NULL)
                    {
                      sym_value = sym->st_value;
-                     ok_dest = TRUE;
+                     if (sym_sec != NULL
+                         && sym_sec->output_section != NULL)
+                       ok_dest = TRUE;
                    }
                  else if (hash->elf.root.type == bfd_link_hash_defined
                           || hash->elf.root.type == bfd_link_hash_defweak)
@@ -11992,8 +13254,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                         use the func descriptor sym instead if it is
                         defined.  */
                      if (hash->elf.root.root.string[0] == '.'
-                         && (fdh = lookup_fdh (hash, htab)) != NULL)
+                         && hash->oh != NULL)
                        {
+                         fdh = ppc_follow_link (hash->oh);
                          if (fdh->elf.root.type == bfd_link_hash_defined
                              || fdh->elf.root.type == bfd_link_hash_defweak)
                            {
@@ -12034,7 +13297,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
                      if (hash == NULL && opd->adjust != NULL)
                        {
-                         long adjust = opd->adjust[sym_value / 8];
+                         long adjust = opd->adjust[OPD_NDX (sym_value)];
                          if (adjust == -1)
                            continue;
                          code_value += adjust;
@@ -12062,7 +13325,19 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                                                &plt_ent, destination,
                                                local_off);
 
-                 if (stub_type != ppc_stub_plt_call)
+                 if (r_type == R_PPC64_REL24_NOTOC)
+                   {
+                     if (stub_type == ppc_stub_plt_call)
+                       stub_type = ppc_stub_plt_call_notoc;
+                     else if (stub_type == ppc_stub_long_branch
+                              || (code_sec != NULL
+                                  && code_sec->output_section != NULL
+                                  && (((hash ? hash->elf.other : sym->st_other)
+                                       & STO_PPC64_LOCAL_MASK)
+                                      > 1 << STO_PPC64_LOCAL_BIT)))
+                       stub_type = ppc_stub_long_branch_notoc;
+                   }
+                 else if (stub_type != ppc_stub_plt_call)
                    {
                      /* Check whether we need a TOC adjusting stub.
                         Since the linker pastes together pieces from
@@ -12070,12 +13345,15 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                         _init and _fini functions, it may be that a
                         call to what looks like a local sym is in
                         fact a call needing a TOC adjustment.  */
-                     if (code_sec != NULL
-                         && code_sec->output_section != NULL
-                         && (htab->stub_group[code_sec->id].toc_off
-                             != htab->stub_group[section->id].toc_off)
-                         && (code_sec->has_toc_reloc
-                             || code_sec->makes_toc_func_call))
+                     if ((code_sec != NULL
+                          && code_sec->output_section != NULL
+                          && (htab->sec_info[code_sec->id].toc_off
+                              != htab->sec_info[section->id].toc_off)
+                          && (code_sec->has_toc_reloc
+                              || code_sec->makes_toc_func_call))
+                         || (((hash ? hash->elf.other : sym->st_other)
+                              & STO_PPC64_LOCAL_MASK)
+                             == 1 << STO_PPC64_LOCAL_BIT))
                        stub_type = ppc_stub_long_branch_r2off;
                    }
 
@@ -12084,9 +13362,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
                  /* __tls_get_addr calls might be eliminated.  */
                  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)
                    {
@@ -12096,24 +13374,32 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                      if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms,
                                         irela - 1, input_bfd))
                        goto error_ret_free_internal;
-                     if (*tls_mask != 0)
+                     if ((*tls_mask & TLS_TLS) != 0
+                         && (*tls_mask & (TLS_GD | TLS_LD)) == 0)
                        continue;
                    }
 
-                 if (stub_type == ppc_stub_plt_call
-                     && irela + 1 < irelaend
-                     && irela[1].r_offset == irela->r_offset + 4
-                     && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
+                 if (stub_type == ppc_stub_plt_call)
                    {
-                     if (!tocsave_find (htab, INSERT,
-                                        &local_syms, irela + 1, input_bfd))
-                       goto error_ret_free_internal;
+                     if (!htab->opd_abi
+                         && htab->params->plt_localentry0 != 0
+                         && is_elfv2_localentry0 (&hash->elf))
+                       htab->has_plt_localentry0 = 1;
+                     else if (irela + 1 < irelaend
+                              && irela[1].r_offset == irela->r_offset + 4
+                              && (ELF64_R_TYPE (irela[1].r_info)
+                                  == R_PPC64_TOCSAVE))
+                       {
+                         if (!tocsave_find (htab, INSERT,
+                                            &local_syms, irela + 1, input_bfd))
+                           goto error_ret_free_internal;
+                       }
+                     else
+                       stub_type = ppc_stub_plt_call_r2save;
                    }
-                 else if (stub_type == ppc_stub_plt_call)
-                   stub_type = ppc_stub_plt_call_r2save;
 
                  /* Support for grouping stub sections.  */
-                 id_sec = htab->stub_group[section->id].link_sec;
+                 id_sec = htab->sec_info[section->id].u.group->link_sec;
 
                  /* Get the name of this stub.  */
                  stub_name = ppc_stub_name (id_sec, sym_sec, hash, irela);
@@ -12124,9 +13410,71 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                                                     stub_name, FALSE, FALSE);
                  if (stub_entry != NULL)
                    {
-                     /* The proper stub has already been created.  */
+                     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
+                        stubs or vice versa, but we might be
+                        upgrading from plt_call to plt_call_r2save or
+                        from long_branch to long_branch_r2off.  */
                      free (stub_name);
-                     if (stub_type == ppc_stub_plt_call_r2save)
+                     old_type = stub_entry->stub_type;
+                     switch (old_type)
+                       {
+                       default:
+                         abort ();
+
+                       case ppc_stub_save_res:
+                         continue;
+
+                       case ppc_stub_plt_call:
+                       case ppc_stub_plt_call_r2save:
+                       case ppc_stub_plt_call_notoc:
+                       case ppc_stub_plt_call_both:
+                         if (stub_type == ppc_stub_plt_call)
+                           continue;
+                         else if (stub_type == ppc_stub_plt_call_r2save)
+                           {
+                             if (old_type == ppc_stub_plt_call_notoc)
+                               stub_type = ppc_stub_plt_call_both;
+                           }
+                         else if (stub_type == ppc_stub_plt_call_notoc)
+                           {
+                             if (old_type == ppc_stub_plt_call_r2save)
+                               stub_type = ppc_stub_plt_call_both;
+                           }
+                         else
+                           abort ();
+                         break;
+
+                       case ppc_stub_plt_branch:
+                       case ppc_stub_plt_branch_r2off:
+                       case ppc_stub_plt_branch_notoc:
+                       case ppc_stub_plt_branch_both:
+                         old_type += (ppc_stub_long_branch
+                                      - ppc_stub_plt_branch);
+                         /* Fall through.  */
+                       case ppc_stub_long_branch:
+                       case ppc_stub_long_branch_r2off:
+                       case ppc_stub_long_branch_notoc:
+                       case ppc_stub_long_branch_both:
+                         if (stub_type == ppc_stub_long_branch)
+                           continue;
+                         else if (stub_type == ppc_stub_long_branch_r2off)
+                           {
+                             if (old_type == ppc_stub_long_branch_notoc)
+                               stub_type = ppc_stub_long_branch_both;
+                           }
+                         else if (stub_type == ppc_stub_long_branch_notoc)
+                           {
+                             if (old_type == ppc_stub_long_branch_r2off)
+                               stub_type = ppc_stub_long_branch_both;
+                           }
+                         else
+                           abort ();
+                         break;
+                       }
+                     if (old_type < stub_type)
                        stub_entry->stub_type = stub_type;
                      continue;
                    }
@@ -12139,30 +13487,33 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                      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;
                    }
 
                  stub_entry->stub_type = stub_type;
-                 if (stub_type != ppc_stub_plt_call
-                     && stub_type != ppc_stub_plt_call_r2save)
+                 if (stub_type >= ppc_stub_plt_call
+                     && stub_type <= ppc_stub_plt_call_both)
                    {
-                     stub_entry->target_value = code_value;
-                     stub_entry->target_section = code_sec;
+                     stub_entry->target_value = sym_value;
+                     stub_entry->target_section = sym_sec;
                    }
                  else
                    {
-                     stub_entry->target_value = sym_value;
-                     stub_entry->target_section = sym_sec;
+                     stub_entry->target_value = code_value;
+                     stub_entry->target_section = code_sec;
                    }
                  stub_entry->h = hash;
                  stub_entry->plt_ent = plt_ent;
+                 stub_entry->symtype
+                   = hash ? hash->elf.type : ELF_ST_TYPE (sym->st_info);
                  stub_entry->other = hash ? hash->elf.other : sym->st_other;
 
-                 if (stub_entry->h != NULL)
+                 if (hash != NULL
+                     && (hash->elf.root.type == bfd_link_hash_defined
+                         || hash->elf.root.type == bfd_link_hash_defweak))
                    htab->stub_globals += 1;
                }
 
@@ -12183,17 +13534,35 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
       /* We may have added some stubs.  Find out the new size of the
         stub sections.  */
-      for (stub_sec = htab->stub_bfd->sections;
-          stub_sec != NULL;
-          stub_sec = stub_sec->next)
-       if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
-         {
-           stub_sec->rawsize = stub_sec->size;
-           stub_sec->size = 0;
-           stub_sec->reloc_count = 0;
-           stub_sec->flags &= ~SEC_RELOC;
-         }
+      for (group = htab->group; group != NULL; group = group->next)
+       {
+         group->lr_restore = 0;
+         group->eh_size = 0;
+         if (group->stub_sec != NULL)
+           {
+             asection *stub_sec = group->stub_sec;
+
+             if (htab->stub_iteration <= STUB_SHRINK_ITER
+                 || stub_sec->rawsize < stub_sec->size)
+               /* Past STUB_SHRINK_ITER, rawsize is the max size seen.  */
+               stub_sec->rawsize = stub_sec->size;
+             stub_sec->size = 0;
+             stub_sec->reloc_count = 0;
+             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)
+       htab->brlt->rawsize = htab->brlt->size;
       htab->brlt->size = 0;
       htab->brlt->reloc_count = 0;
       htab->brlt->flags &= ~SEC_RELOC;
@@ -12202,6 +13571,10 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
       bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
 
+      for (group = htab->group; group != NULL; group = group->next)
+       if (group->needs_save_res)
+         group->stub_sec->size += htab->sfpr->size;
+
       if (info->emitrelocations
          && htab->glink != NULL && htab->glink->size != 0)
        {
@@ -12211,54 +13584,144 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
 
       if (htab->glink_eh_frame != NULL
          && !bfd_is_abs_section (htab->glink_eh_frame->output_section)
-         && htab->glink_eh_frame->output_section->size != 0)
+         && htab->glink_eh_frame->output_section->size > 8)
        {
-         size_t size = 0, align;
+         size_t size = 0, align = 4;
 
-         for (stub_sec = htab->stub_bfd->sections;
-              stub_sec != NULL;
-              stub_sec = stub_sec->next)
-           if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
-             size += 20;
+         for (group = htab->group; group != NULL; group = group->next)
+           if (group->eh_size != 0)
+             size += (group->eh_size + 17 + align - 1) & -align;
          if (htab->glink != NULL && htab->glink->size != 0)
-           size += 24;
+           size += (24 + align - 1) & -align;
          if (size != 0)
-           size += sizeof (glink_eh_frame_cie);
-         align = 1;
-         align <<= htab->glink_eh_frame->output_section->alignment_power;
-         align -= 1;
-         size = (size + align) & ~align;
+           size += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+         align = 1ul << htab->glink_eh_frame->output_section->alignment_power;
+         size = (size + align - 1) & -align;
          htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
          htab->glink_eh_frame->size = size;
        }
 
-      if (htab->plt_stub_align != 0)
-       for (stub_sec = htab->stub_bfd->sections;
-            stub_sec != NULL;
-            stub_sec = stub_sec->next)
-         if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
-           stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
-                             & (-1 << htab->plt_stub_align));
+      if (htab->params->plt_stub_align != 0)
+       for (group = htab->group; group != NULL; group = group->next)
+         if (group->stub_sec != NULL)
+           {
+             int align = abs (htab->params->plt_stub_align);
+             group->stub_sec->size
+               = (group->stub_sec->size + (1 << align) - 1) & -(1 << align);
+           }
 
-      for (stub_sec = htab->stub_bfd->sections;
-          stub_sec != NULL;
-          stub_sec = stub_sec->next)
-       if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
-           && stub_sec->rawsize != stub_sec->size)
+      for (group = htab->group; group != NULL; group = group->next)
+       if (group->stub_sec != NULL
+           && group->stub_sec->rawsize != group->stub_sec->size
+           && (htab->stub_iteration <= STUB_SHRINK_ITER
+               || group->stub_sec->rawsize < group->stub_sec->size))
          break;
 
-      /* Exit from this loop when no stubs have been added, and no stubs
-        have changed size.  */
-      if (stub_sec == NULL
+      if (group == NULL
+         && (htab->brlt->rawsize == htab->brlt->size
+             || (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.  */
-      (*htab->layout_sections_again) ();
+      (*htab->params->layout_sections_again) ();
+    }
+
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->size != 0)
+    {
+      bfd_vma val;
+      bfd_byte *p, *last_fde;
+      size_t last_fde_len, size, align, pad;
+      struct map_stub *group;
+
+      /* It is necessary to at least have a rough outline of the
+        linker generated CIEs and FDEs written before
+        bfd_elf_discard_info is run, in order for these FDEs to be
+        indexed in .eh_frame_hdr.  */
+      p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
+      if (p == NULL)
+       return FALSE;
+      htab->glink_eh_frame->contents = p;
+      last_fde = p;
+      align = 4;
+
+      memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
+      /* CIE length (rewrite in case little-endian).  */
+      last_fde_len = ((sizeof (glink_eh_frame_cie) + align - 1) & -align) - 4;
+      bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
+      p += last_fde_len + 4;
+
+      for (group = htab->group; group != NULL; group = group->next)
+       if (group->eh_size != 0)
+         {
+           group->eh_base = p - htab->glink_eh_frame->contents;
+           last_fde = p;
+           last_fde_len = ((group->eh_size + 17 + align - 1) & -align) - 4;
+           /* FDE length.  */
+           bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
+           p += 4;
+           /* CIE pointer.  */
+           val = p - htab->glink_eh_frame->contents;
+           bfd_put_32 (htab->elf.dynobj, val, p);
+           p += 4;
+           /* Offset to stub section, written later.  */
+           p += 4;
+           /* stub section size.  */
+           bfd_put_32 (htab->elf.dynobj, group->stub_sec->size, p);
+           p += 4;
+           /* Augmentation.  */
+           p += 1;
+           /* Make sure we don't have all nops.  This is enough for
+              elf-eh-frame.c to detect the last non-nop opcode.  */
+           p[group->eh_size - 1] = DW_CFA_advance_loc + 1;
+           p = last_fde + last_fde_len + 4;
+         }
+      if (htab->glink != NULL && htab->glink->size != 0)
+       {
+         last_fde = p;
+         last_fde_len = ((24 + align - 1) & -align) - 4;
+         /* FDE length.  */
+         bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
+         p += 4;
+         /* CIE pointer.  */
+         val = p - htab->glink_eh_frame->contents;
+         bfd_put_32 (htab->elf.dynobj, val, p);
+         p += 4;
+         /* Offset to .glink, written later.  */
+         p += 4;
+         /* .glink size.  */
+         bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p);
+         p += 4;
+         /* Augmentation.  */
+         p += 1;
+
+         *p++ = DW_CFA_advance_loc + 1;
+         *p++ = DW_CFA_register;
+         *p++ = 65;
+         *p++ = htab->opd_abi ? 12 : 0;
+         *p++ = DW_CFA_advance_loc + (htab->opd_abi ? 5 : 7);
+         *p++ = DW_CFA_restore_extended;
+         *p++ = 65;
+         p += ((24 + align - 1) & -align) - 24;
+       }
+      /* Subsume any padding into the last FDE if user .eh_frame
+        sections are aligned more than glink_eh_frame.  Otherwise any
+        zero padding will be seen as a terminator.  */
+      align = 1ul << htab->glink_eh_frame->output_section->alignment_power;
+      size = p - htab->glink_eh_frame->contents;
+      pad = ((size + align - 1) & -align) - size;
+      htab->glink_eh_frame->size = size + pad;
+      bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
     }
 
   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);
 
@@ -12272,7 +13735,33 @@ bfd_vma
 ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
 {
   asection *s;
-  bfd_vma TOCstart;
+  bfd_vma TOCstart, adjust;
+
+  if (info != NULL)
+    {
+      struct elf_link_hash_entry *h;
+      struct elf_link_hash_table *htab = elf_hash_table (info);
+
+      if (is_elf_hash_table (htab)
+         && htab->hgot != NULL)
+       h = htab->hgot;
+      else
+       {
+         h = elf_link_hash_lookup (htab, ".TOC.", FALSE, FALSE, TRUE);
+         if (is_elf_hash_table (htab))
+           htab->hgot = h;
+       }
+      if (h != NULL
+         && h->root.type == bfd_link_hash_defined
+         && !h->root.linker_def
+         && (!is_elf_hash_table (htab)
+             || h->def_regular))
+       {
+         TOCstart = defined_sym_val (h) - TOC_BASE_OFF;
+         _bfd_set_gp_value (obfd, TOCstart);
+         return TOCstart;
+       }
+    }
 
   /* The TOC consists of sections .got, .toc, .tocbss, .plt in that
      order.  The TOC starts where the first of these sections starts.  */
@@ -12320,82 +13809,199 @@ ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
   if (s != NULL)
     TOCstart = s->output_section->vma + s->output_offset;
 
+  /* Force alignment.  */
+  adjust = TOCstart & (TOC_BASE_ALIGN - 1);
+  TOCstart -= adjust;
   _bfd_set_gp_value (obfd, TOCstart);
 
-  if (info != NULL && s != NULL && is_ppc64_elf (obfd))
+  if (info != NULL && s != NULL)
     {
       struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
-      if (htab != NULL
-         && htab->elf.hgot != NULL)
+      if (htab != NULL)
        {
-         htab->elf.hgot->type = STT_OBJECT;
-         htab->elf.hgot->root.type = bfd_link_hash_defined;
-         htab->elf.hgot->root.u.def.value = TOC_BASE_OFF;
-         htab->elf.hgot->root.u.def.section = s;
+         if (htab->elf.hgot != NULL)
+           {
+             htab->elf.hgot->root.u.def.value = TOC_BASE_OFF - adjust;
+             htab->elf.hgot->root.u.def.section = s;
+           }
+       }
+      else
+       {
+         struct bfd_link_hash_entry *bh = NULL;
+         _bfd_generic_link_add_one_symbol (info, obfd, ".TOC.", BSF_GLOBAL,
+                                           s, TOC_BASE_OFF - adjust,
+                                           NULL, FALSE, FALSE, &bh);
        }
     }
   return TOCstart;
 }
 
 /* Called via elf_link_hash_traverse from ppc64_elf_build_stubs to
-   write out any global entry stubs.  */
+   write out any global entry stubs, and PLT relocations.  */
 
 static bfd_boolean
-build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
+build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
-  struct plt_entry *pent;
+  struct plt_entry *ent;
   asection *s;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
+  info = inf;
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->plt.offset != (bfd_vma) -1)
+      {
+       /* This symbol has an entry in the procedure linkage
+          table.  Set it up.  */
+       Elf_Internal_Rela rela;
+       asection *plt, *relplt;
+       bfd_byte *loc;
+
+       if (!htab->elf.dynamic_sections_created
+           || h->dynindx == -1)
+         {
+           if (!(h->def_regular
+                 && (h->root.type == bfd_link_hash_defined
+                     || h->root.type == bfd_link_hash_defweak)))
+             continue;
+           if (h->type == STT_GNU_IFUNC)
+             {
+               plt = htab->elf.iplt;
+               relplt = htab->elf.irelplt;
+               htab->elf.ifunc_resolvers = TRUE;
+               if (htab->opd_abi)
+                 rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+               else
+                 rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+             }
+           else
+             {
+               plt = htab->pltlocal;
+               if (bfd_link_pic (info))
+                 {
+                   relplt = htab->relpltlocal;
+                   if (htab->opd_abi)
+                     rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_SLOT);
+                   else
+                     rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+                 }
+               else
+                 relplt = NULL;
+             }
+           rela.r_addend = defined_sym_val (h) + ent->addend;
+
+           if (relplt == NULL)
+             {
+               loc = plt->contents + ent->plt.offset;
+               bfd_put_64 (info->output_bfd, rela.r_addend, loc);
+               if (htab->opd_abi)
+                 {
+                   bfd_vma toc = elf_gp (info->output_bfd);
+                   toc += htab->sec_info[h->root.u.def.section->id].toc_off;
+                   bfd_put_64 (info->output_bfd, toc, loc + 8);
+                 }
+             }
+           else
+             {
+               rela.r_offset = (plt->output_section->vma
+                                + plt->output_offset
+                                + ent->plt.offset);
+               loc = relplt->contents + (relplt->reloc_count++
+                                         * sizeof (Elf64_External_Rela));
+               bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+             }
+         }
+       else
+         {
+           rela.r_offset = (htab->elf.splt->output_section->vma
+                            + htab->elf.splt->output_offset
+                            + ent->plt.offset);
+           rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
+           rela.r_addend = ent->addend;
+           loc = (htab->elf.srelplt->contents
+                  + ((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->elf.ifunc_resolvers = TRUE;
+           bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+         }
+      }
+
   if (!h->pointer_equality_needed)
     return TRUE;
 
   if (h->def_regular)
     return TRUE;
 
-  info = inf;
-  htab = ppc_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
+  s = htab->global_entry;
+  if (s == NULL || s->size == 0)
+    return TRUE;
 
-  s = htab->glink;
-  for (pent = h->plt.plist; pent != NULL; pent = pent->next)
-    if (pent->plt.offset != (bfd_vma) -1
-       && pent->addend == 0)
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->plt.offset != (bfd_vma) -1
+       && ent->addend == 0)
       {
        bfd_byte *p;
        asection *plt;
        bfd_vma off;
 
-       /* For ELFv2, if this symbol is not defined in a regular file
-          and we are not generating a shared library or pie, then we
-          need to define the symbol in the executable on a call stub.
-          This is to avoid text relocations.  */
-       h->root.u.def.section = s;
-       h->root.u.def.value = s->size;
-       s->size += 16;
        p = s->contents + h->root.u.def.value;
        plt = htab->elf.splt;
        if (!htab->elf.dynamic_sections_created
            || h->dynindx == -1)
-         plt = htab->elf.iplt;
-       off = pent->plt.offset + plt->output_offset + plt->output_section->vma;
+         {
+           if (h->type == STT_GNU_IFUNC)
+             plt = htab->elf.iplt;
+           else
+             plt = htab->pltlocal;
+         }
+       off = ent->plt.offset + plt->output_offset + plt->output_section->vma;
        off -= h->root.u.def.value + s->output_offset + s->output_section->vma;
 
        if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
          {
            info->callbacks->einfo
-             (_("%P: linkage table error against `%T'\n"),
+             (_("%P: linkage table error against `%pT'\n"),
               h->root.root.string);
            bfd_set_error (bfd_error_bad_value);
            htab->stub_error = TRUE;
          }
 
+       htab->stub_count[ppc_stub_global_entry - 1] += 1;
+       if (htab->params->emit_stub_syms)
+         {
+           size_t len = strlen (h->root.root.string);
+           char *name = bfd_malloc (sizeof "12345678.global_entry." + len);
+
+           if (name == NULL)
+             return FALSE;
+
+           sprintf (name, "%08x.global_entry.%s", s->id, h->root.root.string);
+           h = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
+           if (h == NULL)
+             return FALSE;
+           if (h->root.type == bfd_link_hash_new)
+             {
+               h->root.type = bfd_link_hash_defined;
+               h->root.u.def.section = s;
+               h->root.u.def.value = p - s->contents;
+               h->ref_regular = 1;
+               h->def_regular = 1;
+               h->ref_regular_nonweak = 1;
+               h->forced_local = 1;
+               h->non_elf = 0;
+               h->root.linker_def = 1;
+             }
+         }
+
        if (PPC_HA (off) != 0)
          {
            bfd_put_32 (s->owner, ADDIS_R12_R12 | PPC_HA (off), p);
@@ -12411,16 +14017,198 @@ build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
+/* Write PLT relocs for locals.  */
+
+static bfd_boolean
+write_plt_relocs_for_local_syms (struct bfd_link_info *info)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  bfd *ibfd;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      struct got_entry **lgot_ents, **end_lgot_ents;
+      struct plt_entry **local_plt, **lplt, **end_local_plt;
+      Elf_Internal_Shdr *symtab_hdr;
+      bfd_size_type locsymcount;
+      Elf_Internal_Sym *local_syms = NULL;
+      struct plt_entry *ent;
+
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
+      lgot_ents = elf_local_got_ents (ibfd);
+      if (!lgot_ents)
+       continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      locsymcount = symtab_hdr->sh_info;
+      end_lgot_ents = lgot_ents + locsymcount;
+      local_plt = (struct plt_entry **) end_lgot_ents;
+      end_local_plt = local_plt + locsymcount;
+      for (lplt = local_plt; lplt < end_local_plt; ++lplt)
+       for (ent = *lplt; ent != NULL; ent = ent->next)
+         if (ent->plt.offset != (bfd_vma) -1)
+           {
+             Elf_Internal_Sym *sym;
+             asection *sym_sec;
+             asection *plt, *relplt;
+             bfd_byte *loc;
+             bfd_vma val;
+
+             if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms,
+                             lplt - local_plt, ibfd))
+               {
+                 if (symtab_hdr->contents != (unsigned char *) local_syms)
+                   free (local_syms);
+                 return FALSE;
+               }
+
+             val = sym->st_value + ent->addend;
+             if (ELF_ST_TYPE (sym->st_info) != STT_GNU_IFUNC)
+               val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
+             if (sym_sec != NULL && sym_sec->output_section != NULL)
+               val += sym_sec->output_offset + sym_sec->output_section->vma;
+
+             if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+               {
+                 htab->elf.ifunc_resolvers = TRUE;
+                 plt = htab->elf.iplt;
+                 relplt = htab->elf.irelplt;
+               }
+             else
+               {
+                 plt = htab->pltlocal;
+                 relplt = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+               }
+
+             if (relplt == NULL)
+               {
+                 loc = plt->contents + ent->plt.offset;
+                 bfd_put_64 (info->output_bfd, val, loc);
+                 if (htab->opd_abi)
+                   {
+                     bfd_vma toc = elf_gp (ibfd);
+                     bfd_put_64 (info->output_bfd, toc, loc + 8);
+                   }
+               }
+             else
+               {
+                 Elf_Internal_Rela rela;
+                 rela.r_offset = (ent->plt.offset
+                                  + plt->output_offset
+                                  + plt->output_section->vma);
+                 if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                   {
+                     if (htab->opd_abi)
+                       rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+                     else
+                       rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+                   }
+                 else
+                   {
+                     if (htab->opd_abi)
+                       rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_SLOT);
+                     else
+                       rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+                   }
+                 rela.r_addend = val;
+                 loc = relplt->contents + (relplt->reloc_count++
+                                           * sizeof (Elf64_External_Rela));
+                 bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+               }
+           }
+
+      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;
+}
+
+/* Emit the static wrapper function preserving registers around a
+   __tls_get_addr_opt call.  */
+
+static bfd_boolean
+emit_tga_desc (struct ppc_link_hash_table *htab)
+{
+  asection *stub_sec = htab->tga_group->stub_sec;
+  unsigned int cfa_updt = 11 * 4;
+  bfd_byte *p;
+  bfd_vma to, from, delta;
+
+  BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined
+             && htab->tga_desc_fd->elf.root.u.def.section == stub_sec
+             && htab->tga_desc_fd->elf.root.u.def.value == 0);
+  to = defined_sym_val (&htab->tls_get_addr_fd->elf);
+  from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt;
+  delta = to - from;
+  if (delta + (1 << 25) >= 1 << 26)
+    {
+      _bfd_error_handler (_("__tls_get_addr call offset overflow"));
+      htab->stub_error = TRUE;
+      return FALSE;
+    }
+
+  p = stub_sec->contents;
+  p = tls_get_addr_prologue (htab->elf.dynobj, p, htab);
+  bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p);
+  p += 4;
+  p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab);
+  return stub_sec->size == (bfd_size_type) (p - stub_sec->contents);
+}
+
+/* Emit eh_frame describing the static wrapper function.  */
+
+static bfd_byte *
+emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p)
+{
+  unsigned int cfa_updt = 11 * 4;
+  unsigned int i;
+
+  *p++ = DW_CFA_advance_loc + cfa_updt / 4;
+  *p++ = DW_CFA_def_cfa_offset;
+  if (htab->opd_abi)
+    {
+      *p++ = 128;
+      *p++ = 1;
+    }
+  else
+    *p++ = 96;
+  *p++ = DW_CFA_offset_extended_sf;
+  *p++ = 65;
+  *p++ = (-16 / 8) & 0x7f;
+  for (i = 4; i < 12; i++)
+    {
+      *p++ = DW_CFA_offset + i;
+      *p++ = (htab->opd_abi ? 13 : 12) - i;
+    }
+  *p++ = DW_CFA_advance_loc + 10;
+  *p++ = DW_CFA_def_cfa_offset;
+  *p++ = 0;
+  for (i = 4; i < 12; i++)
+    *p++ = DW_CFA_restore + i;
+  *p++ = DW_CFA_advance_loc + 2;
+  *p++ = DW_CFA_restore_extended;
+  *p++ = 65;
+  return p;
+}
+
 /* Build all the stubs associated with the current output file.
    The stubs are kept in a hash table attached to the main linker
    hash table.  This function is called via gldelf64ppc_finish.  */
 
 bfd_boolean
-ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
-                      struct bfd_link_info *info,
+ppc64_elf_build_stubs (struct bfd_link_info *info,
                       char **stats)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  struct map_stub *group;
   asection *stub_sec;
   bfd_byte *p;
   int stub_sec_count = 0;
@@ -12428,23 +14216,21 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
   if (htab == NULL)
     return FALSE;
 
-  htab->emit_stub_syms = emit_stub_syms;
-
   /* Allocate memory to hold the linker stubs.  */
-  for (stub_sec = htab->stub_bfd->sections;
-       stub_sec != NULL;
-       stub_sec = stub_sec->next)
-    if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
-       && stub_sec->size != 0)
-      {
-       stub_sec->contents = bfd_zalloc (htab->stub_bfd, stub_sec->size);
-       if (stub_sec->contents == NULL)
-         return FALSE;
-       /* We want to check that built size is the same as calculated
-          size.  rawsize is a convenient location to use.  */
-       stub_sec->rawsize = stub_sec->size;
-       stub_sec->size = 0;
-      }
+  for (group = htab->group; group != NULL; group = group->next)
+    {
+      group->eh_size = 0;
+      group->lr_restore = 0;
+      if ((stub_sec = group->stub_sec) != NULL
+         && stub_sec->size != 0)
+       {
+         stub_sec->contents = bfd_zalloc (htab->params->stub_bfd,
+                                          stub_sec->size);
+         if (stub_sec->contents == NULL)
+           return FALSE;
+         stub_sec->size = 0;
+       }
+    }
 
   if (htab->glink != NULL && htab->glink->size != 0)
     {
@@ -12452,7 +14238,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
       bfd_vma plt0;
 
       /* Build the .glink plt call stub.  */
-      if (htab->emit_stub_syms)
+      if (htab->params->emit_stub_syms)
        {
          struct elf_link_hash_entry *h;
          h = elf_link_hash_lookup (&htab->elf, "__glink_PLTresolve",
@@ -12469,6 +14255,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
              h->ref_regular_nonweak = 1;
              h->forced_local = 1;
              h->non_elf = 0;
+             h->root.linker_def = 1;
            }
        }
       plt0 = (htab->elf.splt->output_section->vma
@@ -12519,6 +14306,8 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
          p += 4;
          bfd_put_32 (htab->glink->owner, MFLR_R11, p);
          p += 4;
+         bfd_put_32 (htab->glink->owner, STD_R2_0R1 + 24, p);
+         p += 4;
          bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
          p += 4;
          bfd_put_32 (htab->glink->owner, MTLR_R0, p);
@@ -12540,15 +14329,11 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
        }
       bfd_put_32 (htab->glink->owner, BCTR, p);
       p += 4;
-      while (p - htab->glink->contents < GLINK_CALL_STUB_SIZE)
-       {
-         bfd_put_32 (htab->glink->owner, NOP, p);
-         p += 4;
-       }
+      BFD_ASSERT (p == htab->glink->contents + GLINK_PLTRESOLVE_SIZE (htab));
 
       /* Build the .glink lazy link call stubs.  */
       indx = 0;
-      while (p < htab->glink->contents + htab->glink->rawsize)
+      while (p < htab->glink->contents + htab->glink->size)
        {
          if (htab->opd_abi)
            {
@@ -12571,16 +14356,33 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
          indx++;
          p += 4;
        }
+    }
 
-      /* Build .glink global entry stubs.  */
-      if (htab->glink->size > htab->glink->rawsize)
+  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)
        {
-         htab->glink->size = (htab->glink->rawsize + 15) & -16;
-         elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
+         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;
        }
     }
 
-  if (htab->brlt->size != 0)
+  /* Build .glink global entry stubs, and PLT relocs for globals.  */
+  elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
+
+  if (!write_plt_relocs_for_local_syms (info))
+    return FALSE;
+
+  if (htab->brlt != NULL && htab->brlt->size != 0)
     {
       htab->brlt->contents = bfd_zalloc (htab->brlt->owner,
                                         htab->brlt->size);
@@ -12595,147 +14397,103 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
        return FALSE;
     }
 
-  if (htab->glink_eh_frame != NULL
-      && htab->glink_eh_frame->size != 0)
-    {
-      bfd_vma val;
-      bfd_byte *last_fde;
-      size_t last_fde_len, size, align, pad;
+  /* Build the stubs as directed by the stub hash table.  */
+  bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
 
-      p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
-      if (p == NULL)
-       return FALSE;
-      htab->glink_eh_frame->contents = p;
-      last_fde = p;
+  for (group = htab->group; group != NULL; group = group->next)
+    if (group->needs_save_res)
+      group->stub_sec->size += htab->sfpr->size;
 
-      htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
+  if (htab->relbrlt != NULL)
+    htab->relbrlt->reloc_count = 0;
 
-      memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
-      /* CIE length (rewrite in case little-endian).  */
-      last_fde_len = sizeof (glink_eh_frame_cie) - 4;
-      bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
-      p += sizeof (glink_eh_frame_cie);
+  if (htab->params->plt_stub_align != 0)
+    for (group = htab->group; group != NULL; group = group->next)
+      if ((stub_sec = group->stub_sec) != NULL)
+       {
+         int align = abs (htab->params->plt_stub_align);
+         stub_sec->size = (stub_sec->size + (1 << align) - 1) & -(1 << align);
+       }
 
-      for (stub_sec = htab->stub_bfd->sections;
-          stub_sec != NULL;
-          stub_sec = stub_sec->next)
-       if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+  for (group = htab->group; group != NULL; group = group->next)
+    if (group->needs_save_res)
+      {
+       stub_sec = group->stub_sec;
+       memcpy (stub_sec->contents + stub_sec->size - htab->sfpr->size,
+               htab->sfpr->contents, htab->sfpr->size);
+       if (htab->params->emit_stub_syms)
+         {
+           unsigned int i;
+
+           for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
+             if (!sfpr_define (info, &save_res_funcs[i], stub_sec))
+               return FALSE;
+         }
+      }
+
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->size != 0)
+    {
+      bfd_vma val;
+      size_t align = 4;
+
+      p = htab->glink_eh_frame->contents;
+      p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+
+      for (group = htab->group; group != NULL; group = group->next)
+       if (group->eh_size != 0)
          {
-           last_fde = p;
-           last_fde_len = 16;
-           /* FDE length.  */
-           bfd_put_32 (htab->elf.dynobj, 16, p);
-           p += 4;
-           /* CIE pointer.  */
-           val = p - htab->glink_eh_frame->contents;
-           bfd_put_32 (htab->elf.dynobj, val, p);
-           p += 4;
            /* Offset to stub section.  */
-           val = (stub_sec->output_section->vma
-                  + stub_sec->output_offset);
+           val = (group->stub_sec->output_section->vma
+                  + group->stub_sec->output_offset);
            val -= (htab->glink_eh_frame->output_section->vma
-                   + htab->glink_eh_frame->output_offset);
-           val -= p - htab->glink_eh_frame->contents;
+                   + htab->glink_eh_frame->output_offset
+                   + (p + 8 - htab->glink_eh_frame->contents));
            if (val + 0x80000000 > 0xffffffff)
              {
-               info->callbacks->einfo
-                 (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
-                  stub_sec->name);
+               _bfd_error_handler
+                 (_("%s offset too large for .eh_frame sdata4 encoding"),
+                  group->stub_sec->name);
                return FALSE;
              }
-           bfd_put_32 (htab->elf.dynobj, val, p);
-           p += 4;
-           /* stub section size.  */
-           bfd_put_32 (htab->elf.dynobj, stub_sec->rawsize, p);
-           p += 4;
-           /* Augmentation.  */
-           p += 1;
-           /* Pad.  */
-           p += 3;
+           bfd_put_32 (htab->elf.dynobj, val, p + 8);
+           p += (group->eh_size + 17 + 3) & -4;
          }
       if (htab->glink != NULL && htab->glink->size != 0)
        {
-         last_fde = p;
-         last_fde_len = 20;
-         /* FDE length.  */
-         bfd_put_32 (htab->elf.dynobj, 20, p);
-         p += 4;
-         /* CIE pointer.  */
-         val = p - htab->glink_eh_frame->contents;
-         bfd_put_32 (htab->elf.dynobj, val, p);
-         p += 4;
          /* Offset to .glink.  */
          val = (htab->glink->output_section->vma
                 + htab->glink->output_offset
                 + 8);
          val -= (htab->glink_eh_frame->output_section->vma
-                 + htab->glink_eh_frame->output_offset);
-         val -= p - htab->glink_eh_frame->contents;
+                 + htab->glink_eh_frame->output_offset
+                 + (p + 8 - htab->glink_eh_frame->contents));
          if (val + 0x80000000 > 0xffffffff)
            {
-             info->callbacks->einfo
-               (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+             _bfd_error_handler
+               (_("%s offset too large for .eh_frame sdata4 encoding"),
                 htab->glink->name);
              return FALSE;
            }
-         bfd_put_32 (htab->elf.dynobj, val, p);
-         p += 4;
-         /* .glink size.  */
-         bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p);
-         p += 4;
-         /* Augmentation.  */
-         p += 1;
-
-         *p++ = DW_CFA_advance_loc + 1;
-         *p++ = DW_CFA_register;
-         *p++ = 65;
-         *p++ = 12;
-         *p++ = DW_CFA_advance_loc + 4;
-         *p++ = DW_CFA_restore_extended;
-         *p++ = 65;
+         bfd_put_32 (htab->elf.dynobj, val, p + 8);
+         p += (24 + align - 1) & -align;
        }
-      /* Subsume any padding into the last FDE if user .eh_frame
-        sections are aligned more than glink_eh_frame.  Otherwise any
-        zero padding will be seen as a terminator.  */
-      size = p - htab->glink_eh_frame->contents;
-      align = 1;
-      align <<= htab->glink_eh_frame->output_section->alignment_power;
-      align -= 1;
-      pad = ((size + align) & ~align) - size;
-      htab->glink_eh_frame->size = size + pad;
-      bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
     }
 
-  /* Build the stubs as directed by the stub hash table.  */
-  bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
-
-  if (htab->relbrlt != NULL)
-    htab->relbrlt->reloc_count = 0;
-
-  if (htab->plt_stub_align != 0)
-    for (stub_sec = htab->stub_bfd->sections;
-        stub_sec != NULL;
-        stub_sec = stub_sec->next)
-      if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
-       stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
-                         & (-1 << htab->plt_stub_align));
-
-  for (stub_sec = htab->stub_bfd->sections;
-       stub_sec != NULL;
-       stub_sec = stub_sec->next)
-    if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+  for (group = htab->group; group != NULL; group = group->next)
+    if ((stub_sec = group->stub_sec) != NULL)
       {
        stub_sec_count += 1;
-       if (stub_sec->rawsize != stub_sec->size)
+       if (stub_sec->rawsize != stub_sec->size
+           && (htab->stub_iteration <= STUB_SHRINK_ITER
+               || stub_sec->rawsize < stub_sec->size))
          break;
       }
 
-  if (stub_sec != NULL
-      || (htab->glink_eh_frame != NULL
-         && htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size))
+  if (group != NULL)
     {
       htab->stub_error = TRUE;
-      info->callbacks->einfo (_("%P: stubs don't match calculated size\n"));
+      _bfd_error_handler (_("stubs don't match calculated size"));
     }
 
   if (htab->stub_error)
@@ -12743,56 +14501,50 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
 
   if (stats != NULL)
     {
-      *stats = bfd_malloc (500);
-      if (*stats == NULL)
-       return FALSE;
-
-      sprintf (*stats, _("linker stubs in %u group%s\n"
-                        "  branch       %lu\n"
-                        "  toc adjust   %lu\n"
-                        "  long branch  %lu\n"
-                        "  long toc adj %lu\n"
-                        "  plt call     %lu\n"
-                        "  plt call toc %lu"),
-              stub_sec_count,
-              stub_sec_count == 1 ? "" : "s",
-              htab->stub_count[ppc_stub_long_branch - 1],
-              htab->stub_count[ppc_stub_long_branch_r2off - 1],
-              htab->stub_count[ppc_stub_plt_branch - 1],
-              htab->stub_count[ppc_stub_plt_branch_r2off - 1],
-              htab->stub_count[ppc_stub_plt_call - 1],
-              htab->stub_count[ppc_stub_plt_call_r2save - 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;
 }
 
-/* This function undoes the changes made by add_symbol_adjust.  */
-
-static bfd_boolean
-undo_symbol_twiddle (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
-{
-  struct ppc_link_hash_entry *eh;
-
-  if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
-
-  eh = (struct ppc_link_hash_entry *) h;
-  if (eh->elf.root.type != bfd_link_hash_undefweak || !eh->was_undefined)
-    return TRUE;
-
-  eh->elf.root.type = bfd_link_hash_undefined;
-  return TRUE;
-}
-
-void
-ppc64_elf_restore_symbols (struct bfd_link_info *info)
-{
-  struct ppc_link_hash_table *htab = ppc_hash_table (info);
-
-  if (htab != NULL)
-    elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info);
-}
-
 /* What to do when ld finds relocations against symbols defined in
    discarded sections.  */
 
@@ -12811,6 +14563,66 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (sec);
 }
 
+/* These are the dynamic relocations supported by glibc.  */
+
+static bfd_boolean
+ppc64_glibc_dynamic_reloc (enum elf_ppc64_reloc_type r_type)
+{
+  switch (r_type)
+    {
+    case R_PPC64_RELATIVE:
+    case R_PPC64_NONE:
+    case R_PPC64_ADDR64:
+    case R_PPC64_GLOB_DAT:
+    case R_PPC64_IRELATIVE:
+    case R_PPC64_JMP_IREL:
+    case R_PPC64_JMP_SLOT:
+    case R_PPC64_DTPMOD64:
+    case R_PPC64_DTPREL64:
+    case R_PPC64_TPREL64:
+    case R_PPC64_TPREL16_LO_DS:
+    case R_PPC64_TPREL16_DS:
+    case R_PPC64_TPREL16:
+    case R_PPC64_TPREL16_LO:
+    case R_PPC64_TPREL16_HI:
+    case R_PPC64_TPREL16_HIGH:
+    case R_PPC64_TPREL16_HA:
+    case R_PPC64_TPREL16_HIGHA:
+    case R_PPC64_TPREL16_HIGHER:
+    case R_PPC64_TPREL16_HIGHEST:
+    case R_PPC64_TPREL16_HIGHERA:
+    case R_PPC64_TPREL16_HIGHESTA:
+    case R_PPC64_ADDR16_LO_DS:
+    case R_PPC64_ADDR16_LO:
+    case R_PPC64_ADDR16_HI:
+    case R_PPC64_ADDR16_HIGH:
+    case R_PPC64_ADDR16_HA:
+    case R_PPC64_ADDR16_HIGHA:
+    case R_PPC64_REL30:
+    case R_PPC64_COPY:
+    case R_PPC64_UADDR64:
+    case R_PPC64_UADDR32:
+    case R_PPC64_ADDR32:
+    case R_PPC64_ADDR24:
+    case R_PPC64_ADDR16:
+    case R_PPC64_UADDR16:
+    case R_PPC64_ADDR16_DS:
+    case R_PPC64_ADDR16_HIGHER:
+    case R_PPC64_ADDR16_HIGHEST:
+    case R_PPC64_ADDR16_HIGHERA:
+    case R_PPC64_ADDR16_HIGHESTA:
+    case R_PPC64_ADDR14:
+    case R_PPC64_ADDR14_BRTAKEN:
+    case R_PPC64_ADDR14_BRNTAKEN:
+    case R_PPC64_REL32:
+    case R_PPC64_REL64:
+      return TRUE;
+
+    default:
+      return FALSE;
+    }
+}
+
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -12854,6 +14666,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel;
+  Elf_Internal_Rela *wrel;
   Elf_Internal_Rela *relend;
   Elf_Internal_Rela outrel;
   bfd_byte *loc;
@@ -12863,7 +14676,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   bfd_boolean is_opd;
   /* Assume 'at' branch hints.  */
   bfd_boolean is_isa_v2 = TRUE;
-  bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
+  bfd_boolean warned_dynamic = FALSE;
+  bfd_vma d_offset = (bfd_big_endian (input_bfd) ? 2 : 0);
 
   /* Initialize howto table if needed.  */
   if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
@@ -12874,10 +14688,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
     return FALSE;
 
   /* Don't relocate stub sections.  */
-  if (input_section->owner == htab->stub_bfd)
+  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);
@@ -12885,9 +14703,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   sym_hashes = elf_sym_hashes (input_bfd);
   is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
 
-  rel = relocs;
+  rel = wrel = relocs;
   relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++)
+  for (; rel < relend; wrel++, rel++)
     {
       enum elf_ppc64_reloc_type r_type;
       bfd_vma addend;
@@ -12903,7 +14721,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       unsigned char tls_mask, tls_gd, tls_type;
       unsigned char sym_type;
       bfd_vma relocation;
-      bfd_boolean unresolved_reloc;
+      bfd_boolean unresolved_reloc, save_unresolved_reloc;
       bfd_boolean warned;
       enum { DEST_NORMAL, DEST_OPD, DEST_STUB } reloc_dest;
       unsigned int insn;
@@ -12911,7 +14729,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       struct ppc_stub_hash_entry *stub_entry;
       bfd_vma max_br_offset;
       bfd_vma from;
-      const Elf_Internal_Rela orig_rel = *rel;
+      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;
 
       r_type = ELF64_R_TYPE (rel->r_info);
       r_symndx = ELF64_R_SYM (rel->r_info);
@@ -12920,10 +14745,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
         symbol of the previous ADDR64 reloc.  The symbol gives us the
         proper TOC base to use.  */
       if (rel->r_info == ELF64_R_INFO (0, R_PPC64_TOC)
-         && rel != relocs
-         && ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_ADDR64
+         && wrel != relocs
+         && ELF64_R_TYPE (wrel[-1].r_info) == R_PPC64_ADDR64
          && is_opd)
-       r_symndx = ELF64_R_SYM (rel[-1].r_info);
+       r_symndx = ELF64_R_SYM (wrel[-1].r_info);
 
       sym = NULL;
       sec = NULL;
@@ -12945,7 +14770,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          opd = get_opd_info (sec);
          if (opd != NULL && opd->adjust != NULL)
            {
-             long adjust = opd->adjust[(sym->st_value + rel->r_addend) / 8];
+             long adjust = opd->adjust[OPD_NDX (sym->st_value
+                                                + rel->r_addend)];
              if (adjust == -1)
                relocation = 0;
              else
@@ -12964,10 +14790,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
       else
        {
+         bfd_boolean ignored;
+
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h_elf, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
          sym_name = h_elf->root.root.string;
          sym_type = h_elf->type;
          if (sec != NULL
@@ -12998,21 +14826,34 @@ 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))
-       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
-                                        rel, 1, relend,
-                                        ppc64_elf_howto_table[r_type], 0,
-                                        contents);
+       {
+         _bfd_clear_contents (ppc64_elf_howto_table[r_type],
+                              input_bfd, input_section,
+                              contents, rel->r_offset);
+         wrel->r_offset = rel->r_offset;
+         wrel->r_info = 0;
+         wrel->r_addend = 0;
+
+         /* For ld -r, remove relocations in debug sections against
+            symbols defined in discarded sections.  Not done for
+            non-debug to preserve relocs in .eh_frame which the
+            eh_frame editing code expects to be present.  */
+         if (bfd_link_relocatable (info)
+             && (input_section->flags & SEC_DEBUGGING))
+           wrel--;
 
-      if (info->relocatable)
-       continue;
+         continue;
+       }
+
+      if (bfd_link_relocatable (info))
+       goto copy_reloc;
 
       if (h != NULL && &h->elf == htab->elf.hgot)
        {
-         relocation = (TOCstart
-                       + htab->stub_group[input_section->id].toc_off);
+         relocation = TOCstart + htab->sec_info[input_section->id].toc_off;
          sec = bfd_abs_section_ptr;
          unresolved_reloc = FALSE;
        }
@@ -13034,7 +14875,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            (local_plt + symtab_hdr->sh_info);
          tls_mask = lgot_masks[r_symndx];
        }
-      if (tls_mask == 0
+      if (((tls_mask & TLS_TLS) == 0 || tls_mask == (TLS_TLS | TLS_MARK))
          && (r_type == R_PPC64_TLS
              || r_type == R_PPC64_TLSGD
              || r_type == R_PPC64_TLSLD))
@@ -13057,12 +14898,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          && (h == NULL
              || h->elf.root.type == bfd_link_hash_defined
              || h->elf.root.type == bfd_link_hash_defweak)
-         && (IS_PPC64_TLS_RELOC (r_type)
-             != (sym_type == STT_TLS
-                 || (sym_type == STT_SECTION
-                     && (sec->flags & SEC_THREAD_LOCAL) != 0))))
+         && IS_PPC64_TLS_RELOC (r_type) != (sym_type == STT_TLS))
        {
-         if (tls_mask != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (r_type == R_PPC64_TLS
                  || r_type == R_PPC64_TLSGD
                  || r_type == R_PPC64_TLSLD))
@@ -13071,8 +14909,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          else
            info->callbacks->einfo
              (!IS_PPC64_TLS_RELOC (r_type)
-              ? _("%P: %H: %s used with TLS symbol `%T'\n")
-              : _("%P: %H: %s used with non-TLS symbol `%T'\n"),
+              /* xgettext:c-format */
+              ? _("%H: %s used with TLS symbol `%pT'\n")
+              /* xgettext:c-format */
+              : _("%H: %s used with non-TLS symbol `%pT'\n"),
               input_bfd, input_section, rel->r_offset,
               ppc64_elf_howto_table[r_type]->name,
               sym_name);
@@ -13097,11 +14937,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_LO_DS_OPT:
-         insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
-         if ((insn & (0x3f << 26)) != 58u << 26)
+         insn = bfd_get_32 (input_bfd, contents + rel->r_offset - d_offset);
+         if ((insn & (0x3fu << 26)) != 58u << 26)
            abort ();
          insn += (14u << 26) - (58u << 26);
-         bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+         bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset);
          r_type = R_PPC64_TOC16_LO;
          rel->r_info = ELF64_R_INFO (r_symndx, r_type);
          break;
@@ -13126,7 +14966,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (r_type == R_PPC64_TOC16_DS
                    || r_type == R_PPC64_TOC16_LO_DS)
                  {
-                   if (tls_mask != 0
+                   if ((tls_mask & TLS_TLS) != 0
                        && (tls_mask & (TLS_DTPREL | TLS_TPREL)) == 0)
                      goto toctprel;
                  }
@@ -13136,13 +14976,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                       doing a GD->IE transition.  */
                    if (retval == 2)
                      {
-                       tls_gd = TLS_TPRELGD;
-                       if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+                       tls_gd = TLS_GDIE;
+                       if ((tls_mask & TLS_TLS) != 0
+                           && (tls_mask & TLS_GD) == 0)
                          goto tls_ldgd_opt;
                      }
                    else if (retval == 3)
                      {
-                       if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+                       if ((tls_mask & TLS_TLS) != 0
+                           && (tls_mask & TLS_LD) == 0)
                          goto tls_ldgd_opt;
                      }
                  }
@@ -13152,11 +14994,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT_TPREL16_HI:
        case R_PPC64_GOT_TPREL16_HA:
-         if (tls_mask != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
              rel->r_offset -= d_offset;
-             bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+             bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
              r_type = R_PPC64_NONE;
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
            }
@@ -13164,14 +15006,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT_TPREL16_DS:
        case R_PPC64_GOT_TPREL16_LO_DS:
-         if (tls_mask != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
            toctprel:
-             insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+             insn = bfd_get_32 (input_bfd,
+                                contents + rel->r_offset - d_offset);
              insn &= 31 << 21;
              insn |= 0x3c0d0000;       /* addis 0,13,0 */
-             bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+             bfd_put_32 (input_bfd, insn,
+                         contents + rel->r_offset - d_offset);
              r_type = R_PPC64_TPREL16_HA;
              if (toc_symndx != 0)
                {
@@ -13179,51 +15023,92 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  rel->r_addend = toc_addend;
                  /* We changed the symbol.  Start over in order to
                     get h, sym, sec etc. right.  */
-                 rel--;
-                 continue;
+                 goto again;
                }
              else
                rel->r_info = ELF64_R_INFO (r_symndx, r_type);
            }
          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 != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
-             insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
+             insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
              insn = _bfd_elf_ppc_at_tls_transform (insn, 13);
              if (insn == 0)
-               abort ();
-             bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
-             /* Was PPC64_TLS which sits on insn boundary, now
-                PPC64_TPREL16_LO which is at low-order half-word.  */
-             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.  */
-                 rel--;
-                 continue;
+                 bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+                 /* Was PPC64_TLS which sits on insn boundary, now
+                    PPC64_TPREL16_LO which is at low-order half-word.  */
+                 rel->r_offset += d_offset;
+                 r_type = R_PPC64_TPREL16_LO;
+                 if (toc_symndx != 0)
+                   {
+                     rel->r_info = ELF64_R_INFO (toc_symndx, r_type);
+                     rel->r_addend = toc_addend;
+                     /* We changed the symbol.  Start over in order to
+                        get h, sym, sec etc. right.  */
+                     goto again;
+                   }
+                 else
+                   rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+             else if ((rel->r_offset & 3) == 1)
+               {
+                 /* For pcrel IE to LE we already have the full
+                    offset and thus don't need an addi here.  A nop
+                    or mr will do.  */
+                 if ((insn & (0x3fu << 26)) == 14 << 26)
+                   {
+                     /* Extract regs from addi rt,ra,si.  */
+                     unsigned int rt = (insn >> 21) & 0x1f;
+                     unsigned int ra = (insn >> 16) & 0x1f;
+                     if (rt == ra)
+                       insn = NOP;
+                     else
+                       {
+                         /* Build or ra,rs,rb with rb==rs, ie. mr ra,rs.  */
+                         insn = (rt << 16) | (ra << 21) | (ra << 11);
+                         insn |= (31u << 26) | (444u << 1);
+                       }
+                   }
+                 bfd_put_32 (input_bfd, insn, contents + rel->r_offset - 1);
                }
-             else
-               rel->r_info = ELF64_R_INFO (r_symndx, r_type);
            }
          break;
 
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
-         tls_gd = TLS_TPRELGD;
-         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+         tls_gd = TLS_GDIE;
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_gdld_hi;
          break;
 
        case R_PPC64_GOT_TLSLD16_HI:
        case R_PPC64_GOT_TLSLD16_HA:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
            tls_gdld_hi:
              if ((tls_mask & tls_gd) != 0)
@@ -13232,7 +15117,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              else
                {
                  rel->r_offset -= d_offset;
-                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+                 bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
                  r_type = R_PPC64_NONE;
                }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
@@ -13241,17 +15126,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT_TLSGD16:
        case R_PPC64_GOT_TLSGD16_LO:
-         tls_gd = TLS_TPRELGD;
-         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+         tls_gd = TLS_GDIE;
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_ldgd_opt;
          break;
 
        case R_PPC64_GOT_TLSLD16:
        case R_PPC64_GOT_TLSLD16_LO:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
-             unsigned int insn1, insn2, insn3;
-             bfd_vma offset;
+             unsigned int insn1, insn2;
 
            tls_ldgd_opt:
              offset = (bfd_vma) -1;
@@ -13260,51 +15144,47 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                 stays with its arg setup insns, ie. that the next
                 reloc is the __tls_get_addr call associated with
                 the current reloc.  Edit both insns.  */
-             if (input_section->has_tls_get_addr_call
+             if (input_section->nomark_tls_get_addr
                  && rel + 1 < relend
                  && branch_reloc_hash_match (input_bfd, rel + 1,
+                                             htab->tls_get_addr_fd,
+                                             htab->tga_desc_fd,
                                              htab->tls_get_addr,
-                                             htab->tls_get_addr_fd))
+                                             htab->tga_desc))
                offset = rel[1].r_offset;
+             /* We read the low GOT_TLS (or TOC16) insn because we
+                need to keep the destination reg.  It may be
+                something other than the usual r3, and moved to r3
+                before the call by intervening code.  */
+             insn1 = bfd_get_32 (input_bfd,
+                                 contents + rel->r_offset - d_offset);
              if ((tls_mask & tls_gd) != 0)
                {
                  /* IE */
-                 insn1 = bfd_get_32 (output_bfd,
-                                     contents + rel->r_offset - d_offset);
-                 insn1 &= (1 << 26) - (1 << 2);
-                 insn1 |= 58 << 26;    /* ld */
+                 insn1 &= (0x1f << 21) | (0x1f << 16);
+                 insn1 |= 58u << 26;   /* ld */
                  insn2 = 0x7c636a14;   /* add 3,3,13 */
                  if (offset != (bfd_vma) -1)
                    rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
-                 if ((tls_mask & TLS_EXPLICIT) == 0)
-                   r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3)
-                             + R_PPC64_GOT_TPREL16_DS);
-                 else
+                 if (r_type == R_PPC64_TOC16
+                     || r_type == R_PPC64_TOC16_LO)
                    r_type += R_PPC64_TOC16_DS - R_PPC64_TOC16;
+                 else
+                   r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 1)) & 1)
+                             + R_PPC64_GOT_TPREL16_DS);
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                }
              else
                {
                  /* LE */
-                 insn1 = 0x3c6d0000;   /* addis 3,13,0 */
+                 insn1 &= 0x1f << 21;
+                 insn1 |= 0x3c0d0000;  /* addis r,13,0 */
                  insn2 = 0x38630000;   /* addi 3,3,0 */
                  if (tls_gd == 0)
                    {
                      /* Was an LD reloc.  */
-                     if (toc_symndx)
-                       sec = local_sections[toc_symndx];
-                     for (r_symndx = 0;
-                          r_symndx < symtab_hdr->sh_info;
-                          r_symndx++)
-                       if (local_sections[r_symndx] == sec)
-                         break;
-                     if (r_symndx >= symtab_hdr->sh_info)
-                       r_symndx = STN_UNDEF;
+                     r_symndx = STN_UNDEF;
                      rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-                     if (r_symndx != STN_UNDEF)
-                       rel->r_addend -= (local_syms[r_symndx].st_value
-                                         + sec->output_offset
-                                         + sec->output_section->vma);
                    }
                  else if (toc_symndx != 0)
                    {
@@ -13321,39 +15201,95 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      rel[1].r_addend = rel->r_addend;
                    }
                }
-             bfd_put_32 (output_bfd, insn1,
+             bfd_put_32 (input_bfd, insn1,
                          contents + rel->r_offset - d_offset);
              if (offset != (bfd_vma) -1)
                {
-                 insn3 = bfd_get_32 (output_bfd,
-                                     contents + offset + 4);
-                 if (insn3 == NOP
-                     || insn3 == CROR_151515 || insn3 == CROR_313131)
+                 bfd_put_32 (input_bfd, insn2, contents + offset);
+                 if (offset + 8 <= input_section->size)
                    {
-                     rel[1].r_offset += 4;
-                     bfd_put_32 (output_bfd, insn2, contents + offset + 4);
-                     insn2 = NOP;
+                     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);
                    }
-                 bfd_put_32 (output_bfd, insn2, contents + offset);
                }
              if ((tls_mask & tls_gd) == 0
                  && (tls_gd == 0 || toc_symndx != 0))
                {
                  /* We changed the symbol.  Start over in order
                     to get h, sym, sec etc. right.  */
-                 rel--;
-                 continue;
+                 goto again;
+               }
+           }
+         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 != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
+             && rel + 1 < relend)
            {
-             unsigned int insn2, insn3;
-             bfd_vma offset = rel->r_offset;
+             unsigned int insn2;
+             enum elf_ppc64_reloc_type r_type1 = 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);
+                 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;
+               }
 
-             if ((tls_mask & TLS_TPRELGD) != 0)
+             if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+               bfd_put_32 (output_bfd, NOP, contents + offset + 4);
+
+             if ((tls_mask & TLS_GDIE) != 0)
                {
                  /* IE */
                  r_type = R_PPC64_NONE;
@@ -13367,72 +15303,73 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      r_symndx = toc_symndx;
                      rel->r_addend = toc_addend;
                    }
-                 r_type = R_PPC64_TPREL16_LO;
-                 rel->r_offset = offset + d_offset;
-                 insn2 = 0x38630000;   /* addi 3,3,0 */
+                 if (r_type1 == R_PPC64_REL24_NOTOC
+                     || r_type1 == R_PPC64_PLTCALL_NOTOC)
+                   {
+                     r_type = R_PPC64_NONE;
+                     insn2 = NOP;
+                   }
+                 else
+                   {
+                     rel->r_offset = offset + d_offset;
+                     r_type = R_PPC64_TPREL16_LO;
+                     insn2 = 0x38630000;       /* addi 3,3,0 */
+                   }
                }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
              /* Zap the reloc on the _tls_get_addr call too.  */
              BFD_ASSERT (offset == rel[1].r_offset);
              rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
-             insn3 = bfd_get_32 (output_bfd,
-                                 contents + offset + 4);
-             if (insn3 == NOP
-                 || insn3 == CROR_151515 || insn3 == CROR_313131)
-               {
-                 rel->r_offset += 4;
-                 bfd_put_32 (output_bfd, insn2, contents + offset + 4);
-                 insn2 = NOP;
-               }
-             bfd_put_32 (output_bfd, insn2, contents + offset);
-             if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
-               {
-                 rel--;
-                 continue;
-               }
+             bfd_put_32 (input_bfd, insn2, contents + offset);
+             if ((tls_mask & TLS_GDIE) == 0
+                 && toc_symndx != 0
+                 && r_type != R_PPC64_NONE)
+               goto again;
            }
          break;
 
        case R_PPC64_TLSLD:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
+             && rel + 1 < relend)
            {
-             unsigned int insn2, insn3;
-             bfd_vma offset = rel->r_offset;
+             unsigned int insn2;
+             enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
-             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)
+             offset = rel->r_offset;
+             if (is_plt_seq_reloc (r_type1))
+               {
+                 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;
-             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 (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+               bfd_put_32 (output_bfd, NOP, contents + offset + 4);
+
+             if (r_type1 == R_PPC64_REL24_NOTOC
+                 || r_type1 == R_PPC64_PLTCALL_NOTOC)
+               {
+                 r_type = R_PPC64_NONE;
+                 insn2 = NOP;
+               }
+             else
+               {
+                 rel->r_offset = offset + d_offset;
+                 r_symndx = STN_UNDEF;
+                 r_type = R_PPC64_TPREL16_LO;
+                 rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
+                 insn2 = 0x38630000;   /* addi 3,3,0 */
+               }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
-             rel->r_offset = offset + d_offset;
              /* Zap the reloc on the _tls_get_addr call too.  */
              BFD_ASSERT (offset == rel[1].r_offset);
              rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
-             insn2 = 0x38630000;       /* addi 3,3,0 */
-             insn3 = bfd_get_32 (output_bfd,
-                                 contents + offset + 4);
-             if (insn3 == NOP
-                 || insn3 == CROR_151515 || insn3 == CROR_313131)
-               {
-                 rel->r_offset += 4;
-                 bfd_put_32 (output_bfd, insn2, contents + offset + 4);
-                 insn2 = NOP;
-               }
-             bfd_put_32 (output_bfd, insn2, contents + offset);
-             rel--;
-             continue;
+             bfd_put_32 (input_bfd, insn2, contents + offset);
+             if (r_type != R_PPC64_NONE)
+               goto again;
            }
          break;
 
@@ -13444,7 +15381,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if ((tls_mask & TLS_GD) == 0)
                {
                  rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE);
-                 if ((tls_mask & TLS_TPRELGD) != 0)
+                 if ((tls_mask & TLS_GDIE) != 0)
                    r_type = R_PPC64_TPREL64;
                  else
                    {
@@ -13473,6 +15410,52 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            }
          break;
 
+       case R_PPC64_ENTRY:
+         relocation = TOCstart + htab->sec_info[input_section->id].toc_off;
+         if (!bfd_link_pic (info)
+             && !info->traditional_format
+             && relocation + 0x80008000 <= 0xffffffff)
+           {
+             unsigned int insn1, insn2;
+
+             insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             if ((insn1 & ~0xfffc) == LD_R2_0R12
+                 && insn2 == ADD_R2_R2_R12)
+               {
+                 bfd_put_32 (input_bfd,
+                             LIS_R2 + PPC_HA (relocation),
+                             contents + rel->r_offset);
+                 bfd_put_32 (input_bfd,
+                             ADDI_R2_R2 + PPC_LO (relocation),
+                             contents + rel->r_offset + 4);
+               }
+           }
+         else
+           {
+             relocation -= (rel->r_offset
+                            + input_section->output_offset
+                            + input_section->output_section->vma);
+             if (relocation + 0x80008000 <= 0xffffffff)
+               {
+                 unsigned int insn1, insn2;
+
+                 insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+                 insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+                 if ((insn1 & ~0xfffc) == LD_R2_0R12
+                     && insn2 == ADD_R2_R2_R12)
+                   {
+                     bfd_put_32 (input_bfd,
+                                 ADDIS_R2_R12 + PPC_HA (relocation),
+                                 contents + rel->r_offset);
+                     bfd_put_32 (input_bfd,
+                                 ADDI_R2_R2 + PPC_LO (relocation),
+                                 contents + rel->r_offset + 4);
+                   }
+               }
+           }
+         break;
+
        case R_PPC64_REL16_HA:
          /* If we are generating a non-PIC executable, edit
             .  0:      addis 2,12,.TOC.-0b@ha
@@ -13481,7 +15464,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             .          lis 2,.TOC.@ha
             .          addi 2,2,.TOC.@l
             if .TOC. is in range.  */
-         if (!info->shared
+         if (!bfd_link_pic (info)
+             && !info->traditional_format
+             && !htab->opd_abi
+             && rel->r_addend == d_offset
              && h != NULL && &h->elf == htab->elf.hgot
              && rel + 1 < relend
              && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
@@ -13490,18 +15476,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              && relocation + 0x80008000 <= 0xffffffff)
            {
              unsigned int insn1, insn2;
-             bfd_vma offset = rel->r_offset - d_offset;
-             insn1 = bfd_get_32 (output_bfd, contents + offset);
-             insn2 = bfd_get_32 (output_bfd, contents + offset + 4);
-             if ((insn1 & 0xffff0000) == 0x3c4c0000 /* addis 2,12 */
-                 && (insn2 & 0xffff0000) == 0x38420000 /* addi 2,2 */)
+             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
+                 && (insn2 & 0xffff0000) == ADDI_R2_R2)
                {
                  r_type = R_PPC64_ADDR16_HA;
                  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                  rel->r_addend -= d_offset;
                  rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_ADDR16_LO);
                  rel[1].r_addend -= d_offset + 4;
-                 bfd_put_32 (output_bfd, 0x3c400000, contents + offset);
+                 bfd_put_32 (input_bfd, LIS_R2, contents + offset);
                }
            }
          break;
@@ -13537,20 +15523,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_ADDR14_BRTAKEN:
        case R_PPC64_REL14_BRTAKEN:
          insn = 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field.  */
-         /* Fall thru.  */
+         /* Fall through.  */
 
          /* Branch not taken prediction relocations.  */
        case R_PPC64_ADDR14_BRNTAKEN:
        case R_PPC64_REL14_BRNTAKEN:
-         insn |= bfd_get_32 (output_bfd,
+         insn |= bfd_get_32 (input_bfd,
                              contents + rel->r_offset) & ~(0x01 << 21);
-         /* Fall thru.  */
+         /* Fall through.  */
 
        case R_PPC64_REL14:
          max_br_offset = 1 << 15;
-         /* Fall thru.  */
+         /* Fall through.  */
 
        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
@@ -13564,18 +15553,41 @@ 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);
+         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;
+
          if (stub_entry != NULL
-             && (stub_entry->stub_type == ppc_stub_plt_call
-                 || stub_entry->stub_type == ppc_stub_plt_call_r2save
+             && ((stub_entry->stub_type >= ppc_stub_plt_call
+                  && stub_entry->stub_type <= ppc_stub_plt_call_both)
                  || stub_entry->stub_type == ppc_stub_plt_branch_r2off
-                 || stub_entry->stub_type == ppc_stub_long_branch_r2off))
+                 || stub_entry->stub_type == ppc_stub_plt_branch_both
+                 || stub_entry->stub_type == ppc_stub_long_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_long_branch_both))
            {
              bfd_boolean can_plt_call = FALSE;
 
-             /* All of these stubs will modify r2, so there must be a
+             if (stub_entry->stub_type == ppc_stub_plt_call
+                 && !htab->opd_abi
+                 && htab->params->plt_localentry0 != 0
+                 && is_elfv2_localentry0 (&h->elf))
+               {
+                 /* The function doesn't use or change r2.  */
+                 can_plt_call = TRUE;
+               }
+             else if (r_type == R_PPC64_REL24_NOTOC)
+               {
+                 /* NOTOC calls don't need to restore r2.  */
+                 can_plt_call = TRUE;
+               }
+
+             /* All of these stubs may modify r2, so there must be a
                 branch and link followed by a nop.  The nop is
                 replaced by an insn to restore r2.  */
-             if (rel->r_offset + 8 <= input_section->size)
+             else if (rel->r_offset + 8 <= input_section->size)
                {
                  unsigned long br;
 
@@ -13587,13 +15599,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
                      nop = bfd_get_32 (input_bfd,
                                        contents + rel->r_offset + 4);
-                     if (nop == NOP
-                         || nop == CROR_151515 || nop == CROR_313131)
+                     if (nop == LD_R2_0R1 + STK_TOC (htab))
+                       can_plt_call = TRUE;
+                     else if (nop == NOP
+                              || nop == CROR_151515
+                              || nop == CROR_313131)
                        {
                          if (h != NULL
-                             && (h == htab->tls_get_addr_fd
-                                 || h == htab->tls_get_addr)
-                             && !htab->no_tls_get_addr_opt)
+                             && is_tls_get_addr (&h->elf, htab)
+                             && htab->params->tls_get_addr_opt)
                            {
                              /* Special stub used, leave nop alone.  */
                            }
@@ -13649,18 +15663,27 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (!can_plt_call)
                {
-                 info->callbacks->einfo
-                   (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
-                      "recompile with -fPIC"),
-                    input_bfd, input_section, rel->r_offset, sym_name);
+                 if (stub_entry->stub_type >= ppc_stub_plt_call
+                     && stub_entry->stub_type <= ppc_stub_plt_call_both)
+                   info->callbacks->einfo
+                     /* xgettext:c-format */
+                     (_("%H: call to `%pT' lacks nop, can't restore toc; "
+                        "(plt call stub)\n"),
+                      input_bfd, input_section, rel->r_offset, sym_name);
+                 else
+                   info->callbacks->einfo
+                     /* xgettext:c-format */
+                     (_("%H: call to `%pT' lacks nop, can't restore toc; "
+                        "(toc save/adjust stub)\n"),
+                      input_bfd, input_section, rel->r_offset, sym_name);
 
                  bfd_set_error (bfd_error_bad_value);
                  ret = FALSE;
                }
 
              if (can_plt_call
-                 && (stub_entry->stub_type == ppc_stub_plt_call
-                     || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+                 && stub_entry->stub_type >= ppc_stub_plt_call
+                 && stub_entry->stub_type <= ppc_stub_plt_call_both)
                unresolved_reloc = FALSE;
            }
 
@@ -13702,24 +15725,68 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            /* Don't use the stub if this branch is in range.  */
            stub_entry = NULL;
 
+         if (stub_entry != NULL
+             && (stub_entry->stub_type == ppc_stub_long_branch_notoc
+                 || stub_entry->stub_type == ppc_stub_long_branch_both
+                 || stub_entry->stub_type == ppc_stub_plt_branch_notoc
+                 || 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)
+             && (relocation + addend - from + max_br_offset
+                 < 2 * max_br_offset))
+           stub_entry = NULL;
+
+         if (stub_entry != NULL
+             && (stub_entry->stub_type == ppc_stub_long_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_long_branch_both
+                 || stub_entry->stub_type == ppc_stub_plt_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_plt_branch_both)
+             && r_type == R_PPC64_REL24_NOTOC
+             && (relocation + addend - from + max_br_offset
+                 < 2 * max_br_offset))
+           stub_entry = NULL;
+
          if (stub_entry != NULL)
            {
              /* Munge up the value and addend so that we call the stub
                 rather than the procedure directly.  */
-             relocation = (stub_entry->stub_offset
-                           + stub_entry->stub_sec->output_offset
-                           + stub_entry->stub_sec->output_section->vma);
+             asection *stub_sec = stub_entry->group->stub_sec;
+
+             if (stub_entry->stub_type == ppc_stub_save_res)
+               relocation += (stub_sec->output_offset
+                              + stub_sec->output_section->vma
+                              + stub_sec->size - htab->sfpr->size
+                              - htab->sfpr->output_offset
+                              - htab->sfpr->output_section->vma);
+             else
+               relocation = (stub_entry->stub_offset
+                             + stub_sec->output_offset
+                             + stub_sec->output_section->vma);
              addend = 0;
              reloc_dest = DEST_STUB;
 
-             if ((stub_entry->stub_type == ppc_stub_plt_call
-                  || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-                 && (ALWAYS_EMIT_R2SAVE
-                     || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+             if (((stub_entry->stub_type == ppc_stub_plt_call
+                   && ALWAYS_EMIT_R2SAVE)
+                  || stub_entry->stub_type == ppc_stub_plt_call_r2save
+                  || stub_entry->stub_type == ppc_stub_plt_call_both)
+                 && !(h != NULL
+                      && is_tls_get_addr (&h->elf, htab)
+                      && htab->params->tls_get_addr_opt)
                  && rel + 1 < relend
                  && rel[1].r_offset == rel->r_offset + 4
                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
                relocation += 4;
+             else if ((stub_entry->stub_type == ppc_stub_long_branch_both
+                       || stub_entry->stub_type == ppc_stub_plt_branch_both
+                       || stub_entry->stub_type == ppc_stub_plt_call_both)
+                      && r_type == R_PPC64_REL24_NOTOC)
+               relocation += 4;
+
+             if (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)
@@ -13743,7 +15810,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    insn ^= 0x01 << 21;
                }
 
-             bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
            }
 
          /* NOP out calls to undefined weak functions.
@@ -13752,28 +15819,152 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          else if (h != NULL
                   && h->elf.root.type == bfd_link_hash_undefweak
                   && h->elf.dynindx == -1
-                  && r_type == R_PPC64_REL24
+                  && (r_type == R_PPC64_REL24
+                      || r_type == R_PPC64_REL24_NOTOC)
                   && relocation == 0
                   && addend == 0)
            {
-             bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
-             continue;
+             bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
+             goto copy_reloc;
+           }
+         break;
+
+       case R_PPC64_GOT16_DS:
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+           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)
+           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)
+           break;
+         from = (rel->r_offset
+                 + input_section->output_section->vma
+                 + input_section->output_offset);
+         if (relocation - from + (1ULL << 33) < 1ULL << 34
+             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+           {
+             offset = rel->r_offset;
+             pinsn = bfd_get_32 (input_bfd, contents + offset);
+             pinsn <<= 32;
+             pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
+             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+                  == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+               {
+                 /* Replace with paddi.  */
+                 pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+                 r_type = R_PPC64_PCREL34;
+                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+                 bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
+                 bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
+                 goto pcrelopt;
+               }
+           }
+         break;
+
+       case R_PPC64_PCREL34:
+         if (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 */))
+               {
+               pcrelopt:
+                 if (rel + 1 < relend
+                     && rel[1].r_offset == offset
+                     && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT))
+                   {
+                     bfd_vma off2 = rel[1].r_addend;
+                     if (off2 == 0)
+                       /* zero means next insn.  */
+                       off2 = 8;
+                     off2 += offset;
+                     if (off2 + 4 <= input_section->size)
+                       {
+                         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)
        {
        default:
-         info->callbacks->einfo
-           (_("%P: %B: unknown relocation type %d for `%T'\n"),
-            input_bfd, (int) r_type, sym_name);
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%pB: %s unsupported"),
+                             input_bfd, ppc64_elf_howto_table[r_type]->name);
 
          bfd_set_error (bfd_error_bad_value);
          ret = FALSE;
-         continue;
+         goto copy_reloc;
 
        case R_PPC64_NONE:
        case R_PPC64_TLS:
@@ -13782,7 +15973,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TOCSAVE:
        case R_PPC64_GNU_VTINHERIT:
        case R_PPC64_GNU_VTENTRY:
-         continue;
+       case R_PPC64_ENTRY:
+       case R_PPC64_PCREL_OPT:
+         goto copy_reloc;
 
          /* GOT16 relocations.  Like an ADDR16 using the symbol's
             address in the GOT as relocation value instead of the
@@ -13792,6 +15985,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TLSGD16_LO:
        case R_PPC64_GOT_TLSGD16_HI:
        case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TLSGD_PCREL34:
          tls_type = TLS_TLS | TLS_GD;
          goto dogot;
 
@@ -13799,6 +15993,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TLSLD16_LO:
        case R_PPC64_GOT_TLSLD16_HI:
        case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TLSLD_PCREL34:
          tls_type = TLS_TLS | TLS_LD;
          goto dogot;
 
@@ -13806,6 +16001,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_TPREL16_LO_DS:
        case R_PPC64_GOT_TPREL16_HI:
        case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_TPREL_PCREL34:
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogot;
 
@@ -13813,6 +16009,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_DTPREL16_LO_DS:
        case R_PPC64_GOT_DTPREL16_HI:
        case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_GOT_DTPREL_PCREL34:
          tls_type = TLS_TLS | TLS_DTPREL;
          goto dogot;
 
@@ -13822,6 +16019,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_GOT_PCREL34:
        dogot:
          {
            /* Relocation is to the entry for this symbol in the global
@@ -13833,19 +16031,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            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
              {
-
                if (h != NULL)
                  {
-                   bfd_boolean dyn = htab->elf.dynamic_sections_created;
-                   if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared,
-                                                         &h->elf)
-                       || (info->shared
-                           && SYMBOL_CALLS_LOCAL (info, &h->elf)))
+                   if (!htab->elf.dynamic_sections_created
+                       || h->elf.dynindx == -1
+                       || SYMBOL_REFERENCES_LOCAL (info, &h->elf)
+                       || UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf))
                      /* This is actually a static link, or it is a
                         -Bsymbolic link and the symbol is defined
                         locally, or the symbol was forced to be local
@@ -13853,7 +16048,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      ;
                    else
                      {
-                       BFD_ASSERT (h->elf.dynindx != -1);
                        indx = h->elf.dynindx;
                        unresolved_reloc = FALSE;
                      }
@@ -13902,20 +16096,25 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                         ? h->elf.type == STT_GNU_IFUNC
                         : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
                if (ifunc)
-                 relgot = htab->elf.irelplt;
-               else if ((info->shared || indx != 0)
-                        && (h == NULL
-                            || (tls_type == (TLS_TLS | TLS_LD)
-                                && !h->elf.def_dynamic)
-                            || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
-                            || h->elf.root.type != bfd_link_hash_undefweak))
+                 {
+                   relgot = htab->elf.irelplt;
+                   if (indx == 0 || is_static_defined (&h->elf))
+                     htab->elf.ifunc_resolvers = TRUE;
+                 }
+               else if (indx != 0
+                        || (bfd_link_pic (info)
+                            && (h == NULL
+                                || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf))
+                            && !(tls_type != 0
+                                 && bfd_link_executable (info)
+                                 && SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
                  relgot = ppc64_elf_tdata (ent->owner)->relgot;
                if (relgot != NULL)
                  {
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
                                       + off);
-                   outrel.r_addend = addend;
+                   outrel.r_addend = orig_rel.r_addend;
                    if (tls_type & (TLS_LD | TLS_GD))
                      {
                        outrel.r_addend = 0;
@@ -13928,7 +16127,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                            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);
                          }
@@ -13957,7 +16156,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      {
                        outrel.r_addend += relocation;
                        if (tls_type & (TLS_GD | TLS_DTPREL | TLS_TPREL))
-                         outrel.r_addend -= htab->elf.tls_sec->vma;
+                         {
+                           if (htab->elf.tls_sec == NULL)
+                             outrel.r_addend = 0;
+                           else
+                             outrel.r_addend -= htab->elf.tls_sec->vma;
+                         }
                      }
                    loc = relgot->contents;
                    loc += (relgot->reloc_count++
@@ -13969,23 +16173,28 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   emitting a reloc.  */
                else
                  {
-                   relocation += addend;
-                   if (tls_type == (TLS_TLS | TLS_LD))
-                     relocation = 1;
-                   else if (tls_type != 0)
+                   relocation += orig_rel.r_addend;
+                   if (tls_type != 0)
                      {
-                       relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
-                       if (tls_type == (TLS_TLS | TLS_TPREL))
-                         relocation += DTP_OFFSET - TP_OFFSET;
+                       if (htab->elf.tls_sec == NULL)
+                         relocation = 0;
+                       else
+                         {
+                           if (tls_type & TLS_LD)
+                             relocation = 0;
+                           else
+                             relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
+                           if (tls_type & TLS_TPREL)
+                             relocation += DTP_OFFSET - TP_OFFSET;
+                         }
 
-                       if (tls_type == (TLS_TLS | TLS_GD))
+                       if (tls_type & (TLS_GD | TLS_LD))
                          {
                            bfd_put_64 (output_bfd, relocation,
                                        got->contents + off + 8);
                            relocation = 1;
                          }
                      }
-
                    bfd_put_64 (output_bfd, relocation,
                                got->contents + off);
                  }
@@ -13995,52 +16204,93 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              abort ();
 
            relocation = got->output_section->vma + got->output_offset + off;
-           addend = -(TOCstart + htab->stub_group[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;
 
        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:
+       case R_PPC64_PLTSEQ:
+       case R_PPC64_PLTSEQ_NOTOC:
+       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;
+         {
+           struct plt_entry **plt_list = NULL;
+           if (h != NULL)
+             plt_list = &h->elf.plt.plist;
+           else if (local_got_ents != NULL)
+             {
+               struct plt_entry **local_plt = (struct plt_entry **)
+                 (local_got_ents + symtab_hdr->sh_info);
+               plt_list = local_plt + r_symndx;
+             }
+           if (plt_list)
+             {
+               struct plt_entry *ent;
 
-         /* Resolve a PLT reloc against a local symbol directly,
-            without using the procedure linkage table.  */
-         if (h == NULL)
-           break;
+               for (ent = *plt_list; ent != NULL; ent = ent->next)
+                 if (ent->plt.offset != (bfd_vma) -1
+                     && ent->addend == orig_rel.r_addend)
+                   {
+                     asection *plt;
+                     bfd_vma got;
 
-         /* It's possible that we didn't make a PLT entry for this
-            symbol.  This happens when statically linking PIC code,
-            or when using -Bsymbolic.  Go find a match if there is a
-            PLT entry.  */
-         if (htab->elf.splt != NULL)
-           {
-             struct plt_entry *ent;
-             for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next)
-               if (ent->plt.offset != (bfd_vma) -1
-                   && ent->addend == orig_rel.r_addend)
-                 {
-                   relocation = (htab->elf.splt->output_section->vma
-                                 + htab->elf.splt->output_offset
-                                 + ent->plt.offset);
-                   unresolved_reloc = FALSE;
-                   break;
-                 }
-           }
+                     plt = htab->elf.splt;
+                     if (!htab->elf.dynamic_sections_created
+                         || h == NULL
+                         || h->elf.dynindx == -1)
+                       {
+                         if (h != NULL
+                             ? h->elf.type == STT_GNU_IFUNC
+                             : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                           plt = htab->elf.iplt;
+                         else
+                           plt = htab->pltlocal;
+                       }
+                     relocation = (plt->output_section->vma
+                                   + plt->output_offset
+                                   + ent->plt.offset);
+                     if (r_type == R_PPC64_PLT16_HA
+                         || r_type == R_PPC64_PLT16_HI
+                         || r_type == R_PPC64_PLT16_LO
+                         || r_type == R_PPC64_PLT16_LO_DS)
+                       {
+                         got = (elf_gp (output_bfd)
+                                + htab->sec_info[input_section->id].toc_off);
+                         relocation -= got;
+                       }
+                     addend = 0;
+                     unresolved_reloc = FALSE;
+                     break;
+                   }
+             }
+         }
          break;
 
        case R_PPC64_TOC:
          /* Relocation value is TOC base.  */
          relocation = TOCstart;
          if (r_symndx == STN_UNDEF)
-           relocation += htab->stub_group[input_section->id].toc_off;
+           relocation += htab->sec_info[input_section->id].toc_off;
          else if (unresolved_reloc)
            ;
-         else if (sec != NULL && sec->id <= htab->top_id)
-           relocation += htab->stub_group[sec->id].toc_off;
+         else if (sec != NULL && sec->id < htab->sec_info_arr_size)
+           relocation += htab->sec_info[sec->id].toc_off;
          else
            unresolved_reloc = TRUE;
          goto dodyn;
@@ -14055,7 +16305,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TOC16_DS:
        case R_PPC64_TOC16_LO_DS:
        case R_PPC64_TOC16_HA:
-         addend -= TOCstart + htab->stub_group[input_section->id].toc_off;
+         addend -= TOCstart + htab->sec_info[input_section->id].toc_off;
+         if (h != NULL)
+           goto dodyn;
          break;
 
          /* Relocate against the beginning of the section.  */
@@ -14073,12 +16325,24 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_REL16_LO:
        case R_PPC64_REL16_HI:
        case R_PPC64_REL16_HA:
-         break;
-
+       case R_PPC64_REL16_HIGH:
+       case R_PPC64_REL16_HIGHA:
+       case R_PPC64_REL16_HIGHER:
+       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_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:
@@ -14093,6 +16357,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TPREL16_HIGHERA:
        case R_PPC64_TPREL16_HIGHEST:
        case R_PPC64_TPREL16_HIGHESTA:
+       case R_PPC64_TPREL34:
          if (h != NULL
              && h->elf.root.type == bfd_link_hash_undefweak
              && h->elf.dynindx == -1)
@@ -14103,19 +16368,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                 defined before using them.  */
              bfd_byte *p = contents + rel->r_offset - d_offset;
 
-             insn = bfd_get_32 (output_bfd, p);
+             insn = bfd_get_32 (input_bfd, p);
              insn = _bfd_elf_ppc_at_tprel_transform (insn, 13);
              if (insn != 0)
-               bfd_put_32 (output_bfd, insn, p);
+               bfd_put_32 (input_bfd, insn, p);
              break;
            }
-         addend -= htab->elf.tls_sec->vma + TP_OFFSET;
-         if (info->shared)
-           /* The TPREL16 relocs shouldn't really be used in shared
-              libs as they will result in DT_TEXTREL being set, but
-              support them anyway.  */
-           goto dodyn;
-         break;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+         /* The TPREL16 relocs shouldn't really be used in shared
+            libs or with non-local symbols as that will result in
+            DT_TEXTREL being set, but support them anyway.  */
+         goto dodyn;
 
        case R_PPC64_DTPREL16:
        case R_PPC64_DTPREL16_LO:
@@ -14129,7 +16393,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_DTPREL16_HIGHERA:
        case R_PPC64_DTPREL16_HIGHEST:
        case R_PPC64_DTPREL16_HIGHESTA:
-         addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+       case R_PPC64_DTPREL34:
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+         break;
+
+       case R_PPC64_ADDR64_LOCAL:
+         addend += PPC64_LOCAL_ENTRY_OFFSET (h != NULL
+                                             ? h->elf.other
+                                             : sym->st_other);
          break;
 
        case R_PPC64_DTPMOD64:
@@ -14138,12 +16410,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          goto dodyn;
 
        case R_PPC64_TPREL64:
-         addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + TP_OFFSET;
          goto dodyn;
 
        case R_PPC64_DTPREL64:
-         addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
-         /* Fall thru */
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+         /* Fall through.  */
 
          /* Relocations that may need to be propagated if this is a
             dynamic object.  */
@@ -14165,12 +16439,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_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_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;
@@ -14178,26 +16461,19 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (NO_OPD_RELOCS && is_opd)
            break;
 
-         if ((info->shared
-              && (h == NULL
-                  || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
-                  || h->elf.root.type != bfd_link_hash_undefweak)
-              && (must_be_dyn_reloc (info, r_type)
-                  || !SYMBOL_CALLS_LOCAL (info, &h->elf)))
-             || (ELIMINATE_COPY_RELOCS
-                 && !info->shared
-                 && h != NULL
-                 && h->elf.dynindx != -1
-                 && !h->elf.non_got_ref
-                 && !h->elf.def_regular)
-             || (!info->shared
-                 && (h != NULL
-                     ? h->elf.type == STT_GNU_IFUNC
-                     : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
+         if (bfd_link_pic (info)
+             ? ((h == NULL
+                 || h->elf.dyn_relocs != NULL)
+                && ((h != NULL && pc_dynrelocs (h))
+                    || must_be_dyn_reloc (info, r_type)))
+             : (h != NULL
+                ? h->elf.dyn_relocs != NULL
+                : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
            {
              bfd_boolean skip, relocate;
              asection *sreloc;
              bfd_vma out_off;
+             long indx = 0;
 
              /* When generating a dynamic object, these relocations
                 are copied into the output file to be resolved at run
@@ -14230,12 +16506,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (!SYMBOL_CALLS_LOCAL (info, &h->elf)
+             else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
                       && !is_opd
                       && r_type != R_PPC64_TOC)
                {
-                 BFD_ASSERT (h->elf.dynindx != -1);
-                 outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
+                 indx = h->elf.dynindx;
+                 BFD_ASSERT (indx != -1);
+                 outrel.r_info = ELF64_R_INFO (indx, r_type);
                }
              else
                {
@@ -14278,15 +16555,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    }
                  else
                    {
-                     long indx = 0;
-
                      if (h != NULL
                          ? h->elf.type == STT_GNU_IFUNC
                          : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                        {
                          info->callbacks->einfo
-                           (_("%P: %H: %s for indirect "
-                              "function `%T' unsupported\n"),
+                           /* xgettext:c-format */
+                           (_("%H: %s for indirect "
+                              "function `%pT' unsupported\n"),
                             input_bfd, input_section, rel->r_offset,
                             ppc64_elf_howto_table[r_type]->name,
                             sym_name);
@@ -14301,21 +16577,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        }
                      else
                        {
-                         asection *osec;
+                         asection *osec = sec->output_section;
 
-                         osec = sec->output_section;
-                         indx = elf_section_data (osec)->dynindx;
-
-                         if (indx == 0)
+                         if ((osec->flags & SEC_THREAD_LOCAL) != 0)
+                           {
+                             /* TLS symbol values are relative to the
+                                TLS segment.  Dynamic relocations for
+                                local TLS symbols therefore can't be
+                                reduced to a relocation against their
+                                section symbol because it holds the
+                                address of the section, not a value
+                                relative to the TLS segment.  We could
+                                change the .tdata dynamic section symbol
+                                to be zero value but STN_UNDEF works
+                                and is used elsewhere, eg. for TPREL64
+                                GOT relocs against local TLS symbols.  */
+                             osec = htab->elf.tls_sec;
+                             indx = 0;
+                           }
+                         else
                            {
-                             if ((osec->flags & SEC_READONLY) == 0
-                                 && htab->elf.data_index_section != NULL)
-                               osec = htab->elf.data_index_section;
-                             else
-                               osec = htab->elf.text_index_section;
                              indx = elf_section_data (osec)->dynindx;
+                             if (indx == 0)
+                               {
+                                 if ((osec->flags & SEC_READONLY) == 0
+                                     && htab->elf.data_index_section != NULL)
+                                   osec = htab->elf.data_index_section;
+                                 else
+                                   osec = htab->elf.text_index_section;
+                                 indx = elf_section_data (osec)->dynindx;
+                               }
+                             BFD_ASSERT (indx != 0);
                            }
-                         BFD_ASSERT (indx != 0);
 
                          /* We are turning this relocation into one
                             against a section symbol, so subtract out
@@ -14333,7 +16626,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (h != NULL
                  ? h->elf.type == STT_GNU_IFUNC
                  : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
-               sreloc = htab->elf.irelplt;
+               {
+                 sreloc = htab->elf.irelplt;
+                 if (indx == 0 || is_static_defined (&h->elf))
+                   htab->elf.ifunc_resolvers = TRUE;
+               }
              if (sreloc == NULL)
                abort ();
 
@@ -14344,11 +16641,24 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
              bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
 
+             if (!warned_dynamic
+                 && !ppc64_glibc_dynamic_reloc (ELF64_R_TYPE (outrel.r_info)))
+               {
+                 info->callbacks->einfo
+                   /* xgettext:c-format */
+                   (_("%X%P: %pB: %s against %pT "
+                      "is not supported by glibc as a dynamic relocation\n"),
+                    input_bfd,
+                    ppc64_elf_howto_table[ELF64_R_TYPE (outrel.r_info)]->name,
+                    sym_name);
+                 warned_dynamic = TRUE;
+               }
+
              /* If this reloc is against an external symbol, it will
                 be computed at runtime, so there's no need to do
                 anything now.  However, for the sake of prelink ensure
                 that the section contents are a known value.  */
-             if (! relocate)
+             if (!relocate)
                {
                  unresolved_reloc = FALSE;
                  /* The value chosen here is quite arbitrary as ld.so
@@ -14365,9 +16675,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    addend = outrel.r_addend;
                  /* Adjust pc_relative relocs to have zero in *r_offset.  */
                  else if (ppc64_elf_howto_table[r_type]->pc_relative)
-                   addend = (input_section->output_section->vma
-                             + input_section->output_offset
-                             + rel->r_offset);
+                   addend = outrel.r_offset;
                }
            }
          break;
@@ -14392,18 +16700,19 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          /* These ones haven't been implemented yet.  */
 
          info->callbacks->einfo
-           (_("%P: %B: %s is not supported for `%T'\n"),
+           /* xgettext:c-format */
+           (_("%P: %pB: %s is not supported for `%pT'\n"),
             input_bfd,
             ppc64_elf_howto_table[r_type]->name, sym_name);
 
          bfd_set_error (bfd_error_invalid_operation);
          ret = FALSE;
-         continue;
+         goto copy_reloc;
        }
 
       /* Multi-instruction sequences that access the TOC can be
         optimized, eg. addis ra,r2,0; addi rb,ra,x;
-        to             nop;           addi rb,r2,x;  */
+        to             nop;           addi rb,r2,x;  */
       switch (r_type)
        {
        default:
@@ -14424,6 +16733,56 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             insn.  */
          break;
 
+       case R_PPC64_PLTCALL_NOTOC:
+         if (!unresolved_reloc)
+           htab->notoc_plt = 1;
+         /* Fall through.  */
+       case R_PPC64_PLTCALL:
+         if (unresolved_reloc)
+           {
+             /* No plt entry.  Make this into a direct call.  */
+             bfd_byte *p = contents + rel->r_offset;
+             insn = bfd_get_32 (input_bfd, p);
+             insn &= 1;
+             bfd_put_32 (input_bfd, B_DOT | insn, p);
+             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;
+
+       case R_PPC64_PLTSEQ_NOTOC:
+       case R_PPC64_PLTSEQ:
+         if (unresolved_reloc)
+           {
+             unresolved_reloc = FALSE;
+             goto nop_it;
+           }
+         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)
+           {
+             unresolved_reloc = FALSE;
+             goto nop_it;
+           }
+         /* Fall through.  */
        case R_PPC64_GOT_TLSLD16_HA:
        case R_PPC64_GOT_TLSGD16_HA:
        case R_PPC64_GOT_TPREL16_HA:
@@ -14433,11 +16792,22 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000
              && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn)
            {
-             bfd_byte *p = contents + (rel->r_offset & ~3);
+             bfd_byte *p;
+           nop_it:
+             p = contents + (rel->r_offset & ~3);
              bfd_put_32 (input_bfd, NOP, p);
+             goto copy_reloc;
            }
          break;
 
+       case R_PPC64_PLT16_LO:
+       case R_PPC64_PLT16_LO_DS:
+         if (unresolved_reloc)
+           {
+             unresolved_reloc = FALSE;
+             goto nop_it;
+           }
+         /* Fall through.  */
        case R_PPC64_GOT_TLSLD16_LO:
        case R_PPC64_GOT_TLSGD16_LO:
        case R_PPC64_GOT_TPREL16_LO_DS:
@@ -14451,10 +16821,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              insn = bfd_get_32 (input_bfd, p);
-             if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
+             if ((insn & (0x3fu << 26)) == 12u << 26 /* addic */)
                {
                  /* Transform addic to addi when we change reg.  */
-                 insn &= ~((0x3f << 26) | (0x1f << 16));
+                 insn &= ~((0x3fu << 26) | (0x1f << 16));
                  insn |= (14u << 26) | (2 << 16);
                }
              else
@@ -14465,6 +16835,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              bfd_put_32 (input_bfd, insn, p);
            }
          break;
+
+       case R_PPC64_TPREL16_HA:
+         if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+           {
+             bfd_byte *p = contents + (rel->r_offset & ~3);
+             insn = bfd_get_32 (input_bfd, p);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */)
+               /* xgettext:c-format */
+               info->callbacks->minfo
+                 (_("%H: warning: %s unexpected insn %#x.\n"),
+                  input_bfd, input_section, rel->r_offset,
+                  ppc64_elf_howto_table[r_type]->name, insn);
+             else
+               {
+                 bfd_put_32 (input_bfd, NOP, p);
+                 goto copy_reloc;
+               }
+           }
+         break;
+
+       case R_PPC64_TPREL16_LO:
+       case R_PPC64_TPREL16_LO_DS:
+         if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+           {
+             bfd_byte *p = contents + (rel->r_offset & ~3);
+             insn = bfd_get_32 (input_bfd, p);
+             insn &= ~(0x1f << 16);
+             insn |= 13 << 16;
+             bfd_put_32 (input_bfd, insn, p);
+           }
+         break;
        }
 
       /* Do any further special processing.  */
@@ -14474,6 +16876,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_REL16_HA:
+       case R_PPC64_REL16_HIGHA:
+       case R_PPC64_REL16_HIGHERA:
+       case R_PPC64_REL16_HIGHESTA:
+       case R_PPC64_REL16DX_HA:
        case R_PPC64_ADDR16_HA:
        case R_PPC64_ADDR16_HIGHA:
        case R_PPC64_ADDR16_HIGHERA:
@@ -14494,7 +16900,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             alone (it will be set to zero elsewhere in the link).  */
          if (sec == NULL)
            break;
-         /* Fall thru */
+         /* Fall through.  */
 
        case R_PPC64_GOT16_HA:
        case R_PPC64_PLTGOT16_HA:
@@ -14508,6 +16914,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          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:
@@ -14529,24 +16944,29 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_DTPREL16_LO_DS:
          insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
          mask = 3;
-         /* If this reloc is against an lq insn, then the value must be
-            a multiple of 16.  This is somewhat of a hack, but the
-            "correct" way to do this by defining _DQ forms of all the
-            _DS relocs bloats all reloc switches in this file.  It
-            doesn't seem to make much sense to use any of these relocs
-            in data, so testing the insn should be safe.  */
-         if ((insn & (0x3f << 26)) == (56u << 26))
+         /* If this reloc is against an lq, lxv, or stxv insn, then
+            the value must be a multiple of 16.  This is somewhat of
+            a hack, but the "correct" way to do this by defining _DQ
+            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 & (0x3fu << 26)) == (56u << 26)
+             || ((insn & (0x3fu << 26)) == (61u << 26) && (insn & 3) == 1))
            mask = 15;
-         if (((relocation + addend) & mask) != 0)
+         relocation += addend;
+         addend = insn & (mask ^ 3);
+         if ((relocation & mask) != 0)
            {
+             relocation ^= relocation & mask;
              info->callbacks->einfo
-               (_("%P: %H: error: %s not a multiple of %u\n"),
+               /* xgettext:c-format */
+               (_("%H: error: %s not a multiple of %u\n"),
                 input_bfd, input_section, rel->r_offset,
                 ppc64_elf_howto_table[r_type]->name,
                 mask + 1);
              bfd_set_error (bfd_error_bad_value);
              ret = FALSE;
-             continue;
+             goto copy_reloc;
            }
          break;
        }
@@ -14554,6 +16974,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
         because such sections are not SEC_ALLOC and thus ld.so will
         not process them.  */
+      howto = ppc64_elf_howto_table[(int) r_type];
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
               && h->elf.def_dynamic)
@@ -14561,25 +16982,119 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                                      rel->r_offset) != (bfd_vma) -1)
        {
          info->callbacks->einfo
-           (_("%P: %H: unresolvable %s against `%T'\n"),
+           /* xgettext:c-format */
+           (_("%H: unresolvable %s against `%pT'\n"),
             input_bfd, input_section, rel->r_offset,
-            ppc64_elf_howto_table[(int) r_type]->name,
+            howto->name,
             h->elf.root.root.string);
          ret = FALSE;
        }
 
-      r = _bfd_final_link_relocate (ppc64_elf_howto_table[(int) r_type],
-                                   input_bfd,
-                                   input_section,
-                                   contents,
-                                   rel->r_offset,
-                                   relocation,
-                                   addend);
+      /* 16-bit fields in insns mostly have signed values, but a
+        few insns have 16-bit unsigned values.  Really, we should
+        have different reloc types.  */
+      if (howto->complain_on_overflow != complain_overflow_dont
+         && howto->dst_mask == 0xffff
+         && (input_section->flags & SEC_CODE) != 0)
+       {
+         enum complain_overflow complain = complain_overflow_signed;
+
+         insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
+         if ((insn & (0x3fu << 26)) == 10u << 26 /* cmpli */)
+           complain = complain_overflow_bitfield;
+         else if (howto->rightshift == 0
+                  ? ((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)
+           {
+             alt_howto = *howto;
+             alt_howto.complain_on_overflow = complain;
+             howto = &alt_howto;
+           }
+       }
+
+      switch (r_type)
+       {
+         /* 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
+           {
+             relocation += addend;
+             relocation -= (rel->r_offset
+                            + input_section->output_offset
+                            + input_section->output_section->vma);
+             relocation = (bfd_signed_vma) relocation >> 16;
+             insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             insn &= ~0x1fffc1;
+             insn |= (relocation & 0xffc1) | ((relocation & 0x3e) << 15);
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+             r = bfd_reloc_ok;
+             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);
+       }
 
       if (r != bfd_reloc_ok)
        {
          char *more_info = NULL;
-         const char *reloc_name = ppc64_elf_howto_table[r_type]->name;
+         const char *reloc_name = howto->name;
 
          if (reloc_dest != DEST_NORMAL)
            {
@@ -14595,38 +17110,54 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          if (r == bfd_reloc_overflow)
            {
-             if (warned)
-               continue;
-             if (h != NULL
-                 && h->elf.root.type == bfd_link_hash_undefweak
-                 && ppc64_elf_howto_table[r_type]->pc_relative)
-               {
-                 /* Assume this is a call protected by other code that
-                    detects the symbol is undefined.  If this is the case,
-                    we can safely ignore the overflow.  If not, the
-                    program is hosed anyway, and a little warning isn't
-                    going to help.  */
-
-                 continue;
-               }
-
-             if (!((*info->callbacks->reloc_overflow)
-                   (info, &h->elf.root, sym_name,
-                    reloc_name, orig_rel.r_addend,
-                    input_bfd, input_section, rel->r_offset)))
-               return FALSE;
+             /* On code like "if (foo) foo();" don't report overflow
+                on a branch to zero when foo is undefined.  */
+             if (!warned
+                 && (reloc_dest == DEST_STUB
+                     || !(h != NULL
+                          && (h->elf.root.type == bfd_link_hash_undefweak
+                              || h->elf.root.type == bfd_link_hash_undefined)
+                          && is_branch_reloc (r_type))))
+               info->callbacks->reloc_overflow (info, &h->elf.root,
+                                                sym_name, reloc_name,
+                                                orig_rel.r_addend,
+                                                input_bfd, input_section,
+                                                rel->r_offset);
            }
          else
            {
              info->callbacks->einfo
-               (_("%P: %H: %s against `%T': error %d\n"),
+               /* xgettext:c-format */
+               (_("%H: %s against `%pT': error %d\n"),
                 input_bfd, input_section, rel->r_offset,
                 reloc_name, sym_name, (int) r);
              ret = FALSE;
            }
-         if (more_info != NULL)
-           free (more_info);
+         free (more_info);
        }
+    copy_reloc:
+      if (wrel != rel)
+       *wrel = *rel;
+    }
+
+  if (wrel != rel)
+    {
+      Elf_Internal_Shdr *rel_hdr;
+      size_t deleted = rel - wrel;
+
+      rel_hdr = _bfd_elf_single_rel_hdr (input_section->output_section);
+      rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+      if (rel_hdr->sh_size == 0)
+       {
+         /* It is too late to remove an empty reloc section.  Leave
+            one NONE reloc.
+            ??? What is wrong with an empty section???  */
+         rel_hdr->sh_size = rel_hdr->sh_entsize;
+         deleted -= 1;
+       }
+      rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+      rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+      input_section->reloc_count -= deleted;
     }
 
   /* If we're emitting relocations, then shortly after this function
@@ -14634,7 +17165,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
      adjusted.  Worse, reloc symbol indices will be for the output
      file rather than the input.  Save a copy of the relocs for
      opd_entry_value.  */
-  if (is_opd && (info->emitrelocations || info->relocatable))
+  if (is_opd && (info->emitrelocations || bfd_link_relocatable (info)))
     {
       bfd_size_type amt;
       amt = input_section->reloc_count * sizeof (Elf_Internal_Rela);
@@ -14669,10 +17200,10 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
     return 1;
 
   value = elfsym->st_value - input_sec->output_offset;
-  if (!info->relocatable)
+  if (!bfd_link_relocatable (info))
     value -= input_sec->output_section->vma;
 
-  adjust = opd->adjust[value / 8];
+  adjust = opd->adjust[OPD_NDX (value)];
   if (adjust == -1)
     return 2;
 
@@ -14687,99 +17218,62 @@ static bfd_boolean
 ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
                                 struct bfd_link_info *info,
                                 struct elf_link_hash_entry *h,
-                                Elf_Internal_Sym *sym ATTRIBUTE_UNUSED)
+                                Elf_Internal_Sym *sym)
 {
   struct ppc_link_hash_table *htab;
   struct plt_entry *ent;
-  Elf_Internal_Rela rela;
-  bfd_byte *loc;
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
-  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
-    if (ent->plt.offset != (bfd_vma) -1)
-      {
-       /* This symbol has an entry in the procedure linkage
-          table.  Set it up.  */
-       if (!htab->elf.dynamic_sections_created
-           || h->dynindx == -1)
-         {
-           BFD_ASSERT (h->type == STT_GNU_IFUNC
-                       && h->def_regular
-                       && (h->root.type == bfd_link_hash_defined
-                           || h->root.type == bfd_link_hash_defweak));
-           rela.r_offset = (htab->elf.iplt->output_section->vma
-                            + htab->elf.iplt->output_offset
-                            + ent->plt.offset);
-           if (htab->opd_abi)
-             rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
-           else
-             rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
-           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);
-           loc = (htab->elf.irelplt->contents
-                  + (htab->elf.irelplt->reloc_count++
-                     * sizeof (Elf64_External_Rela)));
-         }
-       else
-         {
-           rela.r_offset = (htab->elf.splt->output_section->vma
-                            + htab->elf.splt->output_offset
-                            + ent->plt.offset);
-           rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
-           rela.r_addend = ent->addend;
-           loc = (htab->elf.srelplt->contents
-                  + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
-                     / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
-         }
-       bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
-
-       if (!htab->opd_abi)
-         {
-           if (!h->def_regular)
-             {
-               /* Mark the symbol as undefined, rather than as
-                  defined in glink.  Leave the value if there were
-                  any relocations where pointer equality matters
-                  (this is a clue for the dynamic linker, to make
-                  function pointer comparisons work between an
-                  application and shared library), otherwise set it
-                  to zero.  */
-               sym->st_shndx = SHN_UNDEF;
-               if (!h->pointer_equality_needed)
-                 sym->st_value = 0;
-               else if (!h->ref_regular_nonweak)
-                 {
-                   /* This breaks function pointer comparisons, but
-                      that is better than breaking tests for a NULL
-                      function pointer.  */
-                   sym->st_value = 0;
-                 }
-             }
-         }
-      }
+  if (!htab->opd_abi && !h->def_regular)
+    for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+      if (ent->plt.offset != (bfd_vma) -1)
+       {
+         /* Mark the symbol as undefined, rather than as
+            defined in glink.  Leave the value if there were
+            any relocations where pointer equality matters
+            (this is a clue for the dynamic linker, to make
+            function pointer comparisons work between an
+            application and shared library), otherwise set it
+            to zero.  */
+         sym->st_shndx = SHN_UNDEF;
+         if (!h->pointer_equality_needed)
+           sym->st_value = 0;
+         else if (!h->ref_regular_nonweak)
+           {
+             /* This breaks function pointer comparisons, but
+                that is better than breaking tests for a NULL
+                function pointer.  */
+             sym->st_value = 0;
+           }
+         break;
+       }
 
-  if (h->needs_copy)
+  if (h->needs_copy
+      && (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+      && (h->root.u.def.section == htab->elf.sdynbss
+         || h->root.u.def.section == htab->elf.sdynrelro))
     {
       /* This symbol needs a copy reloc.  Set it up.  */
+      Elf_Internal_Rela rela;
+      asection *srel;
+      bfd_byte *loc;
 
-      if (h->dynindx == -1
-         || (h->root.type != bfd_link_hash_defined
-             && h->root.type != bfd_link_hash_defweak)
-         || htab->relbss == NULL)
+      if (h->dynindx == -1)
        abort ();
 
-      rela.r_offset = (h->root.u.def.value
-                      + h->root.u.def.section->output_section->vma
-                      + h->root.u.def.section->output_offset);
+      rela.r_offset = defined_sym_val (h);
       rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_COPY);
       rela.r_addend = 0;
-      loc = htab->relbss->contents;
-      loc += htab->relbss->reloc_count++ * sizeof (Elf64_External_Rela);
+      if (h->root.u.def.section == htab->elf.sdynrelro)
+       srel = htab->elf.sreldynrelro;
+      else
+       srel = htab->elf.srelbss;
+      loc = srel->contents;
+      loc += srel->reloc_count++ * sizeof (Elf64_External_Rela);
       bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
     }
 
@@ -14859,7 +17353,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
                 of glink rather than the first entry point, which is
                 what ld.so needs, and now have a bigger stub to
                 support automatic multiple TOCs.  */
-             dyn.d_un.d_ptr += GLINK_CALL_STUB_SIZE - 8 * 4;
+             dyn.d_un.d_ptr += GLINK_PLTRESOLVE_SIZE (htab) - 8 * 4;
              break;
 
            case DT_PPC64_OPD:
@@ -14870,8 +17364,11 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              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;
              break;
 
            case DT_PPC64_OPDSZ:
@@ -14895,33 +17392,20 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              dyn.d_un.d_val = htab->elf.srelplt->size;
              break;
 
-           case DT_RELASZ:
-             /* Don't count procedure linkage table relocs in the
-                overall reloc count.  */
-             s = htab->elf.srelplt;
-             if (s == NULL)
-               continue;
-             dyn.d_un.d_val -= s->size;
-             break;
-
-           case DT_RELA:
-             /* We may not be using the standard ELF linker script.
-                If .rela.plt is the first .rela section, we adjust
-                DT_RELA to not include it.  */
-             s = htab->elf.srelplt;
-             if (s == NULL)
-               continue;
-             if (dyn.d_un.d_ptr != s->output_section->vma + s->output_offset)
-               continue;
-             dyn.d_un.d_ptr += s->size;
-             break;
+           case DT_TEXTREL:
+             if (htab->elf.ifunc_resolvers)
+               info->callbacks->einfo
+                 (_("%P: warning: text relocations and GNU indirect "
+                    "functions may result in a segfault at runtime\n"));
+             continue;
            }
 
          bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
        }
     }
 
-  if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0)
+  if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0
+      && htab->elf.sgot->output_section != bfd_abs_section_ptr)
     {
       /* Fill in the first entry in the global offset table.
         We use it to hold the link-time TOCbase.  */
@@ -14930,10 +17414,12 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
                  htab->elf.sgot->contents);
 
       /* Set .got entry size.  */
-      elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 8;
+      elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize
+       = 8;
     }
 
-  if (htab->elf.splt != NULL && htab->elf.splt->size != 0)
+  if (htab->elf.splt != NULL && htab->elf.splt->size != 0
+      && htab->elf.splt->output_section != bfd_abs_section_ptr)
     {
       /* Set .plt entry size.  */
       elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize
@@ -14962,6 +17448,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 
 
   if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->size != 0
       && htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
       && !_bfd_elf_write_section_eh_frame (output_bfd, info,
                                           htab->glink_eh_frame,
@@ -14971,7 +17458,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
   /* We need to handle writing out multiple GOT sections ourselves,
      since we didn't add them to DYNOBJ.  We know dynobj is the first
      bfd.  */
-  while ((dynobj = dynobj->link_next) != NULL)
+  while ((dynobj = dynobj->link.next) != NULL)
     {
       asection *s;
 
@@ -15007,7 +17494,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 #undef  TARGET_LITTLE_NAME
 
 #undef  TARGET_BIG_SYM
-#define TARGET_BIG_SYM bfd_elf64_powerpc_freebsd_vec
+#define TARGET_BIG_SYM powerpc_elf64_fbsd_vec
 #undef  TARGET_BIG_NAME
 #define TARGET_BIG_NAME "elf64-powerpc-freebsd"
 
@@ -15018,4 +17505,3 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf64_bed      elf64_powerpc_fbsd_bed
 
 #include "elf64-target.h"
-
This page took 0.20957 seconds and 4 git commands to generate.