* elflink.c (_bfd_elf_link_create_dynamic_sections): Move from
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index f78f275460aac53a4df9d948ed70a03201883f91..011e637b1226b42a875416ae25e801a02fae2e86 100644 (file)
@@ -3,21 +3,21 @@
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
 
-This file is part of BFD, the Binary File Descriptor library.
+   This file is part of BFD, the Binary File Descriptor library.
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* This file is based on the 64-bit PowerPC ELF ABI.  It is also based
    on the file elf32-ppc.c.  */
@@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
-#include "elf/ppc.h"
+#include "elf/ppc64.h"
 #include "elf64-ppc.h"
 
 static void ppc_howto_init
@@ -56,6 +56,8 @@ static bfd_boolean ppc64_elf_object_p
   PARAMS ((bfd *));
 static bfd_boolean ppc64_elf_merge_private_bfd_data
   PARAMS ((bfd *, bfd *));
+static bfd_boolean ppc64_elf_new_section_hook
+  PARAMS ((bfd *, asection *));
 
 
 /* The name of the dynamic interpreter.  This is put in the .interp
@@ -69,7 +71,11 @@ static bfd_boolean ppc64_elf_merge_private_bfd_data
 #define PLT_INITIAL_ENTRY_SIZE PLT_ENTRY_SIZE
 
 /* TOC base pointers offset from start of TOC.  */
-#define TOC_BASE_OFF (0x8000)
+#define TOC_BASE_OFF   0x8000
+
+/* Offset of tp and dtp pointers from start of TLS block.  */
+#define TP_OFFSET      0x7000
+#define DTP_OFFSET     0x8000
 
 /* .plt call stub instructions.  */
 #define ADDIS_R12_R2   0x3d820000      /* addis %r12,%r2,xxx@ha     */
@@ -126,14 +132,14 @@ static bfd_boolean ppc64_elf_merge_private_bfd_data
 #define ONES(n) (((bfd_vma) 1 << ((n) - 1) << 1) - 1)
 
 /* Relocation HOWTO's.  */
-static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC_max];
+static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC64_max];
 
 static reloc_howto_type ppc64_elf_howto_raw[] = {
   /* This reloc does nothing.  */
   HOWTO (R_PPC64_NONE,         /* type */
         0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        8,                     /* bitsize */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
@@ -667,9 +673,8 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* Like R_PPC64_REL24 without touching the two least significant
-     bits.  Should have been named R_PPC64_REL30!  */
-  HOWTO (R_PPC64_ADDR30,       /* type */
+  /* 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 */
@@ -677,7 +682,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_PPC64_ADDR30",      /* name */
+        "R_PPC64_REL30",       /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0xfffffffc,            /* dst_mask */
@@ -1151,224 +1156,914 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         0xfffc,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* GNU extension to record C++ vtable hierarchy.  */
-  HOWTO (R_PPC64_GNU_VTINHERIT,        /* type */
+  /* Marker reloc for TLS.  */
+  HOWTO (R_PPC64_TLS,
         0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        NULL,                  /* special_function */
-        "R_PPC64_GNU_VTINHERIT", /* name */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC64_TLS",         /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* GNU extension to record C++ vtable member usage.  */
-  HOWTO (R_PPC64_GNU_VTENTRY,  /* type */
+  /* Computes the load module index of the load module that contains the
+     definition of its TLS sym.  */
+  HOWTO (R_PPC64_DTPMOD64,
         0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        NULL,                  /* special_function */
-        "R_PPC64_GNU_VTENTRY", /* name */
+        ppc64_elf_unhandled_reloc, /* special_function */
+        "R_PPC64_DTPMOD64",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
-        0,                     /* dst_mask */
+        ONES (64),             /* dst_mask */
         FALSE),                /* pcrel_offset */
-};
-
-\f
-/* Initialize the ppc64_elf_howto_table, so that linear accesses can
-   be done.  */
-
-static void
-ppc_howto_init ()
-{
-  unsigned int i, type;
 
-  for (i = 0;
-       i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
-       i++)
-    {
-      type = ppc64_elf_howto_raw[i].type;
-      BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
-                         / sizeof (ppc64_elf_howto_table[0])));
-      ppc64_elf_howto_table[type] = &ppc64_elf_howto_raw[i];
-    }
-}
+  /* 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 */
 
-static reloc_howto_type *
-ppc64_elf_reloc_type_lookup (abfd, code)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     bfd_reloc_code_real_type code;
-{
-  enum elf_ppc_reloc_type ppc_reloc = R_PPC_NONE;
+  /* 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 */
 
-  if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
-    /* Initialize howto table if needed.  */
-    ppc_howto_init ();
+  /* 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 */
 
-  switch ((int) code)
-    {
-    default:
-      return (reloc_howto_type *) NULL;
+  /* 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_dont, /* 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 */
 
-    case BFD_RELOC_NONE:                ppc_reloc = R_PPC64_NONE;
-      break;
-    case BFD_RELOC_32:                  ppc_reloc = R_PPC64_ADDR32;
-      break;
-    case BFD_RELOC_PPC_BA26:            ppc_reloc = R_PPC64_ADDR24;
-      break;
-    case BFD_RELOC_16:                  ppc_reloc = R_PPC64_ADDR16;
-      break;
-    case BFD_RELOC_LO16:                ppc_reloc = R_PPC64_ADDR16_LO;
-      break;
-    case BFD_RELOC_HI16:                ppc_reloc = R_PPC64_ADDR16_HI;
-      break;
-    case BFD_RELOC_HI16_S:              ppc_reloc = R_PPC64_ADDR16_HA;
-      break;
-    case BFD_RELOC_PPC_BA16:            ppc_reloc = R_PPC64_ADDR14;
-      break;
-    case BFD_RELOC_PPC_BA16_BRTAKEN:    ppc_reloc = R_PPC64_ADDR14_BRTAKEN;
-      break;
-    case BFD_RELOC_PPC_BA16_BRNTAKEN:   ppc_reloc = R_PPC64_ADDR14_BRNTAKEN;
-      break;
-    case BFD_RELOC_PPC_B26:             ppc_reloc = R_PPC64_REL24;
-      break;
-    case BFD_RELOC_PPC_B16:             ppc_reloc = R_PPC64_REL14;
-      break;
-    case BFD_RELOC_PPC_B16_BRTAKEN:     ppc_reloc = R_PPC64_REL14_BRTAKEN;
-      break;
-    case BFD_RELOC_PPC_B16_BRNTAKEN:    ppc_reloc = R_PPC64_REL14_BRNTAKEN;
-      break;
-    case BFD_RELOC_16_GOTOFF:           ppc_reloc = R_PPC64_GOT16;
-      break;
-    case BFD_RELOC_LO16_GOTOFF:                 ppc_reloc = R_PPC64_GOT16_LO;
-      break;
-    case BFD_RELOC_HI16_GOTOFF:                 ppc_reloc = R_PPC64_GOT16_HI;
-      break;
-    case BFD_RELOC_HI16_S_GOTOFF:       ppc_reloc = R_PPC64_GOT16_HA;
-      break;
-    case BFD_RELOC_PPC_COPY:            ppc_reloc = R_PPC64_COPY;
-      break;
-    case BFD_RELOC_PPC_GLOB_DAT:        ppc_reloc = R_PPC64_GLOB_DAT;
-      break;
-    case BFD_RELOC_32_PCREL:            ppc_reloc = R_PPC64_REL32;
-      break;
-    case BFD_RELOC_32_PLTOFF:           ppc_reloc = R_PPC64_PLT32;
-      break;
-    case BFD_RELOC_32_PLT_PCREL:        ppc_reloc = R_PPC64_PLTREL32;
-      break;
-    case BFD_RELOC_LO16_PLTOFF:                 ppc_reloc = R_PPC64_PLT16_LO;
-      break;
-    case BFD_RELOC_HI16_PLTOFF:                 ppc_reloc = R_PPC64_PLT16_HI;
-      break;
-    case BFD_RELOC_HI16_S_PLTOFF:       ppc_reloc = R_PPC64_PLT16_HA;
-      break;
-    case BFD_RELOC_16_BASEREL:          ppc_reloc = R_PPC64_SECTOFF;
-      break;
-    case BFD_RELOC_LO16_BASEREL:        ppc_reloc = R_PPC64_SECTOFF_LO;
-      break;
-    case BFD_RELOC_HI16_BASEREL:        ppc_reloc = R_PPC64_SECTOFF_HI;
-      break;
-    case BFD_RELOC_HI16_S_BASEREL:      ppc_reloc = R_PPC64_SECTOFF_HA;
-      break;
-    case BFD_RELOC_CTOR:                ppc_reloc = R_PPC64_ADDR64;
-      break;
-    case BFD_RELOC_64:                  ppc_reloc = R_PPC64_ADDR64;
-      break;
-    case BFD_RELOC_PPC64_HIGHER:        ppc_reloc = R_PPC64_ADDR16_HIGHER;
-      break;
-    case BFD_RELOC_PPC64_HIGHER_S:      ppc_reloc = R_PPC64_ADDR16_HIGHERA;
-      break;
-    case BFD_RELOC_PPC64_HIGHEST:       ppc_reloc = R_PPC64_ADDR16_HIGHEST;
-      break;
-    case BFD_RELOC_PPC64_HIGHEST_S:     ppc_reloc = R_PPC64_ADDR16_HIGHESTA;
-      break;
-    case BFD_RELOC_64_PCREL:            ppc_reloc = R_PPC64_REL64;
-      break;
-    case BFD_RELOC_64_PLTOFF:           ppc_reloc = R_PPC64_PLT64;
-      break;
-    case BFD_RELOC_64_PLT_PCREL:        ppc_reloc = R_PPC64_PLTREL64;
-      break;
-    case BFD_RELOC_PPC_TOC16:           ppc_reloc = R_PPC64_TOC16;
-      break;
-    case BFD_RELOC_PPC64_TOC16_LO:      ppc_reloc = R_PPC64_TOC16_LO;
-      break;
-    case BFD_RELOC_PPC64_TOC16_HI:      ppc_reloc = R_PPC64_TOC16_HI;
-      break;
-    case BFD_RELOC_PPC64_TOC16_HA:      ppc_reloc = R_PPC64_TOC16_HA;
-      break;
-    case BFD_RELOC_PPC64_TOC:           ppc_reloc = R_PPC64_TOC;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16:      ppc_reloc = R_PPC64_PLTGOT16;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16_LO:   ppc_reloc = R_PPC64_PLTGOT16_LO;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16_HI:   ppc_reloc = R_PPC64_PLTGOT16_HI;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16_HA:   ppc_reloc = R_PPC64_PLTGOT16_HA;
-      break;
-    case BFD_RELOC_PPC64_ADDR16_DS:      ppc_reloc = R_PPC64_ADDR16_DS;
-      break;
-    case BFD_RELOC_PPC64_ADDR16_LO_DS:   ppc_reloc = R_PPC64_ADDR16_LO_DS;
-      break;
-    case BFD_RELOC_PPC64_GOT16_DS:       ppc_reloc = R_PPC64_GOT16_DS;
-      break;
-    case BFD_RELOC_PPC64_GOT16_LO_DS:    ppc_reloc = R_PPC64_GOT16_LO_DS;
-      break;
-    case BFD_RELOC_PPC64_PLT16_LO_DS:    ppc_reloc = R_PPC64_PLT16_LO_DS;
-      break;
-    case BFD_RELOC_PPC64_SECTOFF_DS:     ppc_reloc = R_PPC64_SECTOFF_DS;
-      break;
-    case BFD_RELOC_PPC64_SECTOFF_LO_DS:  ppc_reloc = R_PPC64_SECTOFF_LO_DS;
-      break;
-    case BFD_RELOC_PPC64_TOC16_DS:       ppc_reloc = R_PPC64_TOC16_DS;
-      break;
-    case BFD_RELOC_PPC64_TOC16_LO_DS:    ppc_reloc = R_PPC64_TOC16_LO_DS;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16_DS:    ppc_reloc = R_PPC64_PLTGOT16_DS;
-      break;
-    case BFD_RELOC_PPC64_PLTGOT16_LO_DS: ppc_reloc = R_PPC64_PLTGOT16_LO_DS;
-      break;
-    case BFD_RELOC_VTABLE_INHERIT:      ppc_reloc = R_PPC64_GNU_VTINHERIT;
-      break;
-    case BFD_RELOC_VTABLE_ENTRY:        ppc_reloc = R_PPC64_GNU_VTENTRY;
-      break;
-    }
+  /* 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_dont, /* 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 */
 
-  return ppc64_elf_howto_table[(int) ppc_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 */
 
-/* Set the howto pointer for a PowerPC ELF 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 */
 
-static void
-ppc64_elf_info_to_howto (abfd, cache_ptr, dst)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *cache_ptr;
-     Elf_Internal_Rela *dst;
-{
-  unsigned int type;
+  /* 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 */
 
-  /* Initialize howto table if needed.  */
-  if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
-    ppc_howto_init ();
+  /* 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 */
 
-  type = ELF64_R_TYPE (dst->r_info);
-  BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
-                     / sizeof (ppc64_elf_howto_table[0])));
-  cache_ptr->howto = ppc64_elf_howto_table[type];
-}
+  /* 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 */
 
-/* Handle the R_PPC_ADDR16_HA and similar relocs.  */
+  /* 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 */
 
-static bfd_reloc_status_type
-ppc64_elf_ha_reloc (abfd, reloc_entry, symbol, data,
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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 */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* 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 */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* 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_dont, /* 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 */
+
+  /* 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_dont, /* 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 */
+
+  /* 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 */
+
+  /* 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 */
+};
+
+\f
+/* Initialize the ppc64_elf_howto_table, so that linear accesses can
+   be done.  */
+
+static void
+ppc_howto_init ()
+{
+  unsigned int i, type;
+
+  for (i = 0;
+       i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
+       i++)
+    {
+      type = ppc64_elf_howto_raw[i].type;
+      BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
+                         / sizeof (ppc64_elf_howto_table[0])));
+      ppc64_elf_howto_table[type] = &ppc64_elf_howto_raw[i];
+    }
+}
+
+static reloc_howto_type *
+ppc64_elf_reloc_type_lookup (abfd, code)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     bfd_reloc_code_real_type code;
+{
+  enum elf_ppc64_reloc_type r = R_PPC64_NONE;
+
+  if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
+    /* Initialize howto table if needed.  */
+    ppc_howto_init ();
+
+  switch ((int) code)
+    {
+    default:
+      return (reloc_howto_type *) NULL;
+
+    case BFD_RELOC_NONE:                       r = R_PPC64_NONE;
+      break;
+    case BFD_RELOC_32:                         r = R_PPC64_ADDR32;
+      break;
+    case BFD_RELOC_PPC_BA26:                   r = R_PPC64_ADDR24;
+      break;
+    case BFD_RELOC_16:                         r = R_PPC64_ADDR16;
+      break;
+    case BFD_RELOC_LO16:                       r = R_PPC64_ADDR16_LO;
+      break;
+    case BFD_RELOC_HI16:                       r = R_PPC64_ADDR16_HI;
+      break;
+    case BFD_RELOC_HI16_S:                     r = R_PPC64_ADDR16_HA;
+      break;
+    case BFD_RELOC_PPC_BA16:                   r = R_PPC64_ADDR14;
+      break;
+    case BFD_RELOC_PPC_BA16_BRTAKEN:           r = R_PPC64_ADDR14_BRTAKEN;
+      break;
+    case BFD_RELOC_PPC_BA16_BRNTAKEN:          r = R_PPC64_ADDR14_BRNTAKEN;
+      break;
+    case BFD_RELOC_PPC_B26:                    r = R_PPC64_REL24;
+      break;
+    case BFD_RELOC_PPC_B16:                    r = R_PPC64_REL14;
+      break;
+    case BFD_RELOC_PPC_B16_BRTAKEN:            r = R_PPC64_REL14_BRTAKEN;
+      break;
+    case BFD_RELOC_PPC_B16_BRNTAKEN:           r = R_PPC64_REL14_BRNTAKEN;
+      break;
+    case BFD_RELOC_16_GOTOFF:                  r = R_PPC64_GOT16;
+      break;
+    case BFD_RELOC_LO16_GOTOFF:                        r = R_PPC64_GOT16_LO;
+      break;
+    case BFD_RELOC_HI16_GOTOFF:                        r = R_PPC64_GOT16_HI;
+      break;
+    case BFD_RELOC_HI16_S_GOTOFF:              r = R_PPC64_GOT16_HA;
+      break;
+    case BFD_RELOC_PPC_COPY:                   r = R_PPC64_COPY;
+      break;
+    case BFD_RELOC_PPC_GLOB_DAT:               r = R_PPC64_GLOB_DAT;
+      break;
+    case BFD_RELOC_32_PCREL:                   r = R_PPC64_REL32;
+      break;
+    case BFD_RELOC_32_PLTOFF:                  r = R_PPC64_PLT32;
+      break;
+    case BFD_RELOC_32_PLT_PCREL:               r = R_PPC64_PLTREL32;
+      break;
+    case BFD_RELOC_LO16_PLTOFF:                        r = R_PPC64_PLT16_LO;
+      break;
+    case BFD_RELOC_HI16_PLTOFF:                        r = R_PPC64_PLT16_HI;
+      break;
+    case BFD_RELOC_HI16_S_PLTOFF:              r = R_PPC64_PLT16_HA;
+      break;
+    case BFD_RELOC_16_BASEREL:                 r = R_PPC64_SECTOFF;
+      break;
+    case BFD_RELOC_LO16_BASEREL:               r = R_PPC64_SECTOFF_LO;
+      break;
+    case BFD_RELOC_HI16_BASEREL:               r = R_PPC64_SECTOFF_HI;
+      break;
+    case BFD_RELOC_HI16_S_BASEREL:             r = R_PPC64_SECTOFF_HA;
+      break;
+    case BFD_RELOC_CTOR:                       r = R_PPC64_ADDR64;
+      break;
+    case BFD_RELOC_64:                         r = R_PPC64_ADDR64;
+      break;
+    case BFD_RELOC_PPC64_HIGHER:               r = R_PPC64_ADDR16_HIGHER;
+      break;
+    case BFD_RELOC_PPC64_HIGHER_S:             r = R_PPC64_ADDR16_HIGHERA;
+      break;
+    case BFD_RELOC_PPC64_HIGHEST:              r = R_PPC64_ADDR16_HIGHEST;
+      break;
+    case BFD_RELOC_PPC64_HIGHEST_S:            r = R_PPC64_ADDR16_HIGHESTA;
+      break;
+    case BFD_RELOC_64_PCREL:                   r = R_PPC64_REL64;
+      break;
+    case BFD_RELOC_64_PLTOFF:                  r = R_PPC64_PLT64;
+      break;
+    case BFD_RELOC_64_PLT_PCREL:               r = R_PPC64_PLTREL64;
+      break;
+    case BFD_RELOC_PPC_TOC16:                  r = R_PPC64_TOC16;
+      break;
+    case BFD_RELOC_PPC64_TOC16_LO:             r = R_PPC64_TOC16_LO;
+      break;
+    case BFD_RELOC_PPC64_TOC16_HI:             r = R_PPC64_TOC16_HI;
+      break;
+    case BFD_RELOC_PPC64_TOC16_HA:             r = R_PPC64_TOC16_HA;
+      break;
+    case BFD_RELOC_PPC64_TOC:                  r = R_PPC64_TOC;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16:             r = R_PPC64_PLTGOT16;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16_LO:          r = R_PPC64_PLTGOT16_LO;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16_HI:          r = R_PPC64_PLTGOT16_HI;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16_HA:          r = R_PPC64_PLTGOT16_HA;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_DS:            r = R_PPC64_ADDR16_DS;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_LO_DS:         r = R_PPC64_ADDR16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_GOT16_DS:             r = R_PPC64_GOT16_DS;
+      break;
+    case BFD_RELOC_PPC64_GOT16_LO_DS:          r = R_PPC64_GOT16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_PLT16_LO_DS:          r = R_PPC64_PLT16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_SECTOFF_DS:           r = R_PPC64_SECTOFF_DS;
+      break;
+    case BFD_RELOC_PPC64_SECTOFF_LO_DS:                r = R_PPC64_SECTOFF_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_TOC16_DS:             r = R_PPC64_TOC16_DS;
+      break;
+    case BFD_RELOC_PPC64_TOC16_LO_DS:          r = R_PPC64_TOC16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16_DS:          r = R_PPC64_PLTGOT16_DS;
+      break;
+    case BFD_RELOC_PPC64_PLTGOT16_LO_DS:       r = R_PPC64_PLTGOT16_LO_DS;
+      break;
+    case BFD_RELOC_PPC_TLS:                    r = R_PPC64_TLS;
+      break;
+    case BFD_RELOC_PPC_DTPMOD:                 r = R_PPC64_DTPMOD64;
+      break;
+    case BFD_RELOC_PPC_TPREL16:                        r = R_PPC64_TPREL16;
+      break;
+    case BFD_RELOC_PPC_TPREL16_LO:             r = R_PPC64_TPREL16_LO;
+      break;
+    case BFD_RELOC_PPC_TPREL16_HI:             r = R_PPC64_TPREL16_HI;
+      break;
+    case BFD_RELOC_PPC_TPREL16_HA:             r = R_PPC64_TPREL16_HA;
+      break;
+    case BFD_RELOC_PPC_TPREL:                  r = R_PPC64_TPREL64;
+      break;
+    case BFD_RELOC_PPC_DTPREL16:               r = R_PPC64_DTPREL16;
+      break;
+    case BFD_RELOC_PPC_DTPREL16_LO:            r = R_PPC64_DTPREL16_LO;
+      break;
+    case BFD_RELOC_PPC_DTPREL16_HI:            r = R_PPC64_DTPREL16_HI;
+      break;
+    case BFD_RELOC_PPC_DTPREL16_HA:            r = R_PPC64_DTPREL16_HA;
+      break;
+    case BFD_RELOC_PPC_DTPREL:                 r = R_PPC64_DTPREL64;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSGD16:            r = R_PPC64_GOT_TLSGD16;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSGD16_LO:         r = R_PPC64_GOT_TLSGD16_LO;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSGD16_HI:         r = R_PPC64_GOT_TLSGD16_HI;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSGD16_HA:         r = R_PPC64_GOT_TLSGD16_HA;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSLD16:            r = R_PPC64_GOT_TLSLD16;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSLD16_LO:         r = R_PPC64_GOT_TLSLD16_LO;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSLD16_HI:         r = R_PPC64_GOT_TLSLD16_HI;
+      break;
+    case BFD_RELOC_PPC_GOT_TLSLD16_HA:         r = R_PPC64_GOT_TLSLD16_HA;
+      break;
+    case BFD_RELOC_PPC_GOT_TPREL16:            r = R_PPC64_GOT_TPREL16_DS;
+      break;
+    case BFD_RELOC_PPC_GOT_TPREL16_LO:         r = R_PPC64_GOT_TPREL16_LO_DS;
+      break;
+    case BFD_RELOC_PPC_GOT_TPREL16_HI:         r = R_PPC64_GOT_TPREL16_HI;
+      break;
+    case BFD_RELOC_PPC_GOT_TPREL16_HA:         r = R_PPC64_GOT_TPREL16_HA;
+      break;
+    case BFD_RELOC_PPC_GOT_DTPREL16:           r = R_PPC64_GOT_DTPREL16_DS;
+      break;
+    case BFD_RELOC_PPC_GOT_DTPREL16_LO:                r = R_PPC64_GOT_DTPREL16_LO_DS;
+      break;
+    case BFD_RELOC_PPC_GOT_DTPREL16_HI:                r = R_PPC64_GOT_DTPREL16_HI;
+      break;
+    case BFD_RELOC_PPC_GOT_DTPREL16_HA:                r = R_PPC64_GOT_DTPREL16_HA;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_DS:           r = R_PPC64_TPREL16_DS;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_LO_DS:                r = R_PPC64_TPREL16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_HIGHER:       r = R_PPC64_TPREL16_HIGHER;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_HIGHERA:      r = R_PPC64_TPREL16_HIGHERA;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_HIGHEST:      r = R_PPC64_TPREL16_HIGHEST;
+      break;
+    case BFD_RELOC_PPC64_TPREL16_HIGHESTA:     r = R_PPC64_TPREL16_HIGHESTA;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_DS:          r = R_PPC64_DTPREL16_DS;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_LO_DS:       r = R_PPC64_DTPREL16_LO_DS;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_HIGHER:      r = R_PPC64_DTPREL16_HIGHER;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_HIGHERA:     r = R_PPC64_DTPREL16_HIGHERA;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_HIGHEST:     r = R_PPC64_DTPREL16_HIGHEST;
+      break;
+    case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:    r = R_PPC64_DTPREL16_HIGHESTA;
+      break;
+    case BFD_RELOC_VTABLE_INHERIT:             r = R_PPC64_GNU_VTINHERIT;
+      break;
+    case BFD_RELOC_VTABLE_ENTRY:               r = R_PPC64_GNU_VTENTRY;
+      break;
+    }
+
+  return ppc64_elf_howto_table[(int) r];
+};
+
+/* Set the howto pointer for a PowerPC ELF reloc.  */
+
+static void
+ppc64_elf_info_to_howto (abfd, cache_ptr, dst)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     arelent *cache_ptr;
+     Elf_Internal_Rela *dst;
+{
+  unsigned int type;
+
+  /* Initialize howto table if needed.  */
+  if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
+    ppc_howto_init ();
+
+  type = ELF64_R_TYPE (dst->r_info);
+  BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
+                     / sizeof (ppc64_elf_howto_table[0])));
+  cache_ptr->howto = ppc64_elf_howto_table[type];
+}
+
+/* Handle the R_PPC64_ADDR16_HA and similar relocs.  */
+
+static bfd_reloc_status_type
+ppc64_elf_ha_reloc (abfd, reloc_entry, symbol, data,
                    input_section, output_bfd, error_message)
      bfd *abfd;
      arelent *reloc_entry;
@@ -1404,7 +2099,7 @@ ppc64_elf_brtaken_reloc (abfd, reloc_entry, symbol, data,
      char **error_message;
 {
   long insn;
-  enum elf_ppc_reloc_type r_type;
+  enum elf_ppc64_reloc_type r_type;
   bfd_size_type octets;
   /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
   bfd_boolean is_power4 = FALSE;
@@ -1419,7 +2114,7 @@ ppc64_elf_brtaken_reloc (abfd, reloc_entry, symbol, data,
   octets = reloc_entry->address * bfd_octets_per_byte (abfd);
   insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
   insn &= ~(0x01 << 21);
-  r_type = (enum elf_ppc_reloc_type) reloc_entry->howto->type;
+  r_type = (enum elf_ppc64_reloc_type) reloc_entry->howto->type;
   if (r_type == R_PPC64_ADDR14_BRTAKEN
       || r_type == R_PPC64_REL14_BRTAKEN)
     insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field.  */
@@ -1676,6 +2371,43 @@ ppc64_elf_merge_private_bfd_data (ibfd, obfd)
 
   return TRUE;
 }
+
+struct _ppc64_elf_section_data
+{
+  struct bfd_elf_section_data elf;
+
+  /* An array with one entry for each opd function descriptor.  */
+  union
+  {
+    /* Points to the function code section for local opd entries.  */
+    asection **func_sec;
+    /* After editing .opd, adjust references to opd local syms.  */
+    long *adjust;
+  } opd;
+
+  /* An array for toc sections, indexed by offset/8.
+     Specifies the relocation symbol index used at a given toc offset.  */
+  unsigned *t_symndx;
+};
+
+#define ppc64_elf_section_data(sec) \
+  ((struct _ppc64_elf_section_data *) elf_section_data (sec))
+
+static bfd_boolean
+ppc64_elf_new_section_hook (abfd, sec)
+     bfd *abfd;
+     asection *sec;
+{
+  struct _ppc64_elf_section_data *sdata;
+  bfd_size_type amt = sizeof (*sdata);
+
+  sdata = (struct _ppc64_elf_section_data *) bfd_zalloc (abfd, amt);
+  if (sdata == NULL)
+    return FALSE;
+  sec->used_by_bfd = (PTR) sdata;
+
+  return _bfd_elf_new_section_hook (abfd, sec);
+}
 \f
 /* The following functions are specific to the ELF linker, while
    functions above are used generally.  Those named ppc64_elf_* are
@@ -1717,7 +2449,7 @@ ppc64_elf_merge_private_bfd_data (ibfd, obfd)
    .
    .   .foo_stub:
    .           addis   12,2,Lfoo@toc@ha        # in practice, the call stub
-   .           addi    12,12,Lfoo@toc@l        # is slightly optimised, but
+   .           addi    12,12,Lfoo@toc@l        # is slightly optimized, but
    .           std     2,40(1)                 # this is the general idea
    .           ld      11,0(12)
    .           ld      2,8(12)
@@ -1765,13 +2497,55 @@ struct ppc_dyn_relocs
   bfd_size_type pc_count;
 };
 
+/* Track GOT entries needed for a given symbol.  We might need more
+   than one got entry per symbol.  */
+struct got_entry
+{
+  struct got_entry *next;
+
+  /* The symbol addend that we'll be placing in the GOT.  */
+  bfd_vma addend;
+
+  /* Reference count until size_dynamic_sections, GOT offset thereafter.  */
+  union
+    {
+      bfd_signed_vma refcount;
+      bfd_vma offset;
+    } got;
+
+  /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD,
+     TLS_TPREL or TLS_DTPREL for tls entries.  */
+  char tls_type;
+};
+
+/* The same for PLT.  */
+struct plt_entry
+{
+  struct plt_entry *next;
+
+  bfd_vma addend;
+
+  union
+    {
+      bfd_signed_vma refcount;
+      bfd_vma offset;
+    } plt;
+};
+
 /* Of those relocs that might be copied as dynamic relocs, this macro
-   selects between relative and absolute types.  */
+   selects those that must be copied when linking a shared library,
+   even when the symbol is local.  */
 
-#define IS_ABSOLUTE_RELOC(RTYPE)               \
+#define MUST_BE_DYN_RELOC(RTYPE)               \
   ((RTYPE) != R_PPC64_REL32                    \
    && (RTYPE) != R_PPC64_REL64                 \
-   && (RTYPE) != R_PPC64_ADDR30)
+   && (RTYPE) != R_PPC64_REL30)
+
+/* 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
+   section, and instead use a dynamic relocation to point into the
+   shared lib.  */
+#define ELIMINATE_COPY_RELOCS 1
 
 /* Section name for stubs is the associated section name plus this
    string.  */
@@ -1830,6 +2604,9 @@ struct ppc_stub_hash_entry {
   /* The symbol table entry, if any, that this was derived from.  */
   struct ppc_link_hash_entry *h;
 
+  /* And the reloc addend that this was derived from.  */
+  bfd_vma addend;
+
   /* 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;
@@ -1865,6 +2642,23 @@ struct ppc_link_hash_entry
   unsigned int is_func:1;
   unsigned int is_func_descriptor:1;
   unsigned int is_entry: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. */
+  char tls_mask;
 };
 
 /* ppc64 ELF linker hash table.  */
@@ -1911,6 +2705,18 @@ struct ppc_link_hash_table
   asection *sbrlt;
   asection *srelbrlt;
 
+  /* Short-cut to first output tls section.  */
+  asection *tls_sec;
+
+  /* Shortcut to .__tls_get_addr.  */
+  struct elf_link_hash_entry *tls_get_addr;
+
+  /* TLS local dynamic got entry handling.  */
+  union {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+  } tlsld_got;
+
   /* Set on error.  */
   unsigned int stub_error;
 
@@ -1955,6 +2761,10 @@ static bfd_boolean ppc64_elf_create_dynamic_sections
 static void ppc64_elf_copy_indirect_symbol
   PARAMS ((struct elf_backend_data *, struct elf_link_hash_entry *,
           struct elf_link_hash_entry *));
+static bfd_boolean update_local_sym_info
+  PARAMS ((bfd *, Elf_Internal_Shdr *, unsigned long, bfd_vma, int));
+static bfd_boolean update_plt_info
+  PARAMS ((bfd *, struct ppc_link_hash_entry *, bfd_vma));
 static bfd_boolean ppc64_elf_check_relocs
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
           const Elf_Internal_Rela *));
@@ -1972,6 +2782,11 @@ static bfd_boolean ppc64_elf_adjust_dynamic_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static void ppc64_elf_hide_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean));
+static bfd_boolean get_sym_h
+  PARAMS ((struct elf_link_hash_entry **, Elf_Internal_Sym **, asection **,
+          char **, Elf_Internal_Sym **, unsigned long, bfd *));
+static int get_tls_mask
+  PARAMS ((char **, Elf_Internal_Sym **, const Elf_Internal_Rela *, bfd *));
 static bfd_boolean allocate_dynrelocs
   PARAMS ((struct elf_link_hash_entry *, PTR));
 static bfd_boolean readonly_dynrelocs
@@ -1980,7 +2795,7 @@ static enum elf_reloc_type_class ppc64_elf_reloc_type_class
   PARAMS ((const Elf_Internal_Rela *));
 static bfd_boolean ppc64_elf_size_dynamic_sections
   PARAMS ((bfd *, struct bfd_link_info *));
-static INLINE enum ppc_stub_type ppc_type_of_stub
+static enum ppc_stub_type ppc_type_of_stub
   PARAMS ((asection *, const Elf_Internal_Rela *,
           struct ppc_link_hash_entry **, bfd_vma));
 static bfd_byte *build_plt_stub
@@ -2112,6 +2927,7 @@ link_hash_newfunc (entry, table, string)
       eh->is_func = 0;
       eh->is_func_descriptor = 0;
       eh->is_entry = 0;
+      eh->tls_mask = 0;
     }
 
   return entry;
@@ -2158,11 +2974,22 @@ ppc64_elf_link_hash_table_create (abfd)
   htab->sfpr = NULL;
   htab->sbrlt = NULL;
   htab->srelbrlt = NULL;
+  htab->tls_sec = NULL;
+  htab->tls_get_addr = NULL;
+  htab->tlsld_got.refcount = 0;
   htab->stub_error = 0;
   htab->has_14bit_branch = 0;
   htab->have_undefweak = 0;
   htab->stub_iteration = 0;
   htab->sym_sec.abfd = NULL;
+  /* Initializing two fields of the union is just cosmetic.  We really
+     only care about glist, but when compiled on a 32-bit host the
+     bfd_vma fields are larger.  Setting the bfd_vma to zero makes
+     debugger inspection of these fields look nicer.  */
+  htab->elf.init_refcount.refcount = 0;
+  htab->elf.init_refcount.glist = NULL;
+  htab->elf.init_offset.offset = 0;
+  htab->elf.init_offset.glist = NULL;
 
   return &htab->elf.root;
 }
@@ -2441,14 +3268,16 @@ ppc64_elf_create_dynamic_sections (dynobj, info)
 
 static void
 ppc64_elf_copy_indirect_symbol (bed, dir, ind)
-     struct elf_backend_data *bed;
+     struct elf_backend_data *bed ATTRIBUTE_UNUSED;
      struct elf_link_hash_entry *dir, *ind;
 {
   struct ppc_link_hash_entry *edir, *eind;
+  flagword mask;
 
   edir = (struct ppc_link_hash_entry *) dir;
   eind = (struct ppc_link_hash_entry *) ind;
 
+  /* Copy over any dynamic relocs we may have on the indirect sym.  */
   if (eind->dyn_relocs != NULL)
     {
       if (edir->dyn_relocs != NULL)
@@ -2456,7 +3285,7 @@ ppc64_elf_copy_indirect_symbol (bed, dir, ind)
          struct ppc_dyn_relocs **pp;
          struct ppc_dyn_relocs *p;
 
-         if (ind->root.type == bfd_link_hash_indirect)
+         if (eind->elf.root.type == bfd_link_hash_indirect)
            abort ();
 
          /* Add reloc counts against the weak sym to the strong sym
@@ -2486,8 +3315,93 @@ ppc64_elf_copy_indirect_symbol (bed, dir, ind)
   edir->is_func |= eind->is_func;
   edir->is_func_descriptor |= eind->is_func_descriptor;
   edir->is_entry |= eind->is_entry;
+  edir->tls_mask |= eind->tls_mask;
+
+  mask = (ELF_LINK_HASH_REF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR
+         | ELF_LINK_HASH_REF_REGULAR_NONWEAK | ELF_LINK_NON_GOT_REF);
+  /* If called to transfer flags for a weakdef during processing
+     of elf_adjust_dynamic_symbol, don't copy ELF_LINK_NON_GOT_REF.
+     We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
+  if (ELIMINATE_COPY_RELOCS
+      && eind->elf.root.type != bfd_link_hash_indirect
+      && (edir->elf.elf_link_hash_flags & ELF_LINK_HASH_DYNAMIC_ADJUSTED) != 0)
+    mask &= ~ELF_LINK_NON_GOT_REF;
+
+  edir->elf.elf_link_hash_flags |= eind->elf.elf_link_hash_flags & mask;
+
+  /* If we were called to copy over info for a weak sym, that's all.  */
+  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)
+    {
+      if (edir->elf.got.glist != NULL)
+       {
+         struct got_entry **entp;
+         struct got_entry *ent;
+
+         for (entp = &eind->elf.got.glist; (ent = *entp) != NULL; )
+           {
+             struct got_entry *dent;
+
+             for (dent = edir->elf.got.glist; dent != NULL; dent = dent->next)
+               if (dent->addend == ent->addend
+                   && dent->tls_type == ent->tls_type)
+                 {
+                   dent->got.refcount += ent->got.refcount;
+                   *entp = ent->next;
+                   break;
+                 }
+             if (dent == NULL)
+               entp = &ent->next;
+           }
+         *entp = edir->elf.got.glist;
+       }
+
+      edir->elf.got.glist = eind->elf.got.glist;
+      eind->elf.got.glist = NULL;
+    }
+
+  /* And plt entries.  */
+  if (eind->elf.plt.plist != NULL)
+    {
+      if (edir->elf.plt.plist != NULL)
+       {
+         struct plt_entry **entp;
+         struct plt_entry *ent;
+
+         for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; )
+           {
+             struct plt_entry *dent;
+
+             for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next)
+               if (dent->addend == ent->addend)
+                 {
+                   dent->plt.refcount += ent->plt.refcount;
+                   *entp = ent->next;
+                   break;
+                 }
+             if (dent == NULL)
+               entp = &ent->next;
+           }
+         *entp = edir->elf.plt.plist;
+       }
 
-  _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+      edir->elf.plt.plist = eind->elf.plt.plist;
+      eind->elf.plt.plist = NULL;
+    }
+
+  if (edir->elf.dynindx == -1)
+    {
+      edir->elf.dynindx = eind->elf.dynindx;
+      edir->elf.dynstr_index = eind->elf.dynstr_index;
+      eind->elf.dynindx = -1;
+      eind->elf.dynstr_index = 0;
+    }
+  else
+    BFD_ASSERT (eind->elf.dynindx == -1);
 }
 
 /* Set a flag, used by ppc64_elf_gc_mark_hook, on the entry symbol and
@@ -2512,6 +3426,83 @@ ppc64_elf_mark_entry_syms (info)
   return TRUE;
 }
 
+static bfd_boolean
+update_local_sym_info (abfd, symtab_hdr, r_symndx, r_addend, tls_type)
+     bfd *abfd;
+     Elf_Internal_Shdr *symtab_hdr;
+     unsigned long r_symndx;
+     bfd_vma r_addend;
+     int tls_type;
+{
+  struct got_entry **local_got_ents = elf_local_got_ents (abfd);
+  char *local_got_tls_masks;
+
+  if (local_got_ents == NULL)
+    {
+      bfd_size_type size = symtab_hdr->sh_info;
+
+      size *= sizeof (*local_got_ents) + sizeof (*local_got_tls_masks);
+      local_got_ents = (struct got_entry **) bfd_zalloc (abfd, size);
+      if (local_got_ents == NULL)
+       return FALSE;
+      elf_local_got_ents (abfd) = local_got_ents;
+    }
+
+  if ((tls_type & TLS_EXPLICIT) == 0)
+    {
+      struct got_entry *ent;
+
+      for (ent = local_got_ents[r_symndx]; ent != NULL; ent = ent->next)
+       if (ent->addend == r_addend && ent->tls_type == tls_type)
+         break;
+      if (ent == NULL)
+       {
+         bfd_size_type amt = sizeof (*ent);
+         ent = (struct got_entry *) bfd_alloc (abfd, amt);
+         if (ent == NULL)
+           return FALSE;
+         ent->next = local_got_ents[r_symndx];
+         ent->addend = r_addend;
+         ent->tls_type = tls_type;
+         ent->got.refcount = 0;
+         local_got_ents[r_symndx] = ent;
+       }
+      ent->got.refcount += 1;
+    }
+
+  local_got_tls_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
+  local_got_tls_masks[r_symndx] |= tls_type;
+  return TRUE;
+}
+
+static bfd_boolean
+update_plt_info (abfd, eh, addend)
+     bfd *abfd;
+     struct ppc_link_hash_entry *eh;
+     bfd_vma addend;
+{
+  struct plt_entry *ent;
+
+  for (ent = eh->elf.plt.plist; ent != NULL; ent = ent->next)
+    if (ent->addend == addend)
+      break;
+  if (ent == NULL)
+    {
+      bfd_size_type amt = sizeof (*ent);
+      ent = (struct plt_entry *) bfd_alloc (abfd, amt);
+      if (ent == NULL)
+       return FALSE;
+      ent->next = eh->elf.plt.plist;
+      ent->addend = addend;
+      ent->plt.refcount = 0;
+      eh->elf.plt.plist = ent;
+    }
+  ent->plt.refcount += 1;
+  eh->elf.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+  eh->is_func = 1;
+  return TRUE;
+}
+
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure
    linkage table, and dynamic reloc sections.  */
@@ -2565,7 +3556,7 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
       opd_sym_map = (asection **) bfd_zalloc (abfd, amt);
       if (opd_sym_map == NULL)
        return FALSE;
-      elf_section_data (sec)->tdata = opd_sym_map;
+      ppc64_elf_section_data (sec)->opd.func_sec = opd_sym_map;
     }
 
   if (htab->elf.dynobj == NULL)
@@ -2579,7 +3570,8 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
     {
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
-      enum elf_ppc_reloc_type r_type;
+      enum elf_ppc64_reloc_type r_type;
+      int tls_type = 0;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -2587,17 +3579,48 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
       else
        h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-      r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info);
+      r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
       switch (r_type)
        {
-         /* GOT16 relocations */
+       case R_PPC64_GOT_TLSLD16:
+       case R_PPC64_GOT_TLSLD16_LO:
+       case R_PPC64_GOT_TLSLD16_HI:
+       case R_PPC64_GOT_TLSLD16_HA:
+         htab->tlsld_got.refcount += 1;
+         tls_type = TLS_TLS | TLS_LD;
+         goto dogottls;
+
+       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 dogottls;
+
+       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:
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         tls_type = TLS_TLS | TLS_TPREL;
+         goto dogottls;
+
+       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;
+       dogottls:
+         sec->has_tls_reloc = 1;
+         /* Fall thru */
+
        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:
-
          /* This symbol requires a global offset table entry.  */
          if (htab->sgot == NULL
              && !create_got_section (htab->elf.dynobj, info))
@@ -2605,28 +3628,34 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
 
          if (h != NULL)
            {
-             h->got.refcount += 1;
-           }
-         else
-           {
-             bfd_signed_vma *local_got_refcounts;
+             struct ppc_link_hash_entry *eh;
+             struct got_entry *ent;
 
-             /* This is a global offset table entry for a local symbol.  */
-             local_got_refcounts = elf_local_got_refcounts (abfd);
-             if (local_got_refcounts == NULL)
+             eh = (struct ppc_link_hash_entry *) h;
+             for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
+               if (ent->addend == rel->r_addend
+                   && ent->tls_type == tls_type)
+                 break;
+             if (ent == NULL)
                {
-                 bfd_size_type size;
-
-                 size = symtab_hdr->sh_info;
-                 size *= sizeof (bfd_signed_vma);
-                 local_got_refcounts = ((bfd_signed_vma *)
-                                        bfd_zalloc (abfd, size));
-                 if (local_got_refcounts == NULL)
+                 bfd_size_type amt = sizeof (*ent);
+                 ent = (struct got_entry *) bfd_alloc (abfd, amt);
+                 if (ent == NULL)
                    return FALSE;
-                 elf_local_got_refcounts (abfd) = local_got_refcounts;
+                 ent->next = eh->elf.got.glist;
+                 ent->addend = rel->r_addend;
+                 ent->tls_type = tls_type;
+                 ent->got.refcount = 0;
+                 eh->elf.got.glist = ent;
                }
-             local_got_refcounts[r_symndx] += 1;
+             ent->got.refcount += 1;
+             eh->tls_mask |= tls_type;
            }
+         else
+           /* This is a global offset table entry for a local symbol.  */
+           if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                       rel->r_addend, tls_type))
+             return FALSE;
          break;
 
        case R_PPC64_PLT16_HA:
@@ -2646,10 +3675,10 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
              bfd_set_error (bfd_error_bad_value);
              return FALSE;
            }
-
-         h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-         h->plt.refcount += 1;
-         ((struct ppc_link_hash_entry *) h)->is_func = 1;
+         else
+           if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h,
+                                 rel->r_addend))
+             return FALSE;
          break;
 
          /* The following relocations don't need to propagate the
@@ -2667,6 +3696,16 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
        case R_PPC64_TOC16_HA:
        case R_PPC64_TOC16_DS:
        case R_PPC64_TOC16_LO_DS:
+       case R_PPC64_DTPREL16:
+       case R_PPC64_DTPREL16_LO:
+       case R_PPC64_DTPREL16_HI:
+       case R_PPC64_DTPREL16_HA:
+       case R_PPC64_DTPREL16_DS:
+       case R_PPC64_DTPREL16_LO_DS:
+       case R_PPC64_DTPREL16_HIGHER:
+       case R_PPC64_DTPREL16_HIGHERA:
+       case R_PPC64_DTPREL16_HIGHEST:
+       case R_PPC64_DTPREL16_HIGHESTA:
          break;
 
          /* This relocation describes the C++ object vtable hierarchy.
@@ -2696,9 +3735,93 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
            {
              /* We may need a .plt entry if the function this reloc
                 refers to is in a shared lib.  */
-             h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-             h->plt.refcount += 1;
-             ((struct ppc_link_hash_entry *) h)->is_func = 1;
+             if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h,
+                                   rel->r_addend))
+               return FALSE;
+             if (h == htab->tls_get_addr)
+               sec->has_tls_reloc = 1;
+             else if ((strncmp (h->root.root.string, ".__tls_get_addr", 15)
+                       == 0)
+                      && (h->root.root.string[15] == 0
+                          || h->root.root.string[15] == '@'))
+               {
+                 htab->tls_get_addr = h;
+                 sec->has_tls_reloc = 1;
+               }
+           }
+         break;
+
+       case R_PPC64_TPREL64:
+         tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         goto dotlstoc;
+
+       case R_PPC64_DTPMOD64:
+         if (rel + 1 < rel_end
+             && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64)
+             && rel[1].r_offset == rel->r_offset + 8)
+           tls_type = TLS_EXPLICIT | TLS_TLS | TLS_GD;
+         else
+           tls_type = TLS_EXPLICIT | TLS_TLS | TLS_LD;
+         goto dotlstoc;
+
+       case R_PPC64_DTPREL64:
+         tls_type = TLS_EXPLICIT | TLS_TLS | TLS_DTPREL;
+         if (rel != relocs
+             && rel[-1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPMOD64)
+             && rel[-1].r_offset == rel->r_offset - 8)
+           /* This is the second reloc of a dtpmod, dtprel pair.
+              Don't mark with TLS_DTPREL.  */
+           goto dodyn;
+
+       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;
+           }
+         else
+           if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                       rel->r_addend, tls_type))
+             return FALSE;
+
+         if (ppc64_elf_section_data (sec)->t_symndx == NULL)
+           {
+             /* One extra to simplify get_tls_mask.  */
+             bfd_size_type amt = sec->_raw_size * sizeof (unsigned) / 8 + 1;
+             ppc64_elf_section_data (sec)->t_symndx
+               = (unsigned *) bfd_zalloc (abfd, amt);
+             if (ppc64_elf_section_data (sec)->t_symndx == NULL)
+               return FALSE;
+           }
+         BFD_ASSERT (rel->r_offset % 8 == 0);
+         ppc64_elf_section_data (sec)->t_symndx[rel->r_offset / 8] = r_symndx;
+
+         /* Mark the second slot of a GD or LD entry.
+            -1 to indicate GD and -2 to indicate LD.  */
+         if (tls_type == (TLS_EXPLICIT | TLS_TLS | TLS_GD))
+           ppc64_elf_section_data (sec)->t_symndx[rel->r_offset / 8 + 1] = -1;
+         else if (tls_type == (TLS_EXPLICIT | TLS_TLS | TLS_LD))
+           ppc64_elf_section_data (sec)->t_symndx[rel->r_offset / 8 + 1] = -2;
+         goto dodyn;
+
+       case R_PPC64_TPREL16:
+       case R_PPC64_TPREL16_LO:
+       case R_PPC64_TPREL16_HI:
+       case R_PPC64_TPREL16_HA:
+       case R_PPC64_TPREL16_DS:
+       case R_PPC64_TPREL16_LO_DS:
+       case R_PPC64_TPREL16_HIGHER:
+       case R_PPC64_TPREL16_HIGHERA:
+       case R_PPC64_TPREL16_HIGHEST:
+       case R_PPC64_TPREL16_HIGHESTA:
+         if (info->shared)
+           {
+             info->flags |= DF_STATIC_TLS;
+             goto dodyn;
            }
          break;
 
@@ -2723,7 +3846,7 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
          if (opd_sym_map != NULL
              && h == NULL
              && rel + 1 < rel_end
-             && ((enum elf_ppc_reloc_type) ELF64_R_TYPE ((rel + 1)->r_info)
+             && ((enum elf_ppc64_reloc_type) ELF64_R_TYPE ((rel + 1)->r_info)
                  == R_PPC64_TOC))
            {
              asection *s;
@@ -2737,8 +3860,9 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
            }
          /* Fall through.  */
 
-       case R_PPC64_REL64:
+       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:
@@ -2753,16 +3877,23 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_ADDR24:
-       case R_PPC64_ADDR30:
        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)
+           /* We may need a copy reloc.  */
+           h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF;
+
          /* Don't propagate .opd relocs.  */
          if (NO_OPD_RELOCS && opd_sym_map != NULL)
            break;
 
+         /* Don't propagate relocs that the dynamic linker won't relocate.  */
+         if ((sec->flags & SEC_ALLOC) == 0)
+           break;
+
          /* If we are creating a shared library, and this is a reloc
             against a global symbol, or a non PC relative reloc
             against a local symbol, then we need to copy the reloc
@@ -2775,7 +3906,7 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
             later (it is never cleared).  In case of a weak definition,
             DEF_REGULAR may be cleared later by a strong definition in
             a shared library.  We account for that possibility below by
-            storing information in the relocs_copied field of the hash
+            storing information in the dyn_relocs field of the hash
             table entry.  A similar situation occurs when creating
             shared libraries and symbol visibility changes render the
             symbol local.
@@ -2784,16 +3915,16 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
             may need to keep relocations for symbols satisfied by a
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
+       dodyn:
          if ((info->shared
-              && (sec->flags & SEC_ALLOC) != 0
-              && (IS_ABSOLUTE_RELOC (r_type)
+              && (MUST_BE_DYN_RELOC (r_type)
                   || (h != NULL
                       && (! info->symbolic
                           || h->root.type == bfd_link_hash_defweak
                           || (h->elf_link_hash_flags
                               & ELF_LINK_HASH_DEF_REGULAR) == 0))))
-             || (!info->shared
-                 && (sec->flags & SEC_ALLOC) != 0
+             || (ELIMINATE_COPY_RELOCS
+                 && !info->shared
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
                      || (h->elf_link_hash_flags
@@ -2884,7 +4015,7 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
                }
 
              p->count += 1;
-             if (!IS_ABSOLUTE_RELOC (r_type))
+             if (!MUST_BE_DYN_RELOC (r_type))
                p->pc_count += 1;
            }
          break;
@@ -2912,10 +4043,10 @@ ppc64_elf_gc_mark_hook (sec, info, rel, h, sym)
 
   if (h != NULL)
     {
-      enum elf_ppc_reloc_type r_type;
+      enum elf_ppc64_reloc_type r_type;
       struct ppc_link_hash_entry *fdh;
 
-      r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info);
+      r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
       switch (r_type)
        {
        case R_PPC64_GNU_VTINHERIT:
@@ -2940,7 +4071,7 @@ ppc64_elf_gc_mark_hook (sec, info, rel, h, sym)
                 sections, as all functions are referenced in .opd.  */
              else if ((fdh->oh != NULL
                        && ((struct ppc_link_hash_entry *) fdh->oh)->is_entry)
-                      || elf_section_data (sec)->tdata == NULL)
+                      || ppc64_elf_section_data (sec)->opd.func_sec == NULL)
                rsec = h->root.u.def.section;
              break;
 
@@ -2958,10 +4089,10 @@ ppc64_elf_gc_mark_hook (sec, info, rel, h, sym)
       asection **opd_sym_section;
 
       rsec = bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-      opd_sym_section = (asection **) elf_section_data (rsec)->tdata;
+      opd_sym_section = ppc64_elf_section_data (rsec)->opd.func_sec;
       if (opd_sym_section != NULL)
        rsec = opd_sym_section[sym->st_value / 24];
-      else if (elf_section_data (sec)->tdata != NULL)
+      else if (ppc64_elf_section_data (sec)->opd.func_sec != NULL)
        rsec = NULL;
     }
 
@@ -2974,137 +4105,128 @@ ppc64_elf_gc_mark_hook (sec, info, rel, h, sym)
 static bfd_boolean
 ppc64_elf_gc_sweep_hook (abfd, info, sec, relocs)
      bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info;
      asection *sec;
      const Elf_Internal_Rela *relocs;
 {
+  struct ppc_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
+  struct got_entry **local_got_ents;
   const Elf_Internal_Rela *rel, *relend;
 
   elf_section_data (sec)->local_dynrel = NULL;
 
+  htab = ppc_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
-  local_got_refcounts = elf_local_got_refcounts (abfd);
+  local_got_ents = elf_local_got_ents (abfd);
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
     {
       unsigned long r_symndx;
-      enum elf_ppc_reloc_type r_type;
-      struct elf_link_hash_entry *h;
+      enum elf_ppc64_reloc_type r_type;
+      struct elf_link_hash_entry *h = NULL;
+      char tls_type = 0;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
-      r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info);
-      switch (r_type)
+      r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
+      if (r_symndx >= symtab_hdr->sh_info)
        {
-       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:
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             if (h->got.refcount > 0)
-               h->got.refcount--;
-           }
-         else
-           {
-             if (local_got_refcounts[r_symndx] > 0)
-               local_got_refcounts[r_symndx]--;
-           }
-         break;
-
-       case R_PPC64_PLT16_HA:
-       case R_PPC64_PLT16_HI:
-       case R_PPC64_PLT16_LO:
-       case R_PPC64_PLT32:
-       case R_PPC64_PLT64:
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             if (h->plt.refcount > 0)
-               h->plt.refcount--;
-           }
-         break;
-
-       case R_PPC64_REL14:
-       case R_PPC64_REL14_BRNTAKEN:
-       case R_PPC64_REL14_BRTAKEN:
-       case R_PPC64_REL24:
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             if (h->plt.refcount > 0)
-               h->plt.refcount--;
-           }
-         break;
+         struct ppc_link_hash_entry *eh;
+         struct ppc_dyn_relocs **pp;
+         struct ppc_dyn_relocs *p;
 
-       case R_PPC64_REL32:
-       case R_PPC64_REL64:
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             struct ppc_link_hash_entry *eh;
-             struct ppc_dyn_relocs **pp;
-             struct ppc_dyn_relocs *p;
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         eh = (struct ppc_link_hash_entry *) h;
 
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             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;
+             }
+       }
 
-             for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-               if (p->sec == sec)
-                 {
-                   p->pc_count -= 1;
-                   p->count -= 1;
-                   if (p->count == 0)
-                     *pp = p->next;
-                   break;
-                 }
-           }
-         break;
+      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:
+         htab->tlsld_got.refcount -= 1;
+         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_ADDR14:
-       case R_PPC64_ADDR14_BRNTAKEN:
-       case R_PPC64_ADDR14_BRTAKEN:
-       case R_PPC64_ADDR16:
-       case R_PPC64_ADDR16_DS:
-       case R_PPC64_ADDR16_HA:
-       case R_PPC64_ADDR16_HI:
-       case R_PPC64_ADDR16_HIGHER:
-       case R_PPC64_ADDR16_HIGHERA:
-       case R_PPC64_ADDR16_HIGHEST:
-       case R_PPC64_ADDR16_HIGHESTA:
-       case R_PPC64_ADDR16_LO:
-       case R_PPC64_ADDR16_LO_DS:
-       case R_PPC64_ADDR24:
-       case R_PPC64_ADDR30:
-       case R_PPC64_ADDR32:
-       case R_PPC64_ADDR64:
-       case R_PPC64_UADDR16:
-       case R_PPC64_UADDR32:
-       case R_PPC64_UADDR64:
-       case R_PPC64_TOC:
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             struct ppc_link_hash_entry *eh;
-             struct ppc_dyn_relocs **pp;
-             struct ppc_dyn_relocs *p;
+       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;
 
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             eh = (struct ppc_link_hash_entry *) h;
+           if (h != NULL)
+             ent = h->got.glist;
+           else
+             ent = local_got_ents[r_symndx];
 
-             for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-               if (p->sec == sec)
-                 {
-                   p->count -= 1;
-                   if (p->count == 0)
-                     *pp = p->next;
-                   break;
-                 }
+           for (; ent != NULL; ent = ent->next)
+             if (ent->addend == rel->r_addend
+                 && 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)
+               abort ();
+             if (ent->plt.refcount > 0)
+               ent->plt.refcount -= 1;
            }
          break;
 
@@ -3125,6 +4247,7 @@ func_desc_adjust (h, inf)
 {
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
+  struct plt_entry *ent;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -3144,7 +4267,10 @@ func_desc_adjust (h, inf)
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR))
     htab->have_undefweak = TRUE;
 
-  if (h->plt.refcount > 0
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->plt.refcount > 0)
+      break;
+  if (ent != NULL
       && h->root.root.string[0] == '.'
       && h->root.root.string[1] != '\0')
     {
@@ -3203,7 +4329,7 @@ func_desc_adjust (h, inf)
                                          | ELF_LINK_NON_GOT_REF));
          if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
            {
-             fdh->plt.refcount = h->plt.refcount;
+             fdh->plt.plist = h->plt.plist;
              fdh->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
            }
          ((struct ppc_link_hash_entry *) fdh)->is_func_descriptor = 1;
@@ -3362,8 +4488,6 @@ ppc64_elf_adjust_dynamic_symbol (info, h)
      struct elf_link_hash_entry *h;
 {
   struct ppc_link_hash_table *htab;
-  struct ppc_link_hash_entry * eh;
-  struct ppc_dyn_relocs *p;
   asection *s;
   unsigned int power_of_two;
 
@@ -3375,20 +4499,24 @@ ppc64_elf_adjust_dynamic_symbol (info, h)
     {
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
+      struct plt_entry *ent;
+      for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+       if (ent->plt.refcount > 0)
+         break;
       if (!((struct ppc_link_hash_entry *) h)->is_func_descriptor
-         || h->plt.refcount <= 0
+         || ent == NULL
          || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0
          || (! info->shared
              && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
              && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0))
        {
-         h->plt.offset = (bfd_vma) -1;
+         h->plt.plist = NULL;
          h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
        }
       return TRUE;
     }
   else
-    h->plt.offset = (bfd_vma) -1;
+    h->plt.plist = NULL;
 
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
@@ -3399,6 +4527,10 @@ ppc64_elf_adjust_dynamic_symbol (info, h)
                  || h->weakdef->root.type == bfd_link_hash_defweak);
       h->root.u.def.section = h->weakdef->root.u.def.section;
       h->root.u.def.value = h->weakdef->root.u.def.value;
+      if (ELIMINATE_COPY_RELOCS)
+       h->elf_link_hash_flags
+         = ((h->elf_link_hash_flags & ~ELF_LINK_NON_GOT_REF)
+            | (h->weakdef->elf_link_hash_flags & ELF_LINK_NON_GOT_REF));
       return TRUE;
     }
 
@@ -3417,20 +4549,26 @@ ppc64_elf_adjust_dynamic_symbol (info, h)
   if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0)
     return TRUE;
 
-  eh = (struct ppc_link_hash_entry *) h;
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+  if (ELIMINATE_COPY_RELOCS)
     {
-      s = p->sec->output_section;
-      if (s != NULL && (s->flags & SEC_READONLY) != 0)
-       break;
-    }
+      struct ppc_link_hash_entry * eh;
+      struct ppc_dyn_relocs *p;
 
-  /* 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 (p == NULL)
-    {
-      h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
-      return TRUE;
+      eh = (struct ppc_link_hash_entry *) h;
+      for (p = eh->dyn_relocs; p != NULL; p = p->next)
+       {
+         s = p->sec->output_section;
+         if (s != NULL && (s->flags & SEC_READONLY) != 0)
+           break;
+       }
+
+      /* 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 (p == NULL)
+       {
+         h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
+         return TRUE;
+       }
     }
 
   /* We must allocate the symbol in our .dynbss section, which will
@@ -3443,8 +4581,8 @@ ppc64_elf_adjust_dynamic_symbol (info, h)
      both the dynamic object and the regular object will refer to the
      same memory location for the variable.  */
 
-  /* We must generate a R_PPC_COPY reloc to tell the dynamic linker to
-     copy the initial value out of the dynamic object and into the
+  /* 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_ALLOC) != 0)
@@ -3537,17 +4675,166 @@ ppc64_elf_hide_symbol (info, h, force_local)
     }
 }
 
+static bfd_boolean
+get_sym_h (hp, symp, symsecp, tls_maskp, locsymsp, r_symndx, ibfd)
+     struct elf_link_hash_entry **hp;
+     Elf_Internal_Sym **symp;
+     asection **symsecp;
+     char **tls_maskp;
+     Elf_Internal_Sym **locsymsp;
+     unsigned long r_symndx;
+     bfd *ibfd;
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
+
+  if (r_symndx >= symtab_hdr->sh_info)
+    {
+      struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
+      struct elf_link_hash_entry *h;
+
+      h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if (hp != NULL)
+       *hp = h;
+
+      if (symp != NULL)
+       *symp = NULL;
+
+      if (symsecp != NULL)
+       {
+         asection *symsec = NULL;
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           symsec = h->root.u.def.section;
+         *symsecp = symsec;
+       }
+
+      if (tls_maskp != NULL)
+       {
+         struct ppc_link_hash_entry *eh;
+
+         eh = (struct ppc_link_hash_entry *) h;
+         *tls_maskp = &eh->tls_mask;
+       }
+    }
+  else
+    {
+      Elf_Internal_Sym *sym;
+      Elf_Internal_Sym *locsyms = *locsymsp;
+
+      if (locsyms == NULL)
+       {
+         locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+         if (locsyms == NULL)
+           locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
+                                           symtab_hdr->sh_info,
+                                           0, NULL, NULL, NULL);
+         if (locsyms == NULL)
+           return FALSE;
+         *locsymsp = locsyms;
+       }
+      sym = locsyms + r_symndx;
+
+      if (hp != NULL)
+       *hp = NULL;
+
+      if (symp != NULL)
+       *symp = sym;
+
+      if (symsecp != NULL)
+       {
+         asection *symsec = NULL;
+         if ((sym->st_shndx != SHN_UNDEF
+              && sym->st_shndx < SHN_LORESERVE)
+             || sym->st_shndx > SHN_HIRESERVE)
+           symsec = bfd_section_from_elf_index (ibfd, sym->st_shndx);
+         *symsecp = symsec;
+       }
+
+      if (tls_maskp != NULL)
+       {
+         struct got_entry **lgot_ents;
+         char *tls_mask;
+
+         tls_mask = NULL;
+         lgot_ents = elf_local_got_ents (ibfd);
+         if (lgot_ents != NULL)
+           {
+             char *lgot_masks = (char *) (lgot_ents + symtab_hdr->sh_info);
+             tls_mask = &lgot_masks[r_symndx];
+           }
+         *tls_maskp = tls_mask;
+       }
+    }
+  return TRUE;
+}
+
+/* Returns TLS_MASKP for the given REL symbol.  Function return is 0 on
+   error, 2 on a toc GD type suitable for optimization, 3 on a toc LD
+   type suitable for optimization, and 1 otherwise.  */  
+
+static int
+get_tls_mask (tls_maskp, locsymsp, rel, ibfd)
+     char **tls_maskp;
+     Elf_Internal_Sym **locsymsp;
+     const Elf_Internal_Rela *rel;
+     bfd *ibfd;
+{
+  unsigned long r_symndx;
+  unsigned int next_r;
+  struct elf_link_hash_entry *h;
+  Elf_Internal_Sym *sym;
+  asection *sec;
+  bfd_vma off;
+
+  r_symndx = ELF64_R_SYM (rel->r_info);
+  if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
+    return 0;
+
+  if ((*tls_maskp != NULL && **tls_maskp != 0)
+      || sec == NULL
+      || ppc64_elf_section_data (sec)->t_symndx == NULL)
+    return 1;
+
+  /* Look inside a TOC section too.  */
+  if (h != NULL)
+    {
+      BFD_ASSERT (h->root.type == bfd_link_hash_defined);
+      off = h->root.u.def.value;
+    }
+  else
+    off = sym->st_value;
+  off += rel->r_addend;
+  BFD_ASSERT (off % 8 == 0);
+  r_symndx = ppc64_elf_section_data (sec)->t_symndx[off / 8];
+  next_r = ppc64_elf_section_data (sec)->t_symndx[off / 8 + 1];
+  if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
+    return 0;
+  if (h == NULL
+      || h->root.type == bfd_link_hash_defined
+      || h->root.type == bfd_link_hash_defweak)
+    {
+      if (next_r == (unsigned) -1)
+       return 2;
+      if (next_r == (unsigned) -2
+         && (h == NULL
+             || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+       return 3;
+    }
+  return 1;
+}
+
 bfd_boolean
 ppc64_elf_edit_opd (obfd, info)
      bfd *obfd;
      struct bfd_link_info *info;
 {
   bfd *ibfd;
-  unsigned int bfd_indx;
 
-  for (bfd_indx = 0, ibfd = info->input_bfds;
-       ibfd != NULL;
-       ibfd = ibfd->link_next, bfd_indx++)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
       asection *sec;
       Elf_Internal_Rela *relstart, *rel, *relend;
@@ -3564,13 +4851,13 @@ ppc64_elf_edit_opd (obfd, info)
        continue;
 
       amt = sec->_raw_size * sizeof (long) / 24;
-      adjust = (long *) elf_section_data (sec)->tdata;
+      adjust = ppc64_elf_section_data (sec)->opd.adjust;
       if (adjust == NULL)
        {
          /* Must be a ld -r link.  ie. check_relocs hasn't been
             called.  */
          adjust = (long *) bfd_zalloc (obfd, amt);
-         elf_section_data (sec)->tdata = adjust;
+         ppc64_elf_section_data (sec)->opd.adjust = adjust;
        }
       memset (adjust, 0, (size_t) amt);
 
@@ -3586,9 +4873,9 @@ ppc64_elf_edit_opd (obfd, info)
       sym_hashes = elf_sym_hashes (ibfd);
 
       /* Read the relocations.  */
-      relstart = _bfd_elf64_link_read_relocs (ibfd, sec, (PTR) NULL,
-                                             (Elf_Internal_Rela *) NULL,
-                                             info->keep_memory);
+      relstart = _bfd_elf_link_read_relocs (ibfd, sec, (PTR) NULL,
+                                           (Elf_Internal_Rela *) NULL,
+                                           info->keep_memory);
       if (relstart == NULL)
        return FALSE;
 
@@ -3599,7 +4886,7 @@ ppc64_elf_edit_opd (obfd, info)
       relend = relstart + sec->reloc_count;
       for (rel = relstart; rel < relend; rel++)
        {
-         enum elf_ppc_reloc_type r_type;
+         enum elf_ppc64_reloc_type r_type;
          unsigned long r_symndx;
          asection *sym_sec;
          struct elf_link_hash_entry *h;
@@ -3608,7 +4895,7 @@ ppc64_elf_edit_opd (obfd, info)
          /* .opd contains a regular array of 24 byte entries.  We're
             only interested in the reloc pointing to a function entry
             point.  */
-         r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info);
+         r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
          if (r_type == R_PPC64_TOC)
            continue;
 
@@ -3623,7 +4910,7 @@ ppc64_elf_edit_opd (obfd, info)
 
          if (rel + 1 >= relend)
            continue;
-         r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE ((rel + 1)->r_info);
+         r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE ((rel + 1)->r_info);
          if (r_type != R_PPC64_TOC)
            continue;
 
@@ -3642,44 +4929,22 @@ ppc64_elf_edit_opd (obfd, info)
            }
 
          r_symndx = ELF64_R_SYM (rel->r_info);
-         sym_sec = NULL;
-         h = NULL;
-         sym = NULL;
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             while (h->root.type == bfd_link_hash_indirect
-                    || h->root.type == bfd_link_hash_warning)
-               h = (struct elf_link_hash_entry *) h->root.u.i.link;
-             if (h->root.type == bfd_link_hash_defined
-                 || h->root.type == bfd_link_hash_defweak)
-               sym_sec = h->root.u.def.section;
-           }
-         else
-           {
-             if (local_syms == NULL)
-               {
-                 local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
-                 if (local_syms == NULL)
-                   local_syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
-                                                      symtab_hdr->sh_info, 0,
-                                                      NULL, NULL, NULL);
-                 if (local_syms == NULL)
-                   goto error_free_rel;
-               }
-             sym = local_syms + r_symndx;
-             if ((sym->st_shndx != SHN_UNDEF
-                  && sym->st_shndx < SHN_LORESERVE)
-                 || sym->st_shndx > SHN_HIRESERVE)
-               sym_sec = bfd_section_from_elf_index (ibfd, sym->st_shndx);
-           }
+         if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                         r_symndx, ibfd))
+           goto error_free_rel;
 
          if (sym_sec == NULL || sym_sec->owner == NULL)
            {
+             const char *sym_name;
+             if (h != NULL)
+               sym_name = h->root.root.string;
+             else
+               sym_name = bfd_elf_local_sym_name (ibfd, sym);
+
              (*_bfd_error_handler)
                (_("%s: undefined sym `%s' in .opd section"),
                 bfd_archive_filename (ibfd),
-                h != NULL ? h->root.root.string : "<local symbol>");
+                sym_name);
              need_edit = FALSE;
              break;
            }
@@ -3744,28 +5009,8 @@ ppc64_elf_edit_opd (obfd, info)
                  Elf_Internal_Sym *sym;
 
                  r_symndx = ELF64_R_SYM (rel->r_info);
-                 sym_sec = NULL;
-                 h = NULL;
-                 sym = NULL;
-                 if (r_symndx >= symtab_hdr->sh_info)
-                   {
-                     h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-                     while (h->root.type == bfd_link_hash_indirect
-                            || h->root.type == bfd_link_hash_warning)
-                       h = (struct elf_link_hash_entry *) h->root.u.i.link;
-                     if (h->root.type == bfd_link_hash_defined
-                         || h->root.type == bfd_link_hash_defweak)
-                       sym_sec = h->root.u.def.section;
-                   }
-                 else
-                   {
-                     sym = local_syms + r_symndx;
-                     if ((sym->st_shndx != SHN_UNDEF
-                          && sym->st_shndx < SHN_LORESERVE)
-                         || sym->st_shndx > SHN_HIRESERVE)
-                       sym_sec = bfd_section_from_elf_index (ibfd,
-                                                             sym->st_shndx);
-                   }
+                 get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                            r_symndx, ibfd);
 
                  skip = (sym_sec->owner != ibfd
                          || sym_sec->output_section == bfd_abs_section_ptr);
@@ -3888,13 +5133,340 @@ ppc64_elf_edit_opd (obfd, info)
   return TRUE;
 }
 
+/* Set htab->tls_sec.  */
+
+bfd_boolean
+ppc64_elf_tls_setup (obfd, info)
+     bfd *obfd;
+     struct bfd_link_info *info;
+{
+  asection *tls;
+  struct ppc_link_hash_table *htab;
+
+  for (tls = obfd->sections; tls != NULL; tls = tls->next)
+    if ((tls->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
+       == (SEC_THREAD_LOCAL | SEC_LOAD))
+      break;
+
+  htab = ppc_hash_table (info);
+  htab->tls_sec = tls;
+
+  if (htab->tls_get_addr != NULL)
+    {
+      struct elf_link_hash_entry *h = htab->tls_get_addr;
+
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      htab->tls_get_addr = h;
+    }
+
+  return tls != NULL;
+}
+
+/* Run through all the TLS relocs looking for optimization
+   opportunities.  The linker has been hacked (see ppc64elf.em) to do
+   a preliminary section layout so that we know the TLS segment
+   offsets.  We can't optimize earlier because some optimizations need
+   to know the tp offset, and we need to optimize before allocating
+   dynamic relocations.  */
+
+bfd_boolean
+ppc64_elf_tls_optimize (obfd, info)
+     bfd *obfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info;
+{
+  bfd *ibfd;
+  asection *sec;
+  struct ppc_link_hash_table *htab;
+
+  if (info->relocateable || info->shared)
+    return TRUE;
+
+  htab = ppc_hash_table (info);
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    {
+      Elf_Internal_Sym *locsyms = NULL;
+
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
+         {
+           Elf_Internal_Rela *relstart, *rel, *relend;
+           int expecting_tls_get_addr;
+
+           /* Read the relocations.  */
+           relstart = _bfd_elf_link_read_relocs (ibfd, sec, (PTR) NULL,
+                                                 (Elf_Internal_Rela *) NULL,
+                                                 info->keep_memory);
+           if (relstart == NULL)
+             return FALSE;
+
+           expecting_tls_get_addr = 0;
+           relend = relstart + sec->reloc_count;
+           for (rel = relstart; rel < relend; rel++)
+             {
+               enum elf_ppc64_reloc_type r_type;
+               unsigned long r_symndx;
+               struct elf_link_hash_entry *h;
+               Elf_Internal_Sym *sym;
+               asection *sym_sec;
+               char *tls_mask;
+               char tls_set, tls_clear, tls_type = 0;
+               bfd_vma value;
+               bfd_boolean ok_tprel, is_local;
+
+               r_symndx = ELF64_R_SYM (rel->r_info);
+               if (!get_sym_h (&h, &sym, &sym_sec, &tls_mask, &locsyms,
+                               r_symndx, ibfd))
+                 {
+                 err_free_rel:
+                   if (elf_section_data (sec)->relocs != relstart)
+                     free (relstart);
+                   if (locsyms != NULL
+                       && (elf_tdata (ibfd)->symtab_hdr.contents
+                           != (unsigned char *) locsyms))
+                     free (locsyms);
+                   return FALSE;
+                 }
+
+               if (h != NULL)
+                 {
+                   if (h->root.type != bfd_link_hash_defined
+                       && h->root.type != bfd_link_hash_defweak)
+                     continue;
+                   value = h->root.u.def.value;
+                 }
+               else
+                 value = sym->st_value;
+
+               ok_tprel = FALSE;
+               is_local = FALSE;
+               if (h == NULL
+                   || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC))
+                 {
+                   is_local = TRUE;
+                   value += sym_sec->output_offset;
+                   value += sym_sec->output_section->vma;
+                   value -= htab->tls_sec->vma;
+                   ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31)
+                               < (bfd_vma) 1 << 32);
+                 }
+
+               r_type
+                 = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
+               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:
+                   /* These relocs should never be against a symbol
+                      defined in a shared lib.  Leave them alone if
+                      that turns out to be the case.  */
+                   htab->tlsld_got.refcount -= 1;
+                   if (!is_local)
+                     continue;
+
+                   /* LD -> LE */
+                   tls_set = 0;
+                   tls_clear = TLS_LD;
+                   tls_type = TLS_TLS | TLS_LD;
+                   expecting_tls_get_addr = 1;
+                   break;
+
+                 case R_PPC64_GOT_TLSGD16:
+                 case R_PPC64_GOT_TLSGD16_LO:
+                 case R_PPC64_GOT_TLSGD16_HI:
+                 case R_PPC64_GOT_TLSGD16_HA:
+                   if (ok_tprel)
+                     /* GD -> LE */
+                     tls_set = 0;
+                   else
+                     /* GD -> IE */
+                     tls_set = TLS_TLS | TLS_TPRELGD;
+                   tls_clear = TLS_GD;
+                   tls_type = TLS_TLS | TLS_GD;
+                   expecting_tls_get_addr = 1;
+                   break;
+
+                 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:
+                   expecting_tls_get_addr = 0;
+                   if (ok_tprel)
+                     {
+                       /* IE -> LE */
+                       tls_set = 0;
+                       tls_clear = TLS_TPREL;
+                       tls_type = TLS_TLS | TLS_TPREL;
+                       break;
+                     }
+                   else
+                     continue;
+
+                 case R_PPC64_REL14:
+                 case R_PPC64_REL14_BRTAKEN:
+                 case R_PPC64_REL14_BRNTAKEN:
+                 case R_PPC64_REL24:
+                   if (h != NULL
+                       && h == htab->tls_get_addr)
+                     {
+                       if (!expecting_tls_get_addr
+                           && rel != relstart
+                           && ((ELF64_R_TYPE (rel[-1].r_info)
+                                == R_PPC64_TOC16)
+                               || (ELF64_R_TYPE (rel[-1].r_info)
+                                   == R_PPC64_TOC16_LO)))
+                         {
+                           /* Check for toc tls entries.  */
+                           char *toc_tls;
+                           int retval;
+
+                           retval = get_tls_mask (&toc_tls, &locsyms,
+                                                  rel - 1, ibfd);
+                           if (retval == 0)
+                             goto err_free_rel;
+                           if (toc_tls != NULL)
+                             expecting_tls_get_addr = retval > 1;
+                         }
+
+                       if (expecting_tls_get_addr)
+                         {
+                           struct plt_entry *ent;
+                           for (ent = h->plt.plist; ent; ent = ent->next)
+                             if (ent->addend == 0)
+                               {
+                                 if (ent->plt.refcount > 0)
+                                   ent->plt.refcount -= 1;
+                                 break;
+                               }
+                         }
+                     }
+                   expecting_tls_get_addr = 0;
+                   continue;
+
+                 case R_PPC64_TPREL64:
+                   expecting_tls_get_addr = 0;
+                   if (ok_tprel)
+                     {
+                       /* IE -> LE */
+                       tls_set = TLS_EXPLICIT;
+                       tls_clear = TLS_TPREL;
+                       break;
+                     }
+                   else
+                     continue;
+
+                 case R_PPC64_DTPMOD64:
+                   expecting_tls_get_addr = 0;
+                   if (rel + 1 < relend
+                       && (rel[1].r_info
+                           == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64))
+                       && rel[1].r_offset == rel->r_offset + 8)
+                     {
+                       if (ok_tprel)
+                         /* GD -> LE */
+                         tls_set = TLS_EXPLICIT | TLS_GD;
+                       else
+                         /* GD -> IE */
+                         tls_set = TLS_EXPLICIT | TLS_GD | TLS_TPRELGD;
+                       tls_clear = TLS_GD;
+                     }
+                   else
+                     {
+                       if (!is_local)
+                         continue;
+
+                       /* LD -> LE */
+                       tls_set = TLS_EXPLICIT;
+                       tls_clear = TLS_LD;
+                     }
+                   break;
+
+                 default:
+                   expecting_tls_get_addr = 0;
+                   continue;
+                 }
+
+               if ((tls_set & TLS_EXPLICIT) == 0)
+                 {
+                   struct got_entry *ent;
+
+                   /* Adjust got entry for this reloc.  */
+                   if (h != NULL)
+                     ent = h->got.glist;
+                   else
+                     ent = elf_local_got_ents (ibfd)[r_symndx];
+
+                   for (; ent != NULL; ent = ent->next)
+                     if (ent->addend == rel->r_addend
+                         && ent->tls_type == tls_type)
+                       break;
+                   if (ent == NULL)
+                     abort ();
+
+                   if (tls_set == 0)
+                     {
+                       /* We managed to get rid of a got entry.  */
+                       if (ent->got.refcount > 0)
+                         ent->got.refcount -= 1;
+                     }
+                 }
+               else if (h != NULL)
+                 {
+                   struct ppc_link_hash_entry * eh;
+                   struct ppc_dyn_relocs **pp;
+                   struct ppc_dyn_relocs *p;
+
+                   /* Adjust dynamic relocs.  */
+                   eh = (struct ppc_link_hash_entry *) h;
+                   for (pp = &eh->dyn_relocs;
+                        (p = *pp) != NULL;
+                        pp = &p->next)
+                     if (p->sec == sec)
+                       {
+                         /* If we got rid of a DTPMOD/DTPREL reloc
+                            pair then we'll lose one or two dyn
+                            relocs.  */
+                         if (tls_set == (TLS_EXPLICIT | TLS_GD))
+                           p->count -= 1;
+                         p->count -= 1;
+                         if (p->count == 0)
+                           *pp = p->next;
+                         break;
+                       }
+                 }
+
+               *tls_mask |= tls_set;
+               *tls_mask &= ~tls_clear;
+             }
+
+           if (elf_section_data (sec)->relocs != relstart)
+             free (relstart);
+         }
+
+      if (locsyms != NULL
+         && (elf_tdata (ibfd)->symtab_hdr.contents
+             != (unsigned char *) locsyms))
+       {
+         if (!info->keep_memory)
+           free (locsyms);
+         else
+           elf_tdata (ibfd)->symtab_hdr.contents = (unsigned char *) locsyms;
+       }
+    }
+  return TRUE;
+}
+
 /* This is the condition under which ppc64_elf_finish_dynamic_symbol
    will be called from elflink.h.  If elflink.h doesn't call our
    finish_dynamic_symbol routine, we'll need to do something about
    initializing any .plt and .got entries in ppc64_elf_relocate_section.  */
-#define WILL_CALL_FINISH_DYNAMIC_SYMBOL(DYN, INFO, H) \
+#define WILL_CALL_FINISH_DYNAMIC_SYMBOL(DYN, SHARED, H) \
   ((DYN)                                                               \
-   && ((INFO)->shared                                                  \
+   && ((SHARED)                                                                \
        || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)     \
    && ((H)->dynindx != -1                                              \
        || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0))
@@ -3912,6 +5484,7 @@ allocate_dynrelocs (h, inf)
   asection *s;
   struct ppc_link_hash_entry *eh;
   struct ppc_dyn_relocs *p;
+  struct got_entry *gent;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -3923,73 +5496,121 @@ allocate_dynrelocs (h, inf)
   htab = ppc_hash_table (info);
 
   if (htab->elf.dynamic_sections_created
-      && h->plt.refcount > 0
-      && h->dynindx != -1)
+      && h->dynindx != -1
+      && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)
+      && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+         || h->root.type != bfd_link_hash_undefweak))
     {
-      BFD_ASSERT (((struct ppc_link_hash_entry *) h)->is_func_descriptor);
-
-      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info, h))
-       {
-         /* If this is the first .plt entry, make room for the special
-            first entry.  */
-         s = htab->splt;
-         if (s->_raw_size == 0)
-           s->_raw_size += PLT_INITIAL_ENTRY_SIZE;
-
-         h->plt.offset = s->_raw_size;
-
-         /* Make room for this entry.  */
-         s->_raw_size += PLT_ENTRY_SIZE;
-
-         /* Make room for the .glink code.  */
-         s = htab->sglink;
-         if (s->_raw_size == 0)
-           s->_raw_size += GLINK_CALL_STUB_SIZE;
-         /* We need bigger stubs past index 32767.  */
-         if (s->_raw_size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
-           s->_raw_size += 4;
-         s->_raw_size += 2*4;
-
-         /* We also need to make an entry in the .rela.plt section.  */
-         s = htab->srelplt;
-         s->_raw_size += sizeof (Elf64_External_Rela);
-       }
-      else
+      struct plt_entry *pent;
+      bfd_boolean doneone = FALSE;
+      for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+       if (pent->plt.refcount > 0)
+         {
+           BFD_ASSERT (((struct ppc_link_hash_entry *) h)->is_func_descriptor);
+
+           /* If this is the first .plt entry, make room for the special
+              first entry.  */
+           s = htab->splt;
+           if (s->_raw_size == 0)
+             s->_raw_size += PLT_INITIAL_ENTRY_SIZE;
+
+           pent->plt.offset = s->_raw_size;
+
+           /* Make room for this entry.  */
+           s->_raw_size += PLT_ENTRY_SIZE;
+
+           /* Make room for the .glink code.  */
+           s = htab->sglink;
+           if (s->_raw_size == 0)
+             s->_raw_size += GLINK_CALL_STUB_SIZE;
+           /* We need bigger stubs past index 32767.  */
+           if (s->_raw_size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+             s->_raw_size += 4;
+           s->_raw_size += 2*4;
+
+           /* We also need to make an entry in the .rela.plt section.  */
+           s = htab->srelplt;
+           s->_raw_size += sizeof (Elf64_External_Rela);
+           doneone = TRUE;
+         }
+       else
+         pent->plt.offset = (bfd_vma) -1;
+      if (!doneone)
        {
-         h->plt.offset = (bfd_vma) -1;
+         h->plt.plist = NULL;
          h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
        }
     }
   else
     {
-      h->plt.offset = (bfd_vma) -1;
+      h->plt.plist = NULL;
       h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
     }
 
-  if (h->got.refcount > 0)
-    {
-      bfd_boolean dyn;
-
-      /* Make sure this symbol is output as a dynamic symbol.
-        Undefined weak syms won't yet be marked as dynamic.  */
-      if (h->dynindx == -1
-         && (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+  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)
        {
-         if (! bfd_elf64_link_record_dynamic_symbol (info, h))
-           return FALSE;
+         /* 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)
+             {
+               gent->got.refcount = 0;
+               break;
+             }
+
+         /* If not, then we'll be using our own TPREL entry.  */
+         if (gent->got.refcount != 0)
+           gent->tls_type = TLS_TLS | TLS_TPREL;
        }
 
-      s = htab->sgot;
-      h->got.offset = s->_raw_size;
-      s->_raw_size += 8;
-      dyn = htab->elf.dynamic_sections_created;
-      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h))
-       htab->srelgot->_raw_size += sizeof (Elf64_External_Rela);
-    }
-  else
-    h->got.offset = (bfd_vma) -1;
+  for (gent = h->got.glist; gent != NULL; gent = gent->next)
+    if (gent->got.refcount > 0)
+      {
+       bfd_boolean dyn;
+
+       /* 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->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+         {
+           if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+             return FALSE;
+         }
+
+       if ((gent->tls_type & TLS_LD) != 0
+           && !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC))
+         {
+           gent->got.offset = htab->tlsld_got.offset;
+           continue;
+         }
+
+       s = htab->sgot;
+       gent->got.offset = s->_raw_size;
+       s->_raw_size
+         += (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)) ? 16 : 8;
+       dyn = htab->elf.dynamic_sections_created;
+       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))
+         htab->srelgot->_raw_size
+           += (gent->tls_type & eh->tls_mask & TLS_GD
+               ? 2 * sizeof (Elf64_External_Rela)
+               : sizeof (Elf64_External_Rela));
+      }
+    else
+      gent->got.offset = (bfd_vma) -1;
 
-  eh = (struct ppc_link_hash_entry *) h;
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -4017,19 +5638,22 @@ allocate_dynrelocs (h, inf)
                pp = &p->next;
            }
        }
+
+      /* Also discard relocs on undefined weak syms with non-default
+        visibility.  */
+      if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+         && h->root.type == bfd_link_hash_undefweak)
+       eh->dyn_relocs = NULL;
     }
-  else
+  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->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
-         && (((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-              && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
-             || (htab->elf.dynamic_sections_created
-                 && (h->root.type == bfd_link_hash_undefweak
-                     || h->root.type == bfd_link_hash_undefined))))
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
        {
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
@@ -4123,12 +5747,23 @@ ppc64_elf_size_dynamic_sections (output_bfd, info)
        }
     }
 
+  if (htab->tlsld_got.refcount > 0)
+    {
+      htab->tlsld_got.offset = htab->sgot->_raw_size;
+      htab->sgot->_raw_size += 16;
+      if (info->shared)
+       htab->srelgot->_raw_size += sizeof (Elf64_External_Rela);
+    }
+  else
+    htab->tlsld_got.offset = (bfd_vma) -1;
+
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
-      bfd_signed_vma *local_got;
-      bfd_signed_vma *end_local_got;
+      struct got_entry **lgot_ents;
+      struct got_entry **end_lgot_ents;
+      char *lgot_masks;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
@@ -4163,26 +5798,53 @@ ppc64_elf_size_dynamic_sections (output_bfd, info)
            }
        }
 
-      local_got = elf_local_got_refcounts (ibfd);
-      if (!local_got)
+      lgot_ents = elf_local_got_ents (ibfd);
+      if (!lgot_ents)
        continue;
 
       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
       locsymcount = symtab_hdr->sh_info;
-      end_local_got = local_got + locsymcount;
+      end_lgot_ents = lgot_ents + locsymcount;
+      lgot_masks = (char *) end_lgot_ents;
       s = htab->sgot;
       srel = htab->srelgot;
-      for (; local_got < end_local_got; ++local_got)
+      for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
        {
-         if (*local_got > 0)
-           {
-             *local_got = s->_raw_size;
-             s->_raw_size += 8;
-             if (info->shared)
-               srel->_raw_size += sizeof (Elf64_External_Rela);
-           }
-         else
-           *local_got = (bfd_vma) -1;
+         struct got_entry *ent;
+
+         for (ent = *lgot_ents; ent != NULL; ent = ent->next)
+           if (ent->got.refcount > 0)
+             {
+               if ((ent->tls_type & *lgot_masks & TLS_LD) != 0)
+                 {
+                   if (htab->tlsld_got.offset == (bfd_vma) -1)
+                     {
+                       htab->tlsld_got.offset = s->_raw_size;
+                       s->_raw_size += 16;
+                       if (info->shared)
+                         srel->_raw_size += sizeof (Elf64_External_Rela);
+                     }
+                   ent->got.offset = htab->tlsld_got.offset;
+                 }
+               else
+                 {
+                   ent->got.offset = s->_raw_size;
+                   if ((ent->tls_type & *lgot_masks & TLS_GD) != 0)
+                     {
+                       s->_raw_size += 16;
+                       if (info->shared)
+                         srel->_raw_size += 2 * sizeof (Elf64_External_Rela);
+                     }
+                   else
+                     {
+                       s->_raw_size += 8;
+                       if (info->shared)
+                         srel->_raw_size += sizeof (Elf64_External_Rela);
+                     }
+                 }
+             }
+           else
+             ent->got.offset = (bfd_vma) -1;
        }
     }
 
@@ -4198,6 +5860,10 @@ ppc64_elf_size_dynamic_sections (output_bfd, info)
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
+      /* Reset _cooked_size since prelim layout will set it wrongly,
+        and a non-zero _cooked_size sticks.  */
+      s->_cooked_size = 0;
+
       if (s == htab->sbrlt || s == htab->srelbrlt)
        /* These haven't been allocated yet;  don't strip.  */
        continue;
@@ -4251,8 +5917,10 @@ ppc64_elf_size_dynamic_sections (output_bfd, info)
       /* Allocate memory for the section contents.  We use bfd_zalloc
         here in case unused entries are not reclaimed before the
         section's contents are written out.  This should not happen,
-        but this way if it does, we get a R_PPC64_NONE reloc instead
-        of garbage.  */
+        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.  */
       s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
       if (s->contents == NULL)
        return FALSE;
@@ -4334,11 +6002,16 @@ ppc_type_of_stub (input_sec, rel, hash, destination)
   if (h != NULL)
     {
       if (h->oh != NULL
-         && h->oh->plt.offset != (bfd_vma) -1
          && h->oh->dynindx != -1)
        {
-         *hash = (struct ppc_link_hash_entry *) h->oh;
-         return ppc_stub_plt_call;
+         struct plt_entry *ent;
+         for (ent = h->oh->plt.plist; ent != NULL; ent = ent->next)
+           if (ent->addend == rel->r_addend
+               && ent->plt.offset != (bfd_vma) -1)
+             {
+               *hash = (struct ppc_link_hash_entry *) h->oh;
+               return ppc_stub_plt_call;
+             }
        }
 
       if (h->elf.root.type == bfd_link_hash_undefweak
@@ -4413,6 +6086,7 @@ ppc_build_one_stub (gen_entry, in_arg)
   bfd_byte *loc;
   bfd_byte *p;
   unsigned int indx;
+  struct plt_entry *ent;
   bfd_vma off;
   int size;
 
@@ -4524,7 +6198,13 @@ ppc_build_one_stub (gen_entry, in_arg)
        }
 
       /* Now build the stub.  */
-      off = stub_entry->h->elf.plt.offset;
+      off = (bfd_vma) -1;
+      for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next)
+       if (ent->addend == stub_entry->addend)
+         {
+           off = ent->plt.offset;
+           break;
+         }
       if (off >= (bfd_vma) -2)
        abort ();
 
@@ -4577,7 +6257,16 @@ ppc_size_one_stub (gen_entry, in_arg)
 
   if (stub_entry->stub_type == ppc_stub_plt_call)
     {
-      off = stub_entry->h->elf.plt.offset & ~(bfd_vma) 1;
+      struct plt_entry *ent;
+      off = (bfd_vma) -1;
+      for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next)
+       if (ent->addend == stub_entry->addend)
+         {
+           off = ent->plt.offset & ~(bfd_vma) 1;
+           break;
+         }
+      if (off >= (bfd_vma) -2)
+       abort ();
       off += (htab->splt->output_offset
              + htab->splt->output_section->vma
              - elf_gp (htab->splt->output_section->owner)
@@ -4756,12 +6445,15 @@ group_sections (htab, stub_group_size, stubs_always_before_branch)
          asection *curr;
          asection *prev;
          bfd_size_type total;
+         bfd_boolean big_sec;
 
          curr = tail;
          if (tail->_cooked_size)
            total = tail->_cooked_size;
          else
            total = tail->_raw_size;
+         big_sec = total >= stub_group_size;
+
          while ((prev = PREV_SEC (curr)) != NULL
                 && ((total += curr->output_offset - prev->output_offset)
                     < stub_group_size))
@@ -4776,7 +6468,7 @@ group_sections (htab, stub_group_size, stubs_always_before_branch)
             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 2834432 bytes, or over 100000 plt call stubs.  */
+            more than 2097152 bytes, or nearly 75000 plt call stubs.  */
          do
            {
              prev = PREV_SEC (tail);
@@ -4786,8 +6478,11 @@ group_sections (htab, stub_group_size, stubs_always_before_branch)
          while (tail != curr && (tail = prev) != NULL);
 
          /* But wait, there's more!  Input sections up to stub_group_size
-            bytes before the stub section can be handled by it too.  */
-         if (!stubs_always_before_branch)
+            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
+            branches may not reach into the stub section.  */
+         if (!stubs_always_before_branch && !big_sec)
            {
              total = 0;
              while (prev != NULL
@@ -4839,9 +6534,18 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
   if (stub_group_size == 1)
     {
       /* Default values.  */
-      stub_group_size = 30720000;
-      if (htab->has_14bit_branch)
-       stub_group_size = 30000;
+      if (stubs_always_before_branch)
+       {
+         stub_group_size = 0x1e00000;
+         if (htab->has_14bit_branch)
+           stub_group_size = 0x7800;
+       }
+      else
+       {
+         stub_group_size = 0x1c00000;
+         if (htab->has_14bit_branch)
+           stub_group_size = 0x7000;
+       }
     }
 
   group_sections (htab, stub_group_size, stubs_always_before_branch);
@@ -4890,9 +6594,9 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
 
              /* Get the relocs.  */
              internal_relocs
-               = _bfd_elf64_link_read_relocs (input_bfd, section, NULL,
-                                              (Elf_Internal_Rela *) NULL,
-                                              info->keep_memory);
+               = _bfd_elf_link_read_relocs (input_bfd, section, NULL,
+                                            (Elf_Internal_Rela *) NULL,
+                                            info->keep_memory);
              if (internal_relocs == NULL)
                goto error_ret_free_local;
 
@@ -4908,13 +6612,15 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
                  bfd_vma sym_value;
                  bfd_vma destination;
                  struct ppc_link_hash_entry *hash;
+                 struct elf_link_hash_entry *h;
+                 Elf_Internal_Sym *sym;
                  char *stub_name;
                  const asection *id_sec;
 
                  r_type = ELF64_R_TYPE (irela->r_info);
                  r_indx = ELF64_R_SYM (irela->r_info);
 
-                 if (r_type >= (unsigned int) R_PPC_max)
+                 if (r_type >= (unsigned int) R_PPC64_max)
                    {
                      bfd_set_error (bfd_error_bad_value);
                      goto error_ret_free_internal;
@@ -4929,33 +6635,16 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
 
                  /* Now determine the call target, its name, value,
                     section.  */
-                 sym_sec = NULL;
-                 sym_value = 0;
                  destination = 0;
-                 hash = NULL;
-                 if (r_indx < symtab_hdr->sh_info)
+                 if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                                 r_indx, input_bfd))
+                   goto error_ret_free_internal;
+                 hash = (struct ppc_link_hash_entry *) h;
+
+                 if (hash == NULL)
                    {
                      /* It's a local symbol.  */
-                     Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
-
-                     if (local_syms == NULL)
-                       {
-                         local_syms
-                           = (Elf_Internal_Sym *) symtab_hdr->contents;
-                         if (local_syms == NULL)
-                           local_syms
-                             = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
-                                                     symtab_hdr->sh_info, 0,
-                                                     NULL, NULL, NULL);
-                         if (local_syms == NULL)
-                           goto error_ret_free_internal;
-                       }
-                     sym = local_syms + r_indx;
-                     hdr = elf_elfsections (input_bfd)[sym->st_shndx];
-                     sym_sec = hdr->bfd_section;
-                     if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
-                       sym_value = sym->st_value;
+                     sym_value = sym->st_value;
                      destination = (sym_value + irela->r_addend
                                     + sym_sec->output_offset
                                     + sym_sec->output_section->vma);
@@ -4963,21 +6652,10 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
                  else
                    {
                      /* It's an external symbol.  */
-                     int e_indx;
-
-                     e_indx = r_indx - symtab_hdr->sh_info;
-                     hash = ((struct ppc_link_hash_entry *)
-                             elf_sym_hashes (input_bfd)[e_indx]);
-
-                     while (hash->elf.root.type == bfd_link_hash_indirect
-                            || hash->elf.root.type == bfd_link_hash_warning)
-                       hash = ((struct ppc_link_hash_entry *)
-                               hash->elf.root.u.i.link);
-
+                     sym_value = 0;
                      if (hash->elf.root.type == bfd_link_hash_defined
                          || hash->elf.root.type == bfd_link_hash_defweak)
                        {
-                         sym_sec = hash->elf.root.u.def.section;
                          sym_value = hash->elf.root.u.def.value;
                          if (sym_sec->output_section != NULL)
                            destination = (sym_value + irela->r_addend
@@ -5001,6 +6679,23 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
                  if (stub_type == ppc_stub_none)
                    continue;
 
+                 /* __tls_get_addr calls might be eliminated.  */
+                 if (stub_type != ppc_stub_plt_call
+                     && hash != NULL
+                     && &hash->elf == htab->tls_get_addr
+                     && section->has_tls_reloc
+                     && irela != internal_relocs)
+                   {
+                     /* Get tls info.  */
+                     char *tls_mask;
+
+                     if (!get_tls_mask (&tls_mask, &local_syms,
+                                        irela - 1, input_bfd))
+                       goto error_ret_free_internal;
+                     if (*tls_mask != 0)
+                       continue;
+                   }
+
                  /* Support for grouping stub sections.  */
                  id_sec = htab->stub_group[section->id].link_sec;
 
@@ -5037,6 +6732,7 @@ ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size,
                  stub_entry->target_section = sym_sec;
                  stub_entry->stub_type = stub_type;
                  stub_entry->h = hash;
+                 stub_entry->addend = irela->r_addend;
                  stub_changed = TRUE;
                }
 
@@ -5287,7 +6983,9 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  bfd_vma *local_got_offsets;
+  Elf_Internal_Rela outrel;
+  bfd_byte *loc;
+  struct got_entry **local_got_ents;
   bfd_vma TOCstart;
   bfd_boolean ret = TRUE;
   bfd_boolean is_opd;
@@ -5302,18 +7000,17 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
     ppc_howto_init ();
 
   htab = ppc_hash_table (info);
-  local_got_offsets = elf_local_got_offsets (input_bfd);
+  local_got_ents = elf_local_got_ents (input_bfd);
   TOCstart = elf_gp (output_bfd);
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
-  is_opd = elf_section_data (input_section)->tdata != NULL;
+  is_opd = ppc64_elf_section_data (input_section)->opd.adjust != NULL;
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      enum elf_ppc_reloc_type r_type;
-      bfd_vma offset;
+      enum elf_ppc64_reloc_type r_type;
       bfd_vma addend;
       bfd_reloc_status_type r;
       Elf_Internal_Sym *sym;
@@ -5322,6 +7019,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
       struct elf_link_hash_entry *fdh;
       const char *sym_name;
       unsigned long r_symndx;
+      char tls_mask, tls_gd, tls_type;
       bfd_vma relocation;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
@@ -5330,11 +7028,8 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
       bfd_vma max_br_offset;
       bfd_vma from;
 
-      r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info);
+      r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel->r_info);
       r_symndx = ELF64_R_SYM (rel->r_info);
-      offset = rel->r_offset;
-      addend = rel->r_addend;
-      r = bfd_reloc_other;
       sym = (Elf_Internal_Sym *) 0;
       sec = (asection *) 0;
       h = (struct elf_link_hash_entry *) 0;
@@ -5352,17 +7047,13 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          /* It's a local symbol.  */
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
-         sym_name = "<local symbol>";
-
+         sym_name = bfd_elf_local_sym_name (input_bfd, sym);
          relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
-         /* rel may have changed, update our copy of addend.  */
-         addend = rel->r_addend;
-
          if (elf_section_data (sec) != NULL)
            {
              long *opd_sym_adjust;
 
-             opd_sym_adjust = (long *) elf_section_data (sec)->tdata;
+             opd_sym_adjust = ppc64_elf_section_data (sec)->opd.adjust;
              if (opd_sym_adjust != NULL && sym->st_value % 24 == 0)
                relocation += opd_sym_adjust[sym->st_value / 24];
            }
@@ -5394,7 +7085,6 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          else if (h->root.type == bfd_link_hash_undefweak)
            ;
          else if (info->shared
-                  && (!info->symbolic || info->allow_shlib_undefined)
                   && !info->no_undefined
                   && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
            ;
@@ -5402,15 +7092,323 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            {
              if (! ((*info->callbacks->undefined_symbol)
                     (info, h->root.root.string, input_bfd, input_section,
-                     offset, (!info->shared
-                              || info->no_undefined
-                              || ELF_ST_VISIBILITY (h->other)))))
+                     rel->r_offset, (!info->shared
+                                     || info->no_undefined
+                                     || ELF_ST_VISIBILITY (h->other)))))
                return FALSE;
              warned = TRUE;
            }
        }
 
-      /* First handle relocations that tweak non-addend part of insn.  */
+      /* TLS optimizations.  Replace instruction sequences and relocs
+        based on information we collected in tls_optimize.  We edit
+        RELOCS so that --emit-relocs will output something sensible
+        for the final instruction stream.  */
+      tls_mask = 0;
+      tls_gd = 0;
+      if (IS_PPC64_TLS_RELOC (r_type))
+       {
+         if (h != NULL)
+           tls_mask = ((struct ppc_link_hash_entry *) h)->tls_mask;
+         else if (local_got_ents != NULL)
+           {
+             char *lgot_masks;
+             lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
+             tls_mask = lgot_masks[r_symndx];
+           }
+       }
+
+      /* Ensure reloc mapping code below stays sane.  */
+      if (R_PPC64_TOC16_LO_DS != R_PPC64_TOC16_DS + 1
+         || R_PPC64_TOC16_LO != R_PPC64_TOC16 + 1
+         || (R_PPC64_GOT_TLSLD16 & 3)    != (R_PPC64_GOT_TLSGD16 & 3)
+         || (R_PPC64_GOT_TLSLD16_LO & 3) != (R_PPC64_GOT_TLSGD16_LO & 3)
+         || (R_PPC64_GOT_TLSLD16_HI & 3) != (R_PPC64_GOT_TLSGD16_HI & 3)
+         || (R_PPC64_GOT_TLSLD16_HA & 3) != (R_PPC64_GOT_TLSGD16_HA & 3)
+         || (R_PPC64_GOT_TLSLD16 & 3)    != (R_PPC64_GOT_TPREL16_DS & 3)
+         || (R_PPC64_GOT_TLSLD16_LO & 3) != (R_PPC64_GOT_TPREL16_LO_DS & 3)
+         || (R_PPC64_GOT_TLSLD16_HI & 3) != (R_PPC64_GOT_TPREL16_HI & 3)
+         || (R_PPC64_GOT_TLSLD16_HA & 3) != (R_PPC64_GOT_TPREL16_HA & 3))
+       abort ();
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case R_PPC64_TOC16:
+       case R_PPC64_TOC16_LO:
+       case R_PPC64_TOC16_DS:
+       case R_PPC64_TOC16_LO_DS:
+         {
+           /* Check for toc tls entries.  */
+           char *toc_tls;
+           int retval;
+
+           retval = get_tls_mask (&toc_tls, &local_syms, rel, input_bfd);
+           if (retval == 0)
+             return FALSE;
+
+           if (toc_tls)
+             {
+               tls_mask = *toc_tls;
+               if (r_type == R_PPC64_TOC16_DS
+                   || r_type == R_PPC64_TOC16_LO_DS)
+                 goto toctprel;
+               else
+                 {
+                   /* If we found a GD reloc pair, then we might be
+                      doing a GD->IE transition.  */
+                   if (retval == 2)
+                     {
+                       tls_gd = TLS_TPRELGD;
+                       if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+                         goto tls_get_addr_check;
+                     }
+                   else if (retval == 3)
+                     {
+                       if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+                         goto tls_get_addr_check;
+                     }
+                 }
+             }
+         }
+         break;
+
+       case R_PPC64_GOT_TPREL16_DS:
+       case R_PPC64_GOT_TPREL16_LO_DS:
+       toctprel:
+         if (tls_mask != 0
+             && (tls_mask & TLS_TPREL) == 0)
+           {
+             bfd_vma insn;
+             insn = bfd_get_32 (output_bfd, contents + rel->r_offset - 2);
+             insn &= 31 << 21;
+             insn |= 0x3c0d0000;       /* addis 0,13,0 */
+             bfd_put_32 (output_bfd, insn, contents + rel->r_offset - 2);
+             r_type = R_PPC64_TPREL16_HA;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+           }
+         break;
+
+       case R_PPC64_TLS:
+         if (tls_mask == 0)
+           {
+             /* Check for toc tls entries.  */
+             char *toc_tls;
+
+             if (!get_tls_mask (&toc_tls, &local_syms, rel, input_bfd))
+               return FALSE;
+
+             if (toc_tls)
+               tls_mask = *toc_tls;
+           }
+         if (tls_mask != 0
+             && (tls_mask & TLS_TPREL) == 0)
+           {
+             bfd_vma insn, rtra;
+             insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
+             if ((insn & ((31 << 26) | (31 << 11)))
+                 == ((31 << 26) | (13 << 11)))
+               rtra = insn & ((1 << 26) - (1 << 16));
+             else if ((insn & ((31 << 26) | (31 << 16)))
+                      == ((31 << 26) | (13 << 16)))
+               rtra = (insn & (31 << 21)) | ((insn & (31 << 11)) << 5);
+             else
+               abort ();
+             if ((insn & ((1 << 11) - (1 << 1))) == 266 << 1)
+               /* add -> addi.  */
+               insn = 14 << 26;
+             else if ((insn & (31 << 1)) == 23 << 1
+                      && ((insn & (31 << 6)) < 14 << 6
+                          || ((insn & (31 << 6)) >= 16 << 6
+                              && (insn & (31 << 6)) < 24 << 6)))
+               /* load and store indexed -> dform.  */
+               insn = (32 | ((insn >> 6) & 31)) << 26;
+             else if ((insn & (31 << 1)) == 21 << 1
+                      && (insn & (0x1a << 6)) == 0)
+               /* ldx, ldux, stdx, stdux -> ld, ldu, std, stdu.  */
+               insn = (((58 | ((insn >> 6) & 4)) << 26)
+                       | ((insn >> 6) & 1));
+             else if ((insn & (31 << 1)) == 21 << 1
+                      && (insn & ((1 << 11) - (1 << 1))) == 341 << 1)
+               /* lwax -> lwa.  */
+               insn = (58 << 26) | 2;
+             else
+               abort ();
+             insn |= rtra;
+             bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+             r_type = R_PPC64_TPREL16_LO;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+             /* Was PPC64_TLS which sits on insn boundary, now
+                PPC64_TPREL16_LO which is at insn+2.  */
+             rel->r_offset += 2;
+           }
+         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)
+           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)
+           {
+           tls_gdld_hi:
+             if ((tls_mask & tls_gd) != 0)
+               r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3)
+                         + R_PPC64_GOT_TPREL16_DS);
+             else
+               {
+                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+                 rel->r_offset -= 2;
+                 r_type = R_PPC64_NONE;
+               }
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+           }
+         break;
+
+       case R_PPC64_GOT_TLSGD16:
+       case R_PPC64_GOT_TLSGD16_LO:
+         tls_gd = TLS_TPRELGD;
+         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+           goto tls_get_addr_check;
+         break;
+
+       case R_PPC64_GOT_TLSLD16:
+       case R_PPC64_GOT_TLSLD16_LO:
+         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+           {
+           tls_get_addr_check:
+             if (rel + 1 < relend)
+               {
+                 enum elf_ppc64_reloc_type r_type2;
+                 unsigned long r_symndx2;
+                 struct elf_link_hash_entry *h2;
+                 bfd_vma insn1, insn2, insn3;
+                 bfd_vma offset;
+
+                 /* The next instruction should be a call to
+                    __tls_get_addr.  Peek at the reloc to be sure.  */
+                 r_type2
+                   = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rel[1].r_info);
+                 r_symndx2 = ELF64_R_SYM (rel[1].r_info);
+                 if (r_symndx2 < symtab_hdr->sh_info
+                     || (r_type2 != R_PPC64_REL14
+                         && r_type2 != R_PPC64_REL14_BRTAKEN
+                         && r_type2 != R_PPC64_REL14_BRNTAKEN
+                         && r_type2 != R_PPC64_REL24))
+                   break;
+
+                 h2 = sym_hashes[r_symndx2 - symtab_hdr->sh_info];
+                 while (h2->root.type == bfd_link_hash_indirect
+                        || h2->root.type == bfd_link_hash_warning)
+                   h2 = (struct elf_link_hash_entry *) h2->root.u.i.link;
+                 if (h2 == NULL || h2 != htab->tls_get_addr)
+                   break;
+
+                 /* OK, it checks out.  Replace the call.  */
+                 offset = rel[1].r_offset;
+                 insn1 = bfd_get_32 (output_bfd,
+                                     contents + rel->r_offset - 2);
+                 insn3 = bfd_get_32 (output_bfd,
+                                     contents + offset + 4);
+                 if ((tls_mask & tls_gd) != 0)
+                   {
+                     /* IE */
+                     insn1 &= (1 << 26) - (1 << 2);
+                     insn1 |= 58 << 26;        /* ld */
+                     insn2 = 0x7c636a14;       /* add 3,3,13 */
+                     rel[1].r_info = ELF64_R_INFO (r_symndx2, 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
+                       r_type += R_PPC64_TOC16_DS - R_PPC64_TOC16;
+                     rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+                   }
+                 else
+                   {
+                     /* LE */
+                     insn1 = 0x3c6d0000;       /* addis 3,13,0 */
+                     insn2 = 0x38630000;       /* addi 3,3,0 */
+                     if (tls_gd == 0)
+                       {
+                         /* Was an LD reloc.  */
+                         r_symndx = 0;
+                         rel->r_addend = htab->tls_sec->vma + DTP_OFFSET;
+                         rel[1].r_addend = htab->tls_sec->vma + DTP_OFFSET;
+                       }
+                     r_type = R_PPC64_TPREL16_HA;
+                     rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+                     rel[1].r_info = ELF64_R_INFO (r_symndx,
+                                                   R_PPC64_TPREL16_LO);
+                     rel[1].r_offset += 2;
+                   }
+                 if (insn3 == NOP
+                     || insn3 == CROR_151515 || insn3 == CROR_313131)
+                   {
+                     insn3 = insn2;
+                     insn2 = NOP;
+                     rel[1].r_offset += 4;
+                   }
+                 bfd_put_32 (output_bfd, insn1, contents + rel->r_offset - 2);
+                 bfd_put_32 (output_bfd, insn2, contents + offset);
+                 bfd_put_32 (output_bfd, insn3, contents + offset + 4);
+                 if (tls_gd == 0)
+                   {
+                     /* We changed the symbol on an LD reloc.  Start over
+                        in order to get h, sym, sec etc. right.  */ 
+                     rel--;
+                     continue;
+                   }
+               }
+           }
+         break;
+
+       case R_PPC64_DTPMOD64:
+         if (rel + 1 < relend
+             && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64)
+             && rel[1].r_offset == rel->r_offset + 8)
+           {
+             if ((tls_mask & TLS_GD) == 0)
+               {
+                 rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE);
+                 if ((tls_mask & TLS_TPRELGD) != 0)
+                   r_type = R_PPC64_TPREL64;
+                 else
+                   {
+                     bfd_put_64 (output_bfd, (bfd_vma) 1,
+                                 contents + rel->r_offset);
+                     r_type = R_PPC64_NONE;
+                   }
+                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+           }
+         else
+           {
+             if ((tls_mask & TLS_LD) == 0)
+               {
+                 bfd_put_64 (output_bfd, (bfd_vma) 1,
+                             contents + rel->r_offset);
+                 r_type = R_PPC64_NONE;
+                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+               }
+           }
+         break;
+
+       case R_PPC64_TPREL64:
+         if ((tls_mask & TLS_TPREL) == 0)
+           {
+             r_type = R_PPC64_NONE;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+           }
+         break;
+       }
+
+      /* Handle other relocations that tweak non-addend part of insn.  */
       insn = 0;
       switch (r_type)
        {
@@ -5426,7 +7424,8 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          /* Branch not taken prediction relocations.  */
        case R_PPC64_ADDR14_BRNTAKEN:
        case R_PPC64_REL14_BRNTAKEN:
-         insn |= bfd_get_32 (output_bfd, contents + offset) & ~(0x01 << 21);
+         insn |= bfd_get_32 (output_bfd,
+                             contents + rel->r_offset) & ~(0x01 << 21);
          if (is_power4)
            {
              /* Set 'a' bit.  This is 0b00010 in BO field for branch
@@ -5441,16 +7440,16 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            }
          else
            {
-             from = (offset
+             from = (rel->r_offset
                      + input_section->output_offset
                      + input_section->output_section->vma);
 
              /* Invert 'y' bit if not the default.  */
-             if ((bfd_signed_vma) (relocation + addend - from) < 0)
+             if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
                insn ^= 0x01 << 21;
            }
 
-         bfd_put_32 (output_bfd, (bfd_vma) insn, contents + offset);
+         bfd_put_32 (output_bfd, (bfd_vma) insn, contents + rel->r_offset);
          break;
 
        case R_PPC64_REL24:
@@ -5461,20 +7460,20 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
             need for a PLT entry.  */
          if (h != NULL
              && (fdh = ((struct ppc_link_hash_entry *) h)->oh) != NULL
-             && fdh->plt.offset != (bfd_vma) -1
+             && fdh->plt.plist != NULL
              && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh,
                                                   rel, htab)) != NULL)
            {
              bfd_boolean can_plt_call = 0;
 
-             if (offset + 8 <= input_section->_cooked_size)
+             if (rel->r_offset + 8 <= input_section->_cooked_size)
                {
-                 insn = bfd_get_32 (input_bfd, contents + offset + 4);
+                 insn = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
                  if (insn == NOP
                      || insn == CROR_151515 || insn == CROR_313131)
                    {
                      bfd_put_32 (input_bfd, (bfd_vma) LD_R2_40R1,
-                                 contents + offset + 4);
+                                 contents + rel->r_offset + 4);
                      can_plt_call = 1;
                    }
                }
@@ -5483,7 +7482,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                {
                  /* If this is a plain branch rather than a branch
                     and link, don't require a nop.  */
-                 insn = bfd_get_32 (input_bfd, contents + offset);
+                 insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
                  if ((insn & 1) == 0)
                    can_plt_call = 1;
                }
@@ -5493,7 +7492,6 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                  relocation = (stub_entry->stub_offset
                                + stub_entry->stub_sec->output_offset
                                + stub_entry->stub_sec->output_section->vma);
-                 addend = 0;
                  unresolved_reloc = FALSE;
                }
            }
@@ -5501,7 +7499,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          if (h != NULL
              && h->root.type == bfd_link_hash_undefweak
              && relocation == 0
-             && addend == 0)
+             && rel->r_addend == 0)
            {
              /* Tweak calls to undefined weak functions to point at a
                 blr.  We can thus call a weak function without first
@@ -5511,7 +7509,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
              relocation = (htab->sfpr->_raw_size - 4
                            + htab->sfpr->output_offset
                            + htab->sfpr->output_section->vma);
-             from = (offset
+             from = (rel->r_offset
                      + input_section->output_offset
                      + input_section->output_section->vma);
 
@@ -5525,6 +7523,8 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        }
 
       /* Set `addend'.  */
+      tls_type = 0;
+      addend = rel->r_addend;
       switch (r_type)
        {
        default:
@@ -5537,103 +7537,187 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          continue;
 
        case R_PPC64_NONE:
-       case R_PPC_GNU_VTINHERIT:
-       case R_PPC_GNU_VTENTRY:
+       case R_PPC64_TLS:
+       case R_PPC64_GNU_VTINHERIT:
+       case R_PPC64_GNU_VTENTRY:
          continue;
 
          /* GOT16 relocations.  Like an ADDR16 using the symbol's
             address in the GOT as relocation value instead of the
-            symbols value itself.  Also, create a GOT entry for the
+            symbol's value itself.  Also, create a GOT entry for the
             symbol and put the symbol value there.  */
+       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_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_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_LO:
        case R_PPC64_GOT16_HI:
        case R_PPC64_GOT16_HA:
        case R_PPC64_GOT16_DS:
        case R_PPC64_GOT16_LO_DS:
+       dogot:
          {
            /* Relocation is to the entry for this symbol in the global
               offset table.  */
+           bfd_vma *offp;
            bfd_vma off;
+           unsigned long indx = 0;
 
            if (htab->sgot == NULL)
              abort ();
 
-           if (h != NULL)
+           if (tls_type == (TLS_TLS | TLS_LD)
+               && (h == NULL
+                   || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+             offp = &htab->tlsld_got.offset;
+           else
              {
-               bfd_boolean dyn;
-
-               off = h->got.offset;
-               dyn = htab->elf.dynamic_sections_created;
-               if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)
-                   || (info->shared
-                       && (info->symbolic
-                           || h->dynindx == -1
-                           || (h->elf_link_hash_flags
-                               & ELF_LINK_FORCED_LOCAL))
-                       && (h->elf_link_hash_flags
-                           & ELF_LINK_HASH_DEF_REGULAR)))
+               struct got_entry *ent;
+
+               if (h != NULL)
                  {
-                   /* 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
-                      because of a version file.  We must initialize
-                      this entry in the global offset table.  Since the
-                      offset must always be a multiple of 8, we use the
-                      least significant bit to record whether we have
-                      initialized it already.
-
-                      When doing a dynamic link, we create a .rel.got
-                      relocation entry to initialize the value.  This
-                      is done in the finish_dynamic_symbol routine.  */
-                   if ((off & 1) != 0)
-                     off &= ~1;
+                   bfd_boolean dyn = htab->elf.dynamic_sections_created;
+                   if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+                       || (info->shared
+                           && (info->symbolic
+                               || h->dynindx == -1
+                               || (h->elf_link_hash_flags
+                                   & ELF_LINK_FORCED_LOCAL))
+                           && (h->elf_link_hash_flags
+                               & ELF_LINK_HASH_DEF_REGULAR)))
+                     /* 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
+                        because of a version file.  */
+                     ;
                    else
                      {
-                       bfd_put_64 (output_bfd, relocation,
-                                   htab->sgot->contents + off);
-                       h->got.offset |= 1;
+                       indx = h->dynindx;
+                       unresolved_reloc = FALSE;
                      }
+                   ent = h->got.glist;
                  }
                else
-                 unresolved_reloc = FALSE;
+                 {
+                   if (local_got_ents == NULL)
+                     abort ();
+                   ent = local_got_ents[r_symndx];
+                 }
+
+               for (; ent != NULL; ent = ent->next)
+                 if (ent->addend == rel->r_addend
+                     && ent->tls_type == tls_type)
+                   break;
+               if (ent == NULL)
+                 abort ();
+               offp = &ent->got.offset;
              }
+
+           /* The offset must always be a multiple of 8.  We use the
+              least significant bit to record whether we have already
+              processed this entry.  */
+           off = *offp;
+           if ((off & 1) != 0)
+             off &= ~1;
            else
              {
-               if (local_got_offsets == NULL)
-                 abort ();
-
-               off = local_got_offsets[r_symndx];
+               /* Generate relocs for the dynamic linker, except in
+                  the case of TLSLD where we'll use one entry per
+                  module.  */
+               *offp = off | 1;
+               if ((info->shared || indx != 0)
+                   && (h == NULL
+                       || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                       || h->root.type != bfd_link_hash_undefweak))
+                 {
+                   outrel.r_offset = (htab->sgot->output_section->vma
+                                      + htab->sgot->output_offset
+                                      + off);
+                   if (tls_type & (TLS_LD | TLS_GD))
+                     {
+                       outrel.r_addend = 0;
+                       outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPMOD64);
+                       if (tls_type == (TLS_TLS | TLS_GD))
+                         {
+                           loc = htab->srelgot->contents;
+                           loc += (htab->srelgot->reloc_count++
+                                   * sizeof (Elf64_External_Rela));
+                           bfd_elf64_swap_reloca_out (output_bfd,
+                                                      &outrel, loc);
+                           outrel.r_offset += 8;
+                           outrel.r_info
+                             = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
+                         }
+                     }
+                   else if (tls_type == (TLS_TLS | TLS_DTPREL))
+                     outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
+                   else if (tls_type == (TLS_TLS | TLS_TPREL))
+                     outrel.r_info = ELF64_R_INFO (indx, R_PPC64_TPREL64);
+                   else if (indx == 0)
+                     outrel.r_info = ELF64_R_INFO (indx, R_PPC64_RELATIVE);
+                   else
+                     outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT);
+                   outrel.r_addend = rel->r_addend;
+                   if (indx == 0)
+                     {
+                       outrel.r_addend += relocation;
+                       if (tls_type & (TLS_GD | TLS_DTPREL | TLS_TPREL))
+                         outrel.r_addend -= htab->tls_sec->vma;
+                     }
+                   loc = htab->srelgot->contents;
+                   loc += (htab->srelgot->reloc_count++
+                           * sizeof (Elf64_External_Rela));
+                   bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+                 }
 
-               /* The offset must always be a multiple of 8.  We use
-                  the least significant bit to record whether we have
-                  already processed this entry.  */
-               if ((off & 1) != 0)
-                 off &= ~1;
+               /* Init the .got section contents if we're not
+                  emitting a reloc.  */
                else
                  {
-                   bfd_put_64 (output_bfd, relocation,
-                               htab->sgot->contents + off);
-
-                   if (info->shared)
+                   relocation += rel->r_addend;
+                   if (tls_type == (TLS_TLS | TLS_LD))
+                     relocation = 1;
+                   else if (tls_type != 0)
                      {
-                       Elf_Internal_Rela outrel;
-                       bfd_byte *loc;
-
-                       /* We need to generate a R_PPC64_RELATIVE reloc
-                          for the dynamic linker.  */
-                       outrel.r_offset = (htab->sgot->output_section->vma
-                                          + htab->sgot->output_offset
-                                          + off);
-                       outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-                       outrel.r_addend = relocation;
-                       loc = htab->srelgot->contents;
-                       loc += (htab->srelgot->reloc_count++
-                               * sizeof (Elf64_External_Rela));
-                       bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+                       relocation -= htab->tls_sec->vma + DTP_OFFSET;
+                       if (tls_type == (TLS_TLS | TLS_TPREL))
+                         relocation += DTP_OFFSET - TP_OFFSET;
+
+                       if (tls_type == (TLS_TLS | TLS_GD))
+                         {
+                           bfd_put_64 (output_bfd, relocation,
+                                       htab->sgot->contents + off + 8);
+                           relocation = 1;
+                         }
                      }
 
-                   local_got_offsets[r_symndx] |= 1;
+                   bfd_put_64 (output_bfd, relocation,
+                               htab->sgot->contents + off);
                  }
              }
 
@@ -5643,7 +7727,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            relocation = htab->sgot->output_offset + off;
 
            /* TOC base (r2) is TOC start plus 0x8000.  */
-           addend -= TOC_BASE_OFF;
+           addend = - TOC_BASE_OFF;
          }
          break;
 
@@ -5660,19 +7744,23 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          if (h == NULL)
            break;
 
-         if (h->plt.offset == (bfd_vma) -1
-             || htab->splt == NULL)
+         /* 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->splt != NULL)
            {
-             /* We didn't make a PLT entry for this symbol.  This
-                happens when statically linking PIC code, or when
-                using -Bsymbolic.  */
-             break;
+             struct plt_entry *ent;
+             for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+               if (ent->addend == rel->r_addend
+                   && ent->plt.offset != (bfd_vma) -1)
+                 {
+                   relocation = (htab->splt->output_section->vma
+                                 + htab->splt->output_offset
+                                 + ent->plt.offset);
+                   unresolved_reloc = FALSE;
+                 }
            }
-
-         relocation = (htab->splt->output_section->vma
-                       + htab->splt->output_offset
-                       + h->plt.offset);
-         unresolved_reloc = FALSE;
          break;
 
          /* TOC16 relocs.  We want the offset relative to the TOC base,
@@ -5705,8 +7793,53 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_PPC64_REL24:
          break;
 
+       case R_PPC64_TPREL16:
+       case R_PPC64_TPREL16_LO:
+       case R_PPC64_TPREL16_HI:
+       case R_PPC64_TPREL16_HA:
+       case R_PPC64_TPREL16_DS:
+       case R_PPC64_TPREL16_LO_DS:
+       case R_PPC64_TPREL16_HIGHER:
+       case R_PPC64_TPREL16_HIGHERA:
+       case R_PPC64_TPREL16_HIGHEST:
+       case R_PPC64_TPREL16_HIGHESTA:
+         addend -= htab->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;
+
+       case R_PPC64_DTPREL16:
+       case R_PPC64_DTPREL16_LO:
+       case R_PPC64_DTPREL16_HI:
+       case R_PPC64_DTPREL16_HA:
+       case R_PPC64_DTPREL16_DS:
+       case R_PPC64_DTPREL16_LO_DS:
+       case R_PPC64_DTPREL16_HIGHER:
+       case R_PPC64_DTPREL16_HIGHERA:
+       case R_PPC64_DTPREL16_HIGHEST:
+       case R_PPC64_DTPREL16_HIGHESTA:
+         addend -= htab->tls_sec->vma + DTP_OFFSET;
+         break;
+
+       case R_PPC64_DTPMOD64:
+         relocation = 1;
+         addend = 0;
+         goto dodyn;
+
+       case R_PPC64_TPREL64:
+         addend -= htab->tls_sec->vma + TP_OFFSET;
+         goto dodyn;
+
+       case R_PPC64_DTPREL64:
+         addend -= htab->tls_sec->vma + DTP_OFFSET;
+         /* Fall thru */
+
          /* Relocations that may need to be propagated if this is a
             dynamic object.  */
+       case R_PPC64_REL30:
        case R_PPC64_REL32:
        case R_PPC64_REL64:
        case R_PPC64_ADDR14:
@@ -5723,7 +7856,6 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
        case R_PPC64_ADDR24:
-       case R_PPC64_ADDR30:
        case R_PPC64_ADDR32:
        case R_PPC64_ADDR64:
        case R_PPC64_UADDR16:
@@ -5732,6 +7864,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          /* r_symndx will be zero only for relocs against symbols
             from removed linkonce sections, or sections discarded by
             a linker script.  */
+       dodyn:
          if (r_symndx == 0)
            break;
          /* Fall thru.  */
@@ -5744,22 +7877,22 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            break;
 
          if ((info->shared
-              && (IS_ABSOLUTE_RELOC (r_type)
+              && (h == NULL
+                  || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                  || h->root.type != bfd_link_hash_undefweak)
+              && (MUST_BE_DYN_RELOC (r_type)
                   || (h != NULL
                       && h->dynindx != -1
                       && (! info->symbolic
                           || (h->elf_link_hash_flags
                               & ELF_LINK_HASH_DEF_REGULAR) == 0))))
-             || (!info->shared
+             || (ELIMINATE_COPY_RELOCS
+                 && !info->shared
                  && h != NULL
                  && h->dynindx != -1
                  && (h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
-                 && (((h->elf_link_hash_flags
-                       & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-                      && (h->elf_link_hash_flags
-                          & ELF_LINK_HASH_DEF_REGULAR) == 0)
-                     || h->root.type == bfd_link_hash_undefweak
-                     || h->root.type == bfd_link_hash_undefined)))
+                 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+                 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0))
            {
              Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
@@ -5782,14 +7915,14 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                skip = TRUE, relocate = TRUE;
              outrel.r_offset += (input_section->output_section->vma
                                  + input_section->output_offset);
-             outrel.r_addend = addend;
+             outrel.r_addend = rel->r_addend;
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
              else if (h != NULL
                       && h->dynindx != -1
                       && !is_opd
-                      && (!IS_ABSOLUTE_RELOC (r_type)
+                      && (!MUST_BE_DYN_RELOC (r_type)
                           || !info->shared
                           || !info->symbolic
                           || (h->elf_link_hash_flags
@@ -5801,6 +7934,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                     or this is an opd section reloc which must point
                     at a local function.  */
                  outrel.r_addend += relocation;
+                 /* ??? why? */
                  relocate = TRUE;
                  if (r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
                    {
@@ -5872,7 +8006,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_PPC64_RELATIVE:
          /* We shouldn't ever see these dynamic relocs in relocatable
             files.  */
-         /* Fall thru */
+         /* Fall through.  */
 
        case R_PPC64_PLTGOT16:
        case R_PPC64_PLTGOT16_DS:
@@ -5885,7 +8019,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          /* These ones haven't been implemented yet.  */
 
          (*_bfd_error_handler)
-           (_("%s: Relocation %s is not supported for symbol %s."),
+           (_("%s: relocation %s is not supported for symbol %s."),
             bfd_archive_filename (input_bfd),
             ppc64_elf_howto_table[(int) r_type]->name, sym_name);
 
@@ -5908,13 +8042,28 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_PPC64_PLT16_HA:
        case R_PPC64_TOC16_HA:
        case R_PPC64_SECTOFF_HA:
+       case R_PPC64_TPREL16_HA:
+       case R_PPC64_DTPREL16_HA:
+       case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_TPREL16_HIGHER:
+       case R_PPC64_TPREL16_HIGHERA:
+       case R_PPC64_TPREL16_HIGHEST:
+       case R_PPC64_TPREL16_HIGHESTA:
+       case R_PPC64_DTPREL16_HIGHER:
+       case R_PPC64_DTPREL16_HIGHERA:
+       case R_PPC64_DTPREL16_HIGHEST:
+       case R_PPC64_DTPREL16_HIGHESTA:
          /* It's just possible that this symbol is a weak symbol
             that's not actually defined anywhere. In that case,
             'sec' would be NULL, and we should leave the symbol
             alone (it will be set to zero elsewhere in the link).  */
          if (sec != NULL)
-           /* Add 0x10000 if sign bit in 0:15 is set.  */
-           addend += ((relocation + addend) & 0x8000) << 1;
+           /* Add 0x10000 if sign bit in 0:15 is set.
+              Bits 0:15 are not used.  */
+           addend += 0x8000;
          break;
 
        case R_PPC64_ADDR16_DS:
@@ -5928,6 +8077,14 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_PPC64_TOC16_LO_DS:
        case R_PPC64_PLTGOT16_DS:
        case R_PPC64_PLTGOT16_LO_DS:
+       case R_PPC64_GOT_TPREL16_DS:
+       case R_PPC64_GOT_TPREL16_LO_DS:
+       case R_PPC64_GOT_DTPREL16_DS:
+       case R_PPC64_GOT_DTPREL16_LO_DS:
+       case R_PPC64_TPREL16_DS:
+       case R_PPC64_TPREL16_LO_DS:
+       case R_PPC64_DTPREL16_DS:
+       case R_PPC64_DTPREL16_LO_DS:
          if (((relocation + addend) & 3) != 0)
            {
              (*_bfd_error_handler)
@@ -5952,7 +8109,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        branch_check:
          /* If the branch is out of reach, then redirect the
             call to the local stub for this function.  */
-         from = (offset
+         from = (rel->r_offset
                  + input_section->output_offset
                  + input_section->output_section->vma);
          if (relocation + addend - from + max_br_offset >= 2 * max_br_offset
@@ -5977,10 +8134,11 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
               && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0))
        {
          (*_bfd_error_handler)
-           (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"),
+           (_("%s(%s+0x%lx): unresolvable %s relocation against symbol `%s'"),
             bfd_archive_filename (input_bfd),
             bfd_get_section_name (input_bfd, input_section),
             (long) rel->r_offset,
+            ppc64_elf_howto_table[(int) r_type]->name,
             h->root.root.string);
          ret = FALSE;
        }
@@ -5989,17 +8147,20 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                                    input_bfd,
                                    input_section,
                                    contents,
-                                   offset,
+                                   rel->r_offset,
                                    relocation,
                                    addend);
 
       if (r != bfd_reloc_ok)
        {
-         const char *name;
-
-         if (h != NULL)
+         if (sym_name == NULL)
+           sym_name = "(null)";
+         if (r == bfd_reloc_overflow)
            {
-             if (h->root.type == bfd_link_hash_undefweak
+             if (warned)
+               continue;
+             if (h != NULL
+                 && h->root.type == bfd_link_hash_undefweak
                  && ppc64_elf_howto_table[(int) r_type]->pc_relative)
                {
                  /* Assume this is a call protected by other code that
@@ -6011,35 +8172,21 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                  continue;
                }
 
-             name = h->root.root.string;
-           }
-         else
-           {
-             name = bfd_elf_string_from_elf_section (input_bfd,
-                                                     symtab_hdr->sh_link,
-                                                     sym->st_name);
-             if (name == NULL)
-               continue;
-             if (*name == '\0')
-               name = bfd_section_name (input_bfd, sec);
-           }
-
-         if (r == bfd_reloc_overflow)
-           {
-             if (warned)
-               continue;
              if (!((*info->callbacks->reloc_overflow)
-                   (info, name, ppc64_elf_howto_table[(int) r_type]->name,
-                    rel->r_addend, input_bfd, input_section, offset)))
+                   (info, sym_name, ppc64_elf_howto_table[(int) r_type]->name,
+                    rel->r_addend, input_bfd, input_section, rel->r_offset)))
                return FALSE;
            }
          else
            {
              (*_bfd_error_handler)
-               (_("%s(%s+0x%lx): reloc against `%s': error %d"),
+               (_("%s(%s+0x%lx): %s reloc against `%s': error %d"),
                 bfd_archive_filename (input_bfd),
                 bfd_get_section_name (input_bfd, input_section),
-                (long) rel->r_offset, name, (int) r);
+                (long) rel->r_offset,
+                ppc64_elf_howto_table[(int) r_type]->name,
+                sym_name,
+                (int) r);
              ret = FALSE;
            }
        }
@@ -6064,79 +8211,36 @@ ppc64_elf_finish_dynamic_symbol (output_bfd, info, h, sym)
   htab = ppc_hash_table (info);
   dynobj = htab->elf.dynobj;
 
-  if (h->plt.offset != (bfd_vma) -1
-      && ((struct ppc_link_hash_entry *) h)->is_func_descriptor)
-    {
-      Elf_Internal_Rela rela;
-      bfd_byte *loc;
-
-      /* This symbol has an entry in the procedure linkage table.  Set
-        it up.  */
-
-      if (htab->splt == NULL
-         || htab->srelplt == NULL
-         || htab->sglink == NULL)
-       abort ();
-
-      /* Create a JMP_SLOT reloc to inform the dynamic linker to
-        fill in the PLT entry.  */
-
-      rela.r_offset = (htab->splt->output_section->vma
-                      + htab->splt->output_offset
-                      + h->plt.offset);
-      rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
-      rela.r_addend = 0;
-
-      loc = htab->srelplt->contents;
-      loc += ((h->plt.offset - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE
-             * sizeof (Elf64_External_Rela));
-      bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
-    }
-
-  if (h->got.offset != (bfd_vma) -1)
+  if (((struct ppc_link_hash_entry *) h)->is_func_descriptor)
     {
+      struct plt_entry *ent;
       Elf_Internal_Rela rela;
       bfd_byte *loc;
 
-      /* This symbol has an entry in the global offset table.  Set it
-        up.  */
-
-      if (htab->sgot == NULL || htab->srelgot == NULL)
-       abort ();
+      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.  */
 
-      rela.r_offset = (htab->sgot->output_section->vma
-                      + htab->sgot->output_offset
-                      + (h->got.offset &~ (bfd_vma) 1));
-
-      /* If this is a static link, or it is a -Bsymbolic link and the
-        symbol is defined locally or was forced to be local because
-        of a version file, we just want to emit a RELATIVE reloc.
-        The entry in the global offset table will already have been
-        initialized in the relocate_section function.  */
-      if (info->shared
-         && (info->symbolic
-             || h->dynindx == -1
-             || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL))
-         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
-       {
-         BFD_ASSERT((h->got.offset & 1) != 0);
-         rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-         rela.r_addend = (h->root.u.def.value
-                          + h->root.u.def.section->output_section->vma
-                          + h->root.u.def.section->output_offset);
-       }
-      else
-       {
-         BFD_ASSERT ((h->got.offset & 1) == 0);
-         bfd_put_64 (output_bfd, (bfd_vma) 0,
-                     htab->sgot->contents + h->got.offset);
-         rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_GLOB_DAT);
-         rela.r_addend = 0;
-       }
+           if (htab->splt == NULL
+               || htab->srelplt == NULL
+               || htab->sglink == NULL)
+             abort ();
 
-      loc = htab->srelgot->contents;
-      loc += htab->srelgot->reloc_count++ * sizeof (Elf64_External_Rela);
-      bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+           /* Create a JMP_SLOT reloc to inform the dynamic linker to
+              fill in the PLT entry.  */
+           rela.r_offset = (htab->splt->output_section->vma
+                            + htab->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->srelplt->contents;
+           loc += ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE
+                   * sizeof (Elf64_External_Rela));
+           bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+         }
     }
 
   if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
@@ -6176,9 +8280,9 @@ static enum elf_reloc_type_class
 ppc64_elf_reloc_type_class (rela)
      const Elf_Internal_Rela *rela;
 {
-  enum elf_ppc_reloc_type r_type;
+  enum elf_ppc64_reloc_type r_type;
 
-  r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rela->r_info);
+  r_type = (enum elf_ppc64_reloc_type) ELF64_R_TYPE (rela->r_info);
   switch (r_type)
     {
     case R_PPC64_RELATIVE:
@@ -6339,6 +8443,7 @@ ppc64_elf_finish_dynamic_sections (output_bfd, info)
 
 #define bfd_elf64_bfd_reloc_type_lookup              ppc64_elf_reloc_type_lookup
 #define bfd_elf64_bfd_merge_private_bfd_data  ppc64_elf_merge_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
 
This page took 0.082088 seconds and 4 git commands to generate.