2010-09-24 Thomas Schwinge <thomas@codesourcery.com>
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 37701f252c6825f27c3ec97c55b9e286d9ddbc7e..a4f3064a4f7702210a929e632f2082e3e63a7bed 100644 (file)
@@ -61,6 +61,7 @@ static bfd_vma opd_entry_value
 #define TARGET_BIG_SYM         bfd_elf64_powerpc_vec
 #define TARGET_BIG_NAME                "elf64-powerpc"
 #define ELF_ARCH               bfd_arch_powerpc
+#define ELF_TARGET_ID          PPC64_ELF_DATA
 #define ELF_MACHINE_CODE       EM_PPC64
 #define ELF_MAXPAGESIZE                0x10000
 #define ELF_COMMONPAGESIZE     0x1000
@@ -2546,10 +2547,10 @@ struct got_entry
 
   /* 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;
+  unsigned char tls_type;
 
   /* Non-zero if got.ent points to real entry.  */
-  char is_indirect;
+  unsigned char is_indirect;
 
   /* Reference count until size_dynamic_sections, GOT offset thereafter.  */
   union
@@ -2592,6 +2593,10 @@ struct ppc64_elf_obj_tdata
 
   /* A copy of relocs before they are modified for --emit-relocs.  */
   Elf_Internal_Rela *opd_relocs;
+
+  /* Nonzero if this bfd has small toc/got relocs, ie. that expect
+     the reloc to be in the range -32768 to 32767.  */
+  unsigned int has_small_toc_reloc;
 };
 
 #define ppc64_elf_tdata(bfd) \
@@ -2647,7 +2652,7 @@ ppc64_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
   elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
 
   /* pr_pid */
-  elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 32);
+  elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 32);
 
   /* pr_reg */
   offset = 112;
@@ -3698,7 +3703,7 @@ struct ppc_link_hash_entry
 #define TLS_EXPLICIT   32      /* Marks TOC section TLS relocs. */
 #define TLS_TPRELGD    64      /* TPREL reloc resulting from GD->IE. */
 #define PLT_IFUNC      128     /* STT_GNU_IFUNC.  */
-  char tls_mask;
+  unsigned char tls_mask;
 };
 
 /* ppc64 ELF linker hash table.  */
@@ -3781,8 +3786,10 @@ struct ppc_link_hash_table
   unsigned int no_tls_get_addr_opt:1;
 
   /* Support for multiple toc sections.  */
+  unsigned int do_multi_toc:1;
   unsigned int multi_toc_needed:1;
   unsigned int second_toc_pass:1;
+  unsigned int do_toc_opt:1;
 
   /* Set on error.  */
   unsigned int stub_error:1;
@@ -3811,10 +3818,11 @@ struct ppc_link_hash_table
 
 /* Nonzero if this section has a call to another section that uses
    the toc or got.  */
-#define makes_toc_func_call sec_flg4
+#define makes_toc_func_call sec_flg3
 
 /* Recursion protection when determining above flag.  */
-#define call_check_in_progress sec_flg5
+#define call_check_in_progress sec_flg4
+#define call_check_done sec_flg5
 
 /* Get the ppc64 ELF linker hash table from a link_info structure.  */
 
@@ -4552,7 +4560,7 @@ make_fdh (struct bfd_link_info *info,
    function type.  */
 
 static bfd_boolean
-ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED,
+ppc64_elf_add_symbol_hook (bfd *ibfd,
                           struct bfd_link_info *info,
                           Elf_Internal_Sym *isym,
                           const char **name ATTRIBUTE_UNUSED,
@@ -4561,11 +4569,14 @@ ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED,
                           bfd_vma *value ATTRIBUTE_UNUSED)
 {
   if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
-    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+    {
+      if ((ibfd->flags & DYNAMIC) == 0)
+       elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+    }
   else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
     ;
   else if (*sec != NULL
-          && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0)
+          && strcmp ((*sec)->name, ".opd") == 0)
     isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
 
   return TRUE;
@@ -4740,7 +4751,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 {
   struct got_entry **local_got_ents = elf_local_got_ents (abfd);
   struct plt_entry **local_plt;
-  char *local_got_tls_masks;
+  unsigned char *local_got_tls_masks;
 
   if (local_got_ents == NULL)
     {
@@ -4782,7 +4793,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
     }
 
   local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info);
-  local_got_tls_masks = (char *) (local_plt + symtab_hdr->sh_info);
+  local_got_tls_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info);
   local_got_tls_masks[r_symndx] |= tls_type;
 
   return local_plt + r_symndx;
@@ -4834,7 +4845,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 {
   struct ppc_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
+  struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
@@ -4864,15 +4875,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   dottga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
                                 FALSE, FALSE, TRUE);
   symtab_hdr = &elf_symtab_hdr (abfd);
-
   sym_hashes = elf_sym_hashes (abfd);
-  sym_hashes_end = (sym_hashes
-                   + symtab_hdr->sh_size / sizeof (Elf64_External_Sym)
-                   - symtab_hdr->sh_info);
-
   sreloc = NULL;
   opd_sym_map = NULL;
-  if (strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0)
+  if (strcmp (sec->name, ".opd") == 0)
     {
       /* Garbage collection needs some extra help with .opd sections.
         We don't want to necessarily keep everything referenced by
@@ -5010,6 +5016,17 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_GOT16_LO_DS:
          /* This symbol requires a global offset table entry.  */
          sec->has_toc_reloc = 1;
+         if (r_type == R_PPC64_GOT_TLSLD16
+             || r_type == R_PPC64_GOT_TLSGD16
+             || r_type == R_PPC64_GOT_TPREL16_DS
+             || r_type == R_PPC64_GOT_DTPREL16_DS
+             || r_type == R_PPC64_GOT16
+             || r_type == R_PPC64_GOT16_DS)
+           {
+             htab->do_multi_toc = 1;
+             ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1;
+           }
+
          if (ppc64_elf_tdata (abfd)->got == NULL
              && !create_got_section (abfd, info))
            return FALSE;
@@ -5106,10 +5123,12 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          break;
 
        case R_PPC64_TOC16:
+       case R_PPC64_TOC16_DS:
+         htab->do_multi_toc = 1;
+         ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1;
        case R_PPC64_TOC16_LO:
        case R_PPC64_TOC16_HI:
        case R_PPC64_TOC16_HA:
-       case R_PPC64_TOC16_DS:
        case R_PPC64_TOC16_LO_DS:
          sec->has_toc_reloc = 1;
          break;
@@ -5543,6 +5562,17 @@ opd_entry_value (asection *opd_sec,
   return val;
 }
 
+/* Return true if symbol is defined in a regular object file.  */
+
+static bfd_boolean
+is_static_defined (struct elf_link_hash_entry *h)
+{
+  return ((h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+         && h->root.u.def.section != NULL
+         && h->root.u.def.section->output_section != NULL);
+}
+
 /* If FDH is a function descriptor symbol, return the associated code
    entry symbol if it is defined.  Return NULL otherwise.  */
 
@@ -5787,7 +5817,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
       unsigned long r_symndx;
       enum elf_ppc64_reloc_type r_type;
       struct elf_link_hash_entry *h = NULL;
-      char tls_type = 0;
+      unsigned char tls_type = 0;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       r_type = ELF64_R_TYPE (rel->r_info);
@@ -5822,7 +5852,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
            {
              struct plt_entry **local_plt = (struct plt_entry **)
                (local_got_ents + symtab_hdr->sh_info);
-             char *local_got_tls_masks = (char *)
+             unsigned char *local_got_tls_masks = (unsigned char *)
                (local_plt + symtab_hdr->sh_info);
              if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
                ifunc = local_plt + r_symndx;
@@ -6549,7 +6579,7 @@ static bfd_boolean
 get_sym_h (struct elf_link_hash_entry **hp,
           Elf_Internal_Sym **symp,
           asection **symsecp,
-          char **tls_maskp,
+          unsigned char **tls_maskp,
           Elf_Internal_Sym **locsymsp,
           unsigned long r_symndx,
           bfd *ibfd)
@@ -6617,7 +6647,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
       if (tls_maskp != NULL)
        {
          struct got_entry **lgot_ents;
-         char *tls_mask;
+         unsigned char *tls_mask;
 
          tls_mask = NULL;
          lgot_ents = elf_local_got_ents (ibfd);
@@ -6625,7 +6655,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
            {
              struct plt_entry **local_plt = (struct plt_entry **)
                (lgot_ents + symtab_hdr->sh_info);
-             char *lgot_masks = (char *)
+             unsigned char *lgot_masks = (unsigned char *)
                (local_plt + symtab_hdr->sh_info);
              tls_mask = &lgot_masks[r_symndx];
            }
@@ -6640,7 +6670,7 @@ get_sym_h (struct elf_link_hash_entry **hp,
    type suitable for optimization, and 1 otherwise.  */
 
 static int
-get_tls_mask (char **tls_maskp,
+get_tls_mask (unsigned char **tls_maskp,
              unsigned long *toc_symndx,
              bfd_vma *toc_addend,
              Elf_Internal_Sym **locsymsp,
@@ -6660,6 +6690,7 @@ get_tls_mask (char **tls_maskp,
 
   if ((*tls_maskp != NULL && **tls_maskp != 0)
       || sec == NULL
+      || ppc64_elf_section_data (sec) == NULL
       || ppc64_elf_section_data (sec)->sec_type != sec_toc)
     return 1;
 
@@ -6681,10 +6712,7 @@ get_tls_mask (char **tls_maskp,
     *toc_addend = ppc64_elf_section_data (sec)->u.toc.add[off / 8];
   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)
-          && !h->def_dynamic))
+  if ((h == NULL || is_static_defined (h))
       && (next_r == -1 || next_r == -2))
     return 1 - next_r;
   return 1;
@@ -6883,8 +6911,7 @@ dec_dynrel_count (bfd_vma r_info,
    applications.  */
 
 bfd_boolean
-ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
-                   bfd_boolean non_overlapping)
+ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
 {
   bfd *ibfd;
   bfd_boolean some_edited = FALSE;
@@ -6896,12 +6923,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
       Elf_Internal_Rela *relstart, *rel, *relend;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      struct elf_link_hash_entry **sym_hashes;
       bfd_vma offset;
       struct _opd_sec_data *opd;
       bfd_boolean need_edit, add_aux_fields;
       bfd_size_type cnt_16b = 0;
 
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
       sec = bfd_get_section_by_name (ibfd, ".opd");
       if (sec == NULL || sec->size == 0)
        continue;
@@ -6918,7 +6947,6 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
 
       local_syms = NULL;
       symtab_hdr = &elf_symtab_hdr (ibfd);
-      sym_hashes = elf_sym_hashes (ibfd);
 
       /* Read the relocations.  */
       relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
@@ -7054,7 +7082,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
          new_contents = NULL;
          amt = sec->size * sizeof (long) / 8;
          opd = &ppc64_elf_section_data (sec)->u.opd;
-         opd->adjust = bfd_zalloc (obfd, amt);
+         opd->adjust = bfd_zalloc (sec->owner, amt);
          if (opd->adjust == NULL)
            return FALSE;
          ppc64_elf_section_data (sec)->sec_type = sec_opd;
@@ -7285,9 +7313,9 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
 /* Set htab->tls_get_addr and call the generic ELF tls_setup function.  */
 
 asection *
-ppc64_elf_tls_setup (bfd *obfd,
-                    struct bfd_link_info *info,
-                    int no_tls_get_addr_opt)
+ppc64_elf_tls_setup (struct bfd_link_info *info,
+                    int no_tls_get_addr_opt,
+                    int *no_multi_toc)
 {
   struct ppc_link_hash_table *htab;
 
@@ -7295,6 +7323,11 @@ ppc64_elf_tls_setup (bfd *obfd,
   if (htab == NULL)
     return NULL;
 
+  if (*no_multi_toc)
+    htab->do_multi_toc = 0;
+  else if (!htab->do_multi_toc)
+    *no_multi_toc = 1;
+
   htab->tls_get_addr = ((struct ppc_link_hash_entry *)
                        elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
                                              FALSE, FALSE, TRUE));
@@ -7348,7 +7381,7 @@ ppc64_elf_tls_setup (bfd *obfd,
                      _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
                                              opt_fd->dynstr_index);
                      if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
-                       return FALSE;
+                       return NULL;
                    }
                  htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd;
                  tga = &htab->tls_get_addr->elf;
@@ -7375,7 +7408,7 @@ ppc64_elf_tls_setup (bfd *obfd,
        no_tls_get_addr_opt = TRUE;
     }
   htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
-  return _bfd_elf_tls_setup (obfd, info);
+  return _bfd_elf_tls_setup (info->output_bfd, info);
 }
 
 /* Return TRUE iff REL is a branch reloc with a global symbol matching
@@ -7412,7 +7445,7 @@ branch_reloc_hash_match (const bfd *ibfd,
    dynamic relocations.  */
 
 bfd_boolean
-ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
+ppc64_elf_tls_optimize (struct bfd_link_info *info)
 {
   bfd *ibfd;
   asection *sec;
@@ -7458,8 +7491,8 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                  struct elf_link_hash_entry *h;
                  Elf_Internal_Sym *sym;
                  asection *sym_sec;
-                 char *tls_mask;
-                 char tls_set, tls_clear, tls_type = 0;
+                 unsigned char *tls_mask;
+                 unsigned char tls_set, tls_clear, tls_type = 0;
                  bfd_vma value;
                  bfd_boolean ok_tprel, is_local;
                  long toc_ref_index = 0;
@@ -7672,7 +7705,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                          if (expecting_tls_get_addr == 2)
                            {
                              /* Check for toc tls entries.  */
-                             char *toc_tls;
+                             unsigned char *toc_tls;
                              int retval;
 
                              retval = get_tls_mask (&toc_tls, NULL, NULL,
@@ -7808,11 +7841,14 @@ struct adjust_toc_info
   bfd_boolean global_toc_syms;
 };
 
+enum toc_skip_enum { ref_from_discarded = 1, can_optimize = 2 };
+
 static bfd_boolean
 adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 {
   struct ppc_link_hash_entry *eh;
   struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf;
+  unsigned long i;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -7830,16 +7866,22 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 
   if (eh->elf.root.u.def.section == toc_inf->toc)
     {
-      unsigned long skip = toc_inf->skip[eh->elf.root.u.def.value >> 3];
-      if (skip != (unsigned long) -1)
-       eh->elf.root.u.def.value -= skip;
+      if (eh->elf.root.u.def.value > toc_inf->toc->rawsize)
+       i = toc_inf->toc->rawsize >> 3;
       else
+       i = eh->elf.root.u.def.value >> 3;
+
+      if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0)
        {
          (*_bfd_error_handler)
-           (_("%s defined in removed toc entry"), eh->elf.root.root.string);
-         eh->elf.root.u.def.section = &bfd_abs_section;
-         eh->elf.root.u.def.value = 0;
+           (_("%s defined on removed toc entry"), eh->elf.root.root.string);
+         do
+           ++i;
+         while ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0);
+         eh->elf.root.u.def.value = (bfd_vma) i << 3;
        }
+
+      eh->elf.root.u.def.value -= toc_inf->skip[i];
       eh->adjust_done = 1;
     }
   else if (strcmp (eh->elf.root.u.def.section->name, ".toc") == 0)
@@ -7852,23 +7894,27 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
    unused .toc entries.  */
 
 bfd_boolean
-ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
+ppc64_elf_edit_toc (struct bfd_link_info *info)
 {
   bfd *ibfd;
   struct adjust_toc_info toc_inf;
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
+  htab->do_toc_opt = 1;
   toc_inf.global_toc_syms = TRUE;
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
       asection *toc, *sec;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      struct elf_link_hash_entry **sym_hashes;
       Elf_Internal_Rela *relstart, *rel;
       unsigned long *skip, *drop;
       unsigned char *used;
       unsigned char *keep, last, some_unused;
 
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
       toc = bfd_get_section_by_name (ibfd, ".toc");
       if (toc == NULL
          || toc->size == 0
@@ -7878,7 +7924,6 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
 
       local_syms = NULL;
       symtab_hdr = &elf_symtab_hdr (ibfd);
-      sym_hashes = elf_sym_hashes (ibfd);
 
       /* Look at sections dropped from the final link.  */
       skip = NULL;
@@ -7946,18 +7991,100 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
 
              if (skip == NULL)
                {
-                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 7) / 8);
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8);
                  if (skip == NULL)
                    goto error_ret;
                }
 
-             skip[val >> 3] = 1;
+             skip[val >> 3] = ref_from_discarded;
            }
 
          if (elf_section_data (sec)->relocs != relstart)
            free (relstart);
        }
 
+      /* For largetoc loads of address constants, we can convert
+        .  addis rx,2,addr@got@ha
+        .  ld ry,addr@got@l(rx)
+        to
+        .  addis rx,2,addr@toc@ha
+        .  addi ry,rx,addr@toc@l
+        when addr is within 2G of the toc pointer.  This then means
+        that the word storing "addr" in the toc is no longer needed.  */
+        
+      if (!ppc64_elf_tdata (ibfd)->has_small_toc_reloc
+         && toc->output_section->rawsize < (bfd_vma) 1 << 31
+         && toc->reloc_count != 0)
+       {
+         /* Read toc relocs.  */
+         relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+                                               info->keep_memory);
+         if (relstart == NULL)
+           goto error_ret;
+
+         for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
+           {
+             enum elf_ppc64_reloc_type r_type;
+             unsigned long r_symndx;
+             asection *sym_sec;
+             struct elf_link_hash_entry *h;
+             Elf_Internal_Sym *sym;
+             bfd_vma val, addr;
+
+             r_type = ELF64_R_TYPE (rel->r_info);
+             if (r_type != R_PPC64_ADDR64)
+               continue;
+
+             r_symndx = ELF64_R_SYM (rel->r_info);
+             if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                             r_symndx, ibfd))
+               goto error_ret;
+
+             if (!SYMBOL_CALLS_LOCAL (info, h))
+               continue;
+
+             if (h != NULL)
+               {
+                 if (h->type == STT_GNU_IFUNC)
+                   continue;
+                 val = h->root.u.def.value;
+               }
+             else
+               {
+                 if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                   continue;
+                 val = sym->st_value;
+               }
+             val += rel->r_addend;
+             val += sym_sec->output_section->vma + sym_sec->output_offset;
+
+             /* We don't yet know the exact toc pointer value, but we
+                know it will be somewhere in the toc section.  Don't
+                optimize if the difference from any possible toc
+                pointer is outside [ff..f80008000, 7fff7fff].  */
+             addr = toc->output_section->vma + TOC_BASE_OFF;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             addr = toc->output_section->vma + toc->output_section->rawsize;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             if (skip == NULL)
+               {
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8);
+                 if (skip == NULL)
+                   goto error_ret;
+               }
+
+             skip[rel->r_offset >> 3]
+               |= can_optimize | ((rel - relstart) << 2);
+           }
+
+         if (elf_section_data (toc)->relocs != relstart)
+           free (relstart);
+       }
+
       if (skip == NULL)
        continue;
 
@@ -7996,7 +8123,8 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
              || (sec->flags & SEC_DEBUGGING) != 0)
            continue;
 
-         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, TRUE);
+         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                               info->keep_memory);
          if (relstart == NULL)
            goto error_ret;
 
@@ -8049,12 +8177,37 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                if (val >= toc->size)
                  continue;
 
+               if ((skip[val >> 3] & can_optimize) != 0)
+                 {
+                   bfd_vma off;
+                   unsigned char opc;
+
+                   switch (r_type)
+                     {
+                     case R_PPC64_TOC16_HA:
+                       break;
+
+                     case R_PPC64_TOC16_LO_DS:
+                       off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
+                       if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
+                         return FALSE;
+                       if ((opc & (0x3f << 2)) == (58u << 2))
+                         break;
+                       /* Fall thru */
+
+                     default:
+                       /* Wrong sort of reloc, or not a ld.  We may
+                          as well clear ref_from_discarded too.  */
+                       skip[val >> 3] = 0;
+                     }
+                 }
+
                /* For the toc section, we only mark as used if
                   this entry itself isn't unused.  */
                if (sec == toc
                    && !used[val >> 3]
                    && (used[rel->r_offset >> 3]
-                       || !skip[rel->r_offset >> 3]))
+                       || !(skip[rel->r_offset >> 3] & ref_from_discarded)))
                  /* Do all the relocs again, to catch reference
                     chains.  */
                  repeat = 1;
@@ -8062,6 +8215,9 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                used[val >> 3] = 1;
              }
          while (repeat);
+
+         if (elf_section_data (sec)->relocs != relstart)
+           free (relstart);
        }
 
       /* Merge the used and skip arrays.  Assume that TOC
@@ -8073,13 +8229,15 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
        {
          if (*keep)
            {
-             *drop = 0;
+             *drop &= ~ref_from_discarded;
+             if ((*drop & can_optimize) != 0)
+               some_unused = 1;
              last = 0;
            }
          else if (*drop)
            {
              some_unused = 1;
-             last = 1;
+             last = ref_from_discarded;
            }
          else
            *drop = last;
@@ -8091,6 +8249,8 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
        {
          bfd_byte *contents, *src;
          unsigned long off;
+         Elf_Internal_Sym *sym;
+         bfd_boolean local_toc_syms = FALSE;
 
          /* Shuffle the toc contents, and at the same time convert the
             skip array from booleans into offsets.  */
@@ -8103,52 +8263,20 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
               src < contents + toc->size;
               src += 8, ++drop)
            {
-             if (*drop)
-               {
-                 *drop = (unsigned long) -1;
-                 off += 8;
-               }
+             if ((*drop & (can_optimize | ref_from_discarded)) != 0)
+               off += 8;
              else if (off != 0)
                {
                  *drop = off;
                  memcpy (src - off, src, 8);
                }
            }
+         *drop = off;
          toc->rawsize = toc->size;
          toc->size = src - contents - off;
 
-         if (toc->reloc_count != 0)
-           {
-             Elf_Internal_Rela *wrel;
-             bfd_size_type sz;
-
-             /* Read toc relocs.  */
-             relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
-                                                   TRUE);
-             if (relstart == NULL)
-               goto error_ret;
-
-             /* Remove unused toc relocs, and adjust those we keep.  */
-             wrel = relstart;
-             for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
-               if (skip[rel->r_offset >> 3] != (unsigned long) -1)
-                 {
-                   wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
-                   wrel->r_info = rel->r_info;
-                   wrel->r_addend = rel->r_addend;
-                   ++wrel;
-                 }
-               else if (!dec_dynrel_count (rel->r_info, toc, info,
-                                           &local_syms, NULL, NULL))
-                 goto error_ret;
-
-             toc->reloc_count = wrel - relstart;
-             sz = elf_section_data (toc)->rel_hdr.sh_entsize;
-             elf_section_data (toc)->rel_hdr.sh_size = toc->reloc_count * sz;
-             BFD_ASSERT (elf_section_data (toc)->rel_hdr2 == NULL);
-           }
-
-         /* Adjust addends for relocs against the toc section sym.  */
+         /* Adjust addends for relocs against the toc section sym,
+            and optimize any accesses we can.  */
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
            {
              if (sec->reloc_count == 0
@@ -8156,7 +8284,7 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                continue;
 
              relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
-                                                   TRUE);
+                                                   info->keep_memory);
              if (relstart == NULL)
                goto error_ret;
 
@@ -8166,7 +8294,7 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                  unsigned long r_symndx;
                  asection *sym_sec;
                  struct elf_link_hash_entry *h;
-                 Elf_Internal_Sym *sym;
+                 bfd_vma val;
 
                  r_type = ELF64_R_TYPE (rel->r_info);
                  switch (r_type)
@@ -8189,41 +8317,91 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
                                  r_symndx, ibfd))
                    goto error_ret;
 
-                 if (sym_sec != toc || h != NULL || sym->st_value != 0)
+                 if (sym_sec != toc)
+                   continue;
+
+                 if (h != NULL)
+                   val = h->root.u.def.value;
+                 else
+                   {
+                     val = sym->st_value;
+                     if (val != 0)
+                       local_toc_syms = TRUE;
+                   }
+
+                 val += rel->r_addend;
+
+                 if (val > toc->rawsize)
+                   val = toc->rawsize;
+                 else if ((skip[val >> 3] & ref_from_discarded) != 0)
+                   continue;
+                 else if ((skip[val >> 3] & can_optimize) != 0)
+                   {
+                     Elf_Internal_Rela *tocrel
+                       = elf_section_data (toc)->relocs + (skip[val >> 3] >> 2);
+                     unsigned long tsym = ELF64_R_SYM (tocrel->r_info);
+
+                     switch (r_type)
+                       {
+                       case R_PPC64_TOC16_HA:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_TOC16_HA);
+                         break;
+
+                       case R_PPC64_TOC16_LO_DS:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT);
+                         break;
+
+                       default:
+                         abort ();
+                       }
+                     rel->r_addend = tocrel->r_addend;
+                     elf_section_data (sec)->relocs = relstart;
+                     continue;
+                   }
+
+                 if (h != NULL || sym->st_value != 0)
                    continue;
 
-                 rel->r_addend -= skip[rel->r_addend >> 3];
+                 rel->r_addend -= skip[val >> 3];
+                 elf_section_data (sec)->relocs = relstart;
                }
+
+             if (elf_section_data (sec)->relocs != relstart)
+               free (relstart);
            }
 
          /* We shouldn't have local or global symbols defined in the TOC,
             but handle them anyway.  */
-         if (local_syms != NULL)
-           {
-             Elf_Internal_Sym *sym;
+         for (sym = local_syms;
+              sym < local_syms + symtab_hdr->sh_info;
+              ++sym)
+           if (sym->st_value != 0
+               && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc)
+             {
+               unsigned long i;
 
-             for (sym = local_syms;
-                  sym < local_syms + symtab_hdr->sh_info;
-                  ++sym)
-               if (sym->st_value != 0
-                   && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc)
+               if (sym->st_value > toc->rawsize)
+                 i = toc->rawsize >> 3;
+               else
+                 i = sym->st_value >> 3;
+
+               if ((skip[i] & (ref_from_discarded | can_optimize)) != 0)
                  {
-                   if (skip[sym->st_value >> 3] != (unsigned long) -1)
-                     sym->st_value -= skip[sym->st_value >> 3];
-                   else
-                     {
-                       (*_bfd_error_handler)
-                         (_("%s defined in removed toc entry"),
-                          bfd_elf_sym_name (ibfd, symtab_hdr, sym,
-                                            NULL));
-                       sym->st_value = 0;
-                       sym->st_shndx = SHN_ABS;
-                     }
-                   symtab_hdr->contents = (unsigned char *) local_syms;
+                   if (local_toc_syms)
+                     (*_bfd_error_handler)
+                       (_("%s defined on removed toc entry"),
+                        bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
+                   do
+                     ++i;
+                   while ((skip[i] & (ref_from_discarded | can_optimize)));
+                   sym->st_value = (bfd_vma) i << 3;
                  }
-           }
 
-         /* Finally, adjust any global syms defined in the toc.  */
+               sym->st_value -= skip[i];
+               symtab_hdr->contents = (unsigned char *) local_syms;
+             }
+
+         /* Adjust any global syms defined in this toc input section.  */
          if (toc_inf.global_toc_syms)
            {
              toc_inf.toc = toc;
@@ -8232,6 +8410,38 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
              elf_link_hash_traverse (elf_hash_table (info), adjust_toc_syms,
                                      &toc_inf);
            }
+
+         if (toc->reloc_count != 0)
+           {
+             Elf_Internal_Rela *wrel;
+             bfd_size_type sz;
+
+             /* Read toc relocs.  */
+             relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+                                                   TRUE);
+             if (relstart == NULL)
+               goto error_ret;
+
+             /* Remove unused toc relocs, and adjust those we keep.  */
+             wrel = relstart;
+             for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
+               if ((skip[rel->r_offset >> 3]
+                    & (ref_from_discarded | can_optimize)) == 0)
+                 {
+                   wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
+                   wrel->r_info = rel->r_info;
+                   wrel->r_addend = rel->r_addend;
+                   ++wrel;
+                 }
+               else if (!dec_dynrel_count (rel->r_info, toc, info,
+                                           &local_syms, NULL, NULL))
+                 goto error_ret;
+
+             toc->reloc_count = wrel - relstart;
+             sz = elf_section_data (toc)->rel_hdr.sh_entsize;
+             elf_section_data (toc)->rel_hdr.sh_size = toc->reloc_count * sz;
+             BFD_ASSERT (elf_section_data (toc)->rel_hdr2 == NULL);
+           }
        }
 
       if (local_syms != NULL
@@ -8248,6 +8458,16 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Return true iff input section I references the TOC using
+   instructions limited to +/-32k offsets.  */
+
+bfd_boolean
+ppc64_elf_has_small_toc_reloc (asection *i)
+{
+  return (is_ppc64_elf (i->owner)
+         && ppc64_elf_tdata (i->owner)->has_small_toc_reloc);
+}
+
 /* Allocate space for one GOT entry.  */
 
 static void
@@ -8284,6 +8504,26 @@ allocate_got (struct elf_link_hash_entry *h,
     }
 }
 
+/* This function merges got entries in the same toc group.  */
+
+static void
+merge_got_entries (struct got_entry **pent)
+{
+  struct got_entry *ent, *ent2;
+
+  for (ent = *pent; ent != NULL; ent = ent->next)
+    if (!ent->is_indirect)
+      for (ent2 = ent->next; ent2 != NULL; ent2 = ent2->next)
+       if (!ent2->is_indirect
+           && ent2->addend == ent->addend
+           && ent2->tls_type == ent->tls_type
+           && elf_gp (ent2->owner) == elf_gp (ent->owner))
+         {
+           ent2->is_indirect = TRUE;
+           ent2->got.ent = ent;
+         }
+}
+
 /* Allocate space in .plt, .got and associated reloc sections for
    dynamic relocs.  */
 
@@ -8295,7 +8535,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   asection *s;
   struct ppc_link_hash_entry *eh;
   struct ppc_dyn_relocs *p;
-  struct got_entry *gent;
+  struct got_entry **pgent, *gent;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -8394,8 +8634,30 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            gent->tls_type = TLS_TLS | TLS_TPREL;
        }
 
-  for (gent = h->got.glist; gent != NULL; gent = gent->next)
+  /* Remove any list entry that won't generate a word in the GOT before
+     we call merge_got_entries.  Otherwise we risk merging to empty
+     entries.  */
+  pgent = &h->got.glist;
+  while ((gent = *pgent) != NULL)
     if (gent->got.refcount > 0)
+      {
+       if ((gent->tls_type & TLS_LD) != 0
+           && !h->def_dynamic)
+         {
+           ppc64_tlsld_got (gent->owner)->got.refcount += 1;
+           *pgent = gent->next;
+         }
+       else
+         pgent = &gent->next;
+      }
+    else
+      *pgent = gent->next;
+
+  if (!htab->do_multi_toc)
+    merge_got_entries (&h->got.glist);
+
+  for (gent = h->got.glist; gent != NULL; gent = gent->next)
+    if (!gent->is_indirect)
       {
        /* Make sure this symbol is output as a dynamic symbol.
           Undefined weak syms won't yet be marked as dynamic,
@@ -8409,21 +8671,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
              return FALSE;
          }
 
-       if ((gent->tls_type & TLS_LD) != 0
-           && !h->def_dynamic)
-         {
-           ppc64_tlsld_got (gent->owner)->got.refcount += 1;
-           gent->got.offset = (bfd_vma) -1;
-           continue;
-         }
-
        if (!is_ppc64_elf (gent->owner))
          abort ();
 
        allocate_got (h, info, gent);
       }
-    else
-      gent->got.offset = (bfd_vma) -1;
 
   if (eh->dyn_relocs == NULL
       || (!htab->elf.dynamic_sections_created
@@ -8563,6 +8815,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   asection *s;
   bfd_boolean relocs;
   bfd *ibfd;
+  struct got_entry *first_tlsld;
 
   htab = ppc_hash_table (info);
   if (htab == NULL)
@@ -8593,7 +8846,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       struct got_entry **end_lgot_ents;
       struct plt_entry **local_plt;
       struct plt_entry **end_local_plt;
-      char *lgot_masks;
+      unsigned char *lgot_masks;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
@@ -8636,20 +8889,21 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       end_lgot_ents = lgot_ents + locsymcount;
       local_plt = (struct plt_entry **) end_lgot_ents;
       end_local_plt = local_plt + locsymcount;
-      lgot_masks = (char *) end_local_plt;
+      lgot_masks = (unsigned char *) end_local_plt;
       s = ppc64_elf_tdata (ibfd)->got;
       srel = ppc64_elf_tdata (ibfd)->relgot;
       for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
        {
-         struct got_entry *ent;
+         struct got_entry **pent, *ent;
 
-         for (ent = *lgot_ents; ent != NULL; ent = ent->next)
+         pent = lgot_ents;
+         while ((ent = *pent) != NULL)
            if (ent->got.refcount > 0)
              {
                if ((ent->tls_type & *lgot_masks & TLS_LD) != 0)
                  {
                    ppc64_tlsld_got (ibfd)->got.refcount += 1;
-                   ent->got.offset = (bfd_vma) -1;
+                   *pent = ent->next;
                  }
                else
                  {
@@ -8667,10 +8921,11 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
                        htab->got_reli_size
                          += num * sizeof (Elf64_External_Rela);
                      }
+                   pent = &ent->next;
                  }
              }
            else
-             ent->got.offset = (bfd_vma) -1;
+             *pent = ent->next;
        }
 
       /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt.  */
@@ -8696,25 +8951,39 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
      sym dynamic relocs.  */
   elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
 
+  first_tlsld = NULL;
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
+      struct got_entry *ent;
+
       if (!is_ppc64_elf (ibfd))
        continue;
 
-      if (ppc64_tlsld_got (ibfd)->got.refcount > 0)
+      ent = ppc64_tlsld_got (ibfd);
+      if (ent->got.refcount > 0)
        {
-         s = ppc64_elf_tdata (ibfd)->got;
-         ppc64_tlsld_got (ibfd)->got.offset = s->size;
-         ppc64_tlsld_got (ibfd)->owner = ibfd;
-         s->size += 16;
-         if (info->shared)
+         if (!htab->do_multi_toc && first_tlsld != NULL)
            {
-             asection *srel = ppc64_elf_tdata (ibfd)->relgot;
-             srel->size += sizeof (Elf64_External_Rela);
+             ent->is_indirect = TRUE;
+             ent->got.ent = first_tlsld;
+           }
+         else
+           {
+             if (first_tlsld == NULL)
+               first_tlsld = ent;
+             s = ppc64_elf_tdata (ibfd)->got;
+             ent->got.offset = s->size;
+             ent->owner = ibfd;
+             s->size += 16;
+             if (info->shared)
+               {
+                 asection *srel = ppc64_elf_tdata (ibfd)->relgot;
+                 srel->size += sizeof (Elf64_External_Rela);
+               }
            }
        }
       else
-       ppc64_tlsld_got (ibfd)->got.offset = (bfd_vma) -1;
+       ent->got.offset = (bfd_vma) -1;
     }
 
   /* We now have determined the sizes of the various dynamic sections.
@@ -8737,7 +9006,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          /* Strip this section if we don't need it; see the
             comment below.  */
        }
-      else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela"))
+      else if (CONST_STRNEQ (s->name, ".rela"))
        {
          if (s->size != 0)
            {
@@ -8902,13 +9171,15 @@ ppc_type_of_stub (asection *input_sec,
       struct ppc_link_hash_entry *fdh = h;
       if (h->oh != NULL
          && h->oh->is_func_descriptor)
-       fdh = ppc_follow_link (h->oh);
+       {
+         fdh = ppc_follow_link (h->oh);
+         *hash = fdh;
+       }
 
       for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next)
        if (ent->addend == rel->r_addend
            && ent->plt.offset != (bfd_vma) -1)
          {
-           *hash = fdh;
            *plt_ent = ent;
            return ppc_stub_plt_call;
          }
@@ -8917,12 +9188,8 @@ ppc_type_of_stub (asection *input_sec,
         either a defined function descriptor or a defined entry symbol
         in a regular object file, then it is pointless trying to make
         any other type of stub.  */
-      if (!((fdh->elf.root.type == bfd_link_hash_defined
-           || fdh->elf.root.type == bfd_link_hash_defweak)
-           && fdh->elf.root.u.def.section->output_section != NULL)
-         && !((h->elf.root.type == bfd_link_hash_defined
-               || h->elf.root.type == bfd_link_hash_defweak)
-              && h->elf.root.u.def.section->output_section != NULL))
+      if (!is_static_defined (&fdh->elf)
+         && !is_static_defined (&h->elf))
        return ppc_stub_none;
     }
   else if (elf_local_got_ents (input_sec->owner) != NULL)
@@ -9764,7 +10031,7 @@ bfd_boolean
 ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
-  bfd_vma addr, off;
+  bfd_vma addr, off, limit;
 
   if (htab == NULL)
     return FALSE;
@@ -9780,7 +10047,10 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
 
       addr = isec->output_offset + isec->output_section->vma;
       off = addr - htab->toc_curr;
-      if (off + isec->size > 0x10000)
+      limit = 0x80008000;
+      if (ppc64_elf_tdata (isec->owner)->has_small_toc_reloc)
+       limit = 0x10000;
+      if (off + isec->size > limit)
        {
          addr = (htab->toc_first_sec->output_offset
                  + htab->toc_first_sec->output_section->vma);
@@ -9826,43 +10096,11 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
   return TRUE;
 }
 
-/* This function removes unneeded got entries (those with got.offset == -1)
-   and merges entries in the same toc group.  */
+/* Called via elf_link_hash_traverse to merge GOT entries for global
+   symbol H.  */
 
-static void
-merge_got_entries (struct got_entry **pent)
-{
-  struct got_entry *ent, *ent2;
-
-  while ((ent = *pent) != NULL)
-    {
-      if (!ent->is_indirect)
-       {
-         if (ent->got.offset == (bfd_vma) -1)
-           {
-             *pent = ent->next;
-             continue;
-           }
-         for (ent2 = ent->next; ent2 != NULL; ent2 = ent2->next)
-           if (!ent2->is_indirect
-               && ent2->got.offset != (bfd_vma) -1
-               && ent2->addend == ent->addend
-               && ent2->tls_type == ent->tls_type
-               && elf_gp (ent2->owner) == elf_gp (ent->owner))
-             {
-               ent2->is_indirect = TRUE;
-               ent2->got.ent = ent;
-             }
-       }
-      pent = &ent->next;
-    }
-}
-
-/* Called via elf_link_hash_traverse to merge GOT entries for global
-   symbol H.  */
-
-static bfd_boolean
-merge_global_got (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
+static bfd_boolean
+merge_global_got (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
 {
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -9908,30 +10146,10 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
 
   htab->multi_toc_needed = htab->toc_curr != elf_gp (info->output_bfd);
 
-  /* Merge local got entries within a toc group.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
-    {
-      struct got_entry **lgot_ents;
-      struct got_entry **end_lgot_ents;
-      Elf_Internal_Shdr *symtab_hdr;
-      bfd_size_type locsymcount;
-
-      if (!is_ppc64_elf (ibfd))
-       continue;
-
-      lgot_ents = elf_local_got_ents (ibfd);
-      if (!lgot_ents)
-       continue;
-
-      symtab_hdr = &elf_symtab_hdr (ibfd);
-      locsymcount = symtab_hdr->sh_info;
-      end_lgot_ents = lgot_ents + locsymcount;
-
-      for (; lgot_ents < end_lgot_ents; ++lgot_ents)
-       merge_got_entries (lgot_ents);
-    }
+  if (!htab->do_multi_toc)
+    return FALSE;
 
-  /* And the same for global sym got entries.  */
+  /* Merge global sym got entries within a toc group.  */
   elf_link_hash_traverse (&htab->elf, merge_global_got, info);
 
   /* And tlsld_got.  */
@@ -9994,7 +10212,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
       struct got_entry **end_lgot_ents;
       struct plt_entry **local_plt;
       struct plt_entry **end_local_plt;
-      char *lgot_masks;
+      unsigned char *lgot_masks;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *s, *srel;
@@ -10011,7 +10229,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
       end_lgot_ents = lgot_ents + locsymcount;
       local_plt = (struct plt_entry **) end_lgot_ents;
       end_local_plt = local_plt + locsymcount;
-      lgot_masks = (char *) end_local_plt;
+      lgot_masks = (unsigned char *) end_local_plt;
       s = ppc64_elf_tdata (ibfd)->got;
       srel = ppc64_elf_tdata (ibfd)->relgot;
       for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
@@ -10019,23 +10237,22 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
          struct got_entry *ent;
 
          for (ent = *lgot_ents; ent != NULL; ent = ent->next)
-           if (!ent->is_indirect)
-             {
-               unsigned int num = 1;
-               ent->got.offset = s->size;
-               if ((ent->tls_type & *lgot_masks & TLS_GD) != 0)
-                 num = 2;
-               s->size += num * 8;
-               if (info->shared)
-                 srel->size += num * sizeof (Elf64_External_Rela);
-               else if ((*lgot_masks & PLT_IFUNC) != 0)
-                 {
-                   htab->reliplt->size
-                     += num * sizeof (Elf64_External_Rela);
-                   htab->got_reli_size
-                     += num * sizeof (Elf64_External_Rela);
-                 }
-             }
+           {
+             unsigned int num = 1;
+             ent->got.offset = s->size;
+             if ((ent->tls_type & *lgot_masks & TLS_GD) != 0)
+               num = 2;
+             s->size += num * 8;
+             if (info->shared)
+               srel->size += num * sizeof (Elf64_External_Rela);
+             else if ((*lgot_masks & PLT_IFUNC) != 0)
+               {
+                 htab->reliplt->size
+                   += num * sizeof (Elf64_External_Rela);
+                 htab->got_reli_size
+                   += num * sizeof (Elf64_External_Rela);
+               }
+           }
        }
     }
 
@@ -10099,9 +10316,6 @@ ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
-  if (htab == NULL)
-    return;
-
   /* After the second pass, toc_curr tracks the TOC offset used
      for code sections below in ppc64_elf_next_input_section.  */
   htab->toc_curr = TOC_BASE_OFF;
@@ -10118,10 +10332,10 @@ ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info)
 static int
 toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 {
-  Elf_Internal_Rela *relstart, *rel;
-  Elf_Internal_Sym *local_syms;
   int ret;
-  struct ppc_link_hash_table *htab;
+
+  /* Mark this section as checked.  */
+  isec->call_check_done = 1;
 
   /* We know none of our code bearing sections will need toc stubs.  */
   if ((isec->flags & SEC_LINKER_CREATED) != 0)
@@ -10133,179 +10347,189 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
   if (isec->output_section == NULL)
     return 0;
 
-  if (isec->reloc_count == 0)
-    return 0;
-
-  relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL,
-                                       info->keep_memory);
-  if (relstart == NULL)
-    return -1;
-
-  /* Look for branches to outside of this section.  */
-  local_syms = NULL;
   ret = 0;
-  htab = ppc_hash_table (info);
-  if (htab == NULL)
-    return -1;
-
-  for (rel = relstart; rel < relstart + isec->reloc_count; ++rel)
+  if (isec->reloc_count != 0)
     {
-      enum elf_ppc64_reloc_type r_type;
-      unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
-      struct ppc_link_hash_entry *eh;
-      Elf_Internal_Sym *sym;
-      asection *sym_sec;
-      struct _opd_sec_data *opd;
-      bfd_vma sym_value;
-      bfd_vma dest;
-
-      r_type = ELF64_R_TYPE (rel->r_info);
-      if (r_type != R_PPC64_REL24
-         && r_type != R_PPC64_REL14
-         && r_type != R_PPC64_REL14_BRTAKEN
-         && r_type != R_PPC64_REL14_BRNTAKEN)
-       continue;
-
-      r_symndx = ELF64_R_SYM (rel->r_info);
-      if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_symndx,
-                     isec->owner))
-       {
-         ret = -1;
-         break;
-       }
-
-      /* Calls to dynamic lib functions go through a plt call stub
-        that uses r2.  */
-      eh = (struct ppc_link_hash_entry *) h;
-      if (eh != NULL
-         && (eh->elf.plt.plist != NULL
-             || (eh->oh != NULL
-                 && ppc_follow_link (eh->oh)->elf.plt.plist != NULL)))
-       {
-         ret = 1;
-         break;
-       }
+      Elf_Internal_Rela *relstart, *rel;
+      Elf_Internal_Sym *local_syms;
+      struct ppc_link_hash_table *htab;
 
-      if (sym_sec == NULL)
-       /* Ignore other undefined symbols.  */
-       continue;
+      relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL,
+                                           info->keep_memory);
+      if (relstart == NULL)
+       return -1;
 
-      /* Assume branches to other sections not included in the link need
-        stubs too, to cover -R and absolute syms.  */
-      if (sym_sec->output_section == NULL)
-       {
-         ret = 1;
-         break;
-       }
+      /* Look for branches to outside of this section.  */
+      local_syms = NULL;
+      htab = ppc_hash_table (info);
+      if (htab == NULL)
+       return -1;
 
-      if (h == NULL)
-       sym_value = sym->st_value;
-      else
+      for (rel = relstart; rel < relstart + isec->reloc_count; ++rel)
        {
-         if (h->root.type != bfd_link_hash_defined
-             && h->root.type != bfd_link_hash_defweak)
-           abort ();
-         sym_value = h->root.u.def.value;
-       }
-      sym_value += rel->r_addend;
+         enum elf_ppc64_reloc_type r_type;
+         unsigned long r_symndx;
+         struct elf_link_hash_entry *h;
+         struct ppc_link_hash_entry *eh;
+         Elf_Internal_Sym *sym;
+         asection *sym_sec;
+         struct _opd_sec_data *opd;
+         bfd_vma sym_value;
+         bfd_vma dest;
+
+         r_type = ELF64_R_TYPE (rel->r_info);
+         if (r_type != R_PPC64_REL24
+             && r_type != R_PPC64_REL14
+             && r_type != R_PPC64_REL14_BRTAKEN
+             && r_type != R_PPC64_REL14_BRNTAKEN)
+           continue;
 
-      /* If this branch reloc uses an opd sym, find the code section.  */
-      opd = get_opd_info (sym_sec);
-      if (opd != NULL)
-       {
-         if (h == NULL && opd->adjust != NULL)
+         r_symndx = ELF64_R_SYM (rel->r_info);
+         if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_symndx,
+                         isec->owner))
            {
-             long adjust;
+             ret = -1;
+             break;
+           }
 
-             adjust = opd->adjust[sym->st_value / 8];
-             if (adjust == -1)
-               /* Assume deleted functions won't ever be called.  */
-               continue;
-             sym_value += adjust;
+         /* Calls to dynamic lib functions go through a plt call stub
+            that uses r2.  */
+         eh = (struct ppc_link_hash_entry *) h;
+         if (eh != NULL
+             && (eh->elf.plt.plist != NULL
+                 || (eh->oh != NULL
+                     && ppc_follow_link (eh->oh)->elf.plt.plist != NULL)))
+           {
+             ret = 1;
+             break;
            }
 
-         dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL);
-         if (dest == (bfd_vma) -1)
+         if (sym_sec == NULL)
+           /* Ignore other undefined symbols.  */
            continue;
-       }
-      else
-       dest = (sym_value
-               + sym_sec->output_offset
-               + sym_sec->output_section->vma);
 
-      /* Ignore branch to self.  */
-      if (sym_sec == isec)
-       continue;
+         /* Assume branches to other sections not included in the
+            link need stubs too, to cover -R and absolute syms.  */
+         if (sym_sec->output_section == NULL)
+           {
+             ret = 1;
+             break;
+           }
 
-      /* If the called function uses the toc, we need a stub.  */
-      if (sym_sec->has_toc_reloc
-         || sym_sec->makes_toc_func_call)
-       {
-         ret = 1;
-         break;
-       }
+         if (h == NULL)
+           sym_value = sym->st_value;
+         else
+           {
+             if (h->root.type != bfd_link_hash_defined
+                 && h->root.type != bfd_link_hash_defweak)
+               abort ();
+             sym_value = h->root.u.def.value;
+           }
+         sym_value += rel->r_addend;
 
-      /* Assume any branch that needs a long branch stub might in fact
-        need a plt_branch stub.  A plt_branch stub uses r2.  */
-      else if (dest - (isec->output_offset
-                      + isec->output_section->vma
-                      + rel->r_offset) + (1 << 25) >= (2 << 25))
-       {
-         ret = 1;
-         break;
-       }
+         /* If this branch reloc uses an opd sym, find the code section.  */
+         opd = get_opd_info (sym_sec);
+         if (opd != NULL)
+           {
+             if (h == NULL && opd->adjust != NULL)
+               {
+                 long adjust;
 
-      /* If calling back to a section in the process of being tested, we
-        can't say for sure that no toc adjusting stubs are needed, so
-        don't return zero.  */
-      else if (sym_sec->call_check_in_progress)
-       ret = 2;
+                 adjust = opd->adjust[sym->st_value / 8];
+                 if (adjust == -1)
+                   /* Assume deleted functions won't ever be called.  */
+                   continue;
+                 sym_value += adjust;
+               }
 
-      /* Branches to another section that itself doesn't have any TOC
-        references are OK.  Recursively call ourselves to check.  */
-      else if (sym_sec->id <= htab->top_id
-              && htab->stub_group[sym_sec->id].toc_off == 0)
-       {
-         int recur;
+             dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL);
+             if (dest == (bfd_vma) -1)
+               continue;
+           }
+         else
+           dest = (sym_value
+                   + sym_sec->output_offset
+                   + sym_sec->output_section->vma);
 
-         /* Mark current section as indeterminate, so that other
-            sections that call back to current won't be marked as
-            known.  */
-         isec->call_check_in_progress = 1;
-         recur = toc_adjusting_stub_needed (info, sym_sec);
-         isec->call_check_in_progress = 0;
+         /* Ignore branch to self.  */
+         if (sym_sec == isec)
+           continue;
 
-         if (recur < 0)
+         /* If the called function uses the toc, we need a stub.  */
+         if (sym_sec->has_toc_reloc
+             || sym_sec->makes_toc_func_call)
            {
-             /* An error.  Exit.  */
-             ret = -1;
+             ret = 1;
              break;
            }
-         else if (recur <= 1)
+
+         /* Assume any branch that needs a long branch stub might in fact
+            need a plt_branch stub.  A plt_branch stub uses r2.  */
+         else if (dest - (isec->output_offset
+                          + isec->output_section->vma
+                          + rel->r_offset) + (1 << 25) >= (2 << 25))
            {
-             /* Known result.  Mark as checked and set section flag.  */
-             htab->stub_group[sym_sec->id].toc_off = 1;
+             ret = 1;
+             break;
+           }
+
+         /* If calling back to a section in the process of being
+            tested, we can't say for sure that no toc adjusting stubs
+            are needed, so don't return zero.  */
+         else if (sym_sec->call_check_in_progress)
+           ret = 2;
+
+         /* Branches to another section that itself doesn't have any TOC
+            references are OK.  Recursively call ourselves to check.  */
+         else if (!sym_sec->call_check_done)
+           {
+             int recur;
+
+             /* Mark current section as indeterminate, so that other
+                sections that call back to current won't be marked as
+                known.  */
+             isec->call_check_in_progress = 1;
+             recur = toc_adjusting_stub_needed (info, sym_sec);
+             isec->call_check_in_progress = 0;
+
              if (recur != 0)
                {
-                 sym_sec->makes_toc_func_call = 1;
-                 ret = 1;
-                 break;
+                 ret = recur;
+                 if (recur != 2)
+                   break;
                }
            }
-         else
-           {
-             /* Unknown result.  Continue checking.  */
-             ret = 2;
-           }
        }
+
+      if (local_syms != NULL
+         && (elf_symtab_hdr (isec->owner).contents
+             != (unsigned char *) local_syms))
+       free (local_syms);
+      if (elf_section_data (isec)->relocs != relstart)
+       free (relstart);
     }
 
-  if (local_syms != NULL
-      && (elf_symtab_hdr (isec->owner).contents != (unsigned char *) local_syms))
-    free (local_syms);
-  if (elf_section_data (isec)->relocs != relstart)
-    free (relstart);
+  if ((ret & 1) == 0
+      && isec->map_head.s != NULL
+      && (strcmp (isec->output_section->name, ".init") == 0
+         || strcmp (isec->output_section->name, ".fini") == 0))
+    {
+      if (isec->map_head.s->has_toc_reloc
+         || isec->map_head.s->makes_toc_func_call)
+       ret = 1;
+      else if (!isec->map_head.s->call_check_done)
+       {
+         int recur;
+         isec->call_check_in_progress = 1;
+         recur = toc_adjusting_stub_needed (info, isec->map_head.s);
+         isec->call_check_in_progress = 0;
+         if (recur != 0)
+           ret = recur;
+       }
+    }
+
+  if (ret == 1)
+    isec->makes_toc_func_call = 1;
 
   return ret;
 }
@@ -10351,23 +10575,55 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec)
          if (elf_gp (isec->owner) != 0)
            htab->toc_curr = elf_gp (isec->owner);
        }
-      else if (htab->stub_group[isec->id].toc_off == 0)
-       {
-         int ret = toc_adjusting_stub_needed (info, isec);
-         if (ret < 0)
-           return FALSE;
-         else
-           isec->makes_toc_func_call = ret & 1;
-       }
+      else if (!isec->call_check_done
+              && toc_adjusting_stub_needed (info, isec) < 0)
+       return FALSE;
     }
 
   /* Functions that don't use the TOC can belong in any TOC group.
      Use the last TOC base.  This happens to make _init and _fini
-     pasting work.  */
+     pasting work, because the fragments generally don't use the TOC.  */
   htab->stub_group[isec->id].toc_off = htab->toc_curr;
   return TRUE;
 }
 
+/* Check that all .init and .fini sections use the same toc, if they
+   have toc relocs.  */
+
+static bfd_boolean
+check_pasted_section (struct bfd_link_info *info, const char *name)
+{
+  asection *o = bfd_get_section_by_name (info->output_bfd, name);
+
+  if (o != NULL)
+    {
+      struct ppc_link_hash_table *htab = ppc_hash_table (info);
+      bfd_vma toc_off = 0;
+      asection *i;
+
+      for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+       if (i->has_toc_reloc)
+         {
+           if (toc_off == 0)
+             toc_off = htab->stub_group[i->id].toc_off;
+           else if (toc_off != htab->stub_group[i->id].toc_off)
+             return FALSE;
+         }
+      /* Make sure the whole pasted function uses the same toc offset.  */
+      if (toc_off != 0)
+       for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+         htab->stub_group[i->id].toc_off = toc_off;
+    }
+  return TRUE;
+}
+
+bfd_boolean
+ppc64_elf_check_init_fini (struct bfd_link_info *info)
+{
+  return (check_pasted_section (info, ".init")
+         & check_pasted_section (info, ".fini"));
+}
+
 /* See whether we can group stub sections together.  Grouping stub
    sections may result in fewer stubs.  More importantly, we need to
    put all .init* and .fini* stubs at the beginning of the .init or
@@ -10416,7 +10672,8 @@ group_sections (struct ppc_link_hash_table *htab,
 
          curr = tail;
          total = tail->size;
-         big_sec = total > (ppc64_elf_section_data (tail)->has_14bit_branch
+         big_sec = total > (ppc64_elf_section_data (tail) != NULL
+                            && ppc64_elf_section_data (tail)->has_14bit_branch
                             ? stub14_group_size : stub_group_size);
          if (big_sec && !suppress_size_errors)
            (*_bfd_error_handler) (_("%B section %A exceeds stub group size"),
@@ -10425,7 +10682,8 @@ group_sections (struct ppc_link_hash_table *htab,
 
          while ((prev = PREV_SEC (curr)) != NULL
                 && ((total += curr->output_offset - prev->output_offset)
-                    < (ppc64_elf_section_data (prev)->has_14bit_branch
+                    < (ppc64_elf_section_data (prev) != NULL
+                       && ppc64_elf_section_data (prev)->has_14bit_branch
                        ? stub14_group_size : stub_group_size))
                 && htab->stub_group[prev->id].toc_off == curr_toc)
            curr = prev;
@@ -10458,7 +10716,8 @@ group_sections (struct ppc_link_hash_table *htab,
              total = 0;
              while (prev != NULL
                     && ((total += tail->output_offset - prev->output_offset)
-                        < (ppc64_elf_section_data (prev)->has_14bit_branch
+                        < (ppc64_elf_section_data (prev) != NULL
+                           && ppc64_elf_section_data (prev)->has_14bit_branch
                            ? stub14_group_size : stub_group_size))
                     && htab->stub_group[prev->id].toc_off == curr_toc)
                {
@@ -10713,7 +10972,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size)
                      && irela != internal_relocs)
                    {
                      /* Get tls info.  */
-                     char *tls_mask;
+                     unsigned char *tls_mask;
 
                      if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms,
                                         irela - 1, input_bfd))
@@ -11137,6 +11396,63 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (sec);
 }
 
+/* REL points to a low-part reloc on a largetoc instruction sequence.
+   Find the matching high-part reloc instruction and verify that it
+   is addis REG,x,imm.  If so, set *REG to x and return a pointer to
+   the high-part reloc.  */
+
+static const Elf_Internal_Rela *
+ha_reloc_match (const Elf_Internal_Rela *relocs,
+               const Elf_Internal_Rela *rel,
+               unsigned int *reg,
+               bfd_boolean match_addend,
+               const bfd *input_bfd,
+               const bfd_byte *contents)
+{
+  enum elf_ppc64_reloc_type r_type, r_type_ha;
+  bfd_vma r_info_ha, r_addend;
+
+  r_type = ELF64_R_TYPE (rel->r_info);
+  switch (r_type)
+    {
+    case R_PPC64_GOT_TLSLD16_LO:
+    case R_PPC64_GOT_TLSGD16_LO:
+    case R_PPC64_GOT_TPREL16_LO_DS:
+    case R_PPC64_GOT_DTPREL16_LO_DS:
+    case R_PPC64_GOT16_LO:
+    case R_PPC64_TOC16_LO:
+      r_type_ha = r_type + 2;
+      break;
+    case R_PPC64_GOT16_LO_DS:
+      r_type_ha = R_PPC64_GOT16_HA;
+      break;
+    case R_PPC64_TOC16_LO_DS:
+      r_type_ha = R_PPC64_TOC16_HA;
+      break;
+    default:
+      abort ();
+    }
+  r_info_ha = ELF64_R_INFO (ELF64_R_SYM (rel->r_info), r_type_ha);
+  r_addend = rel->r_addend;
+
+  while (--rel >= relocs)
+    if (rel->r_info == r_info_ha
+       && (!match_addend
+           || rel->r_addend == r_addend))
+      {
+       const bfd_byte *p = contents + (rel->r_offset & ~3);
+       unsigned int insn = bfd_get_32 (input_bfd, p);
+       if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */
+           && (insn & (0x1f << 21)) == (*reg << 21))
+         {
+           *reg = (insn >> 16) & 0x1f;
+           return rel;
+         }
+       break;
+      }
+  return NULL;
+}
+
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -11184,7 +11500,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela outrel;
   bfd_byte *loc;
   struct got_entry **local_got_ents;
+  unsigned char *ha_opt;
   bfd_vma TOCstart;
+  bfd_boolean no_ha_opt;
   bfd_boolean ret = TRUE;
   bfd_boolean is_opd;
   /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
@@ -11210,6 +11528,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   symtab_hdr = &elf_symtab_hdr (input_bfd);
   sym_hashes = elf_sym_hashes (input_bfd);
   is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
+  ha_opt = NULL;
+  no_ha_opt = FALSE;
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
@@ -11226,12 +11546,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       const char *sym_name;
       unsigned long r_symndx, toc_symndx;
       bfd_vma toc_addend;
-      char tls_mask, tls_gd, tls_type;
-      char sym_type;
+      unsigned char tls_mask, tls_gd, tls_type;
+      unsigned char sym_type;
       bfd_vma relocation;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
-      unsigned long insn, mask;
+      unsigned int insn;
+      bfd_vma mask;
       struct ppc_stub_hash_entry *stub_entry;
       bfd_vma max_br_offset;
       bfd_vma from;
@@ -11325,7 +11646,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        {
          struct plt_entry **local_plt = (struct plt_entry **)
            (local_got_ents + symtab_hdr->sh_info);
-         char *lgot_masks = (char *)
+         unsigned char *lgot_masks = (unsigned char *)
            (local_plt + symtab_hdr->sh_info);
          tls_mask = lgot_masks[r_symndx];
        }
@@ -11335,7 +11656,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              || r_type == R_PPC64_TLSLD))
        {
          /* Check for toc tls entries.  */
-         char *toc_tls;
+         unsigned char *toc_tls;
 
          if (!get_tls_mask (&toc_tls, &toc_symndx, &toc_addend,
                             &local_syms, rel, input_bfd))
@@ -11347,7 +11668,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
       /* Check that tls relocs are used with tls syms, and non-tls
         relocs are used with non-tls syms.  */
-      if (r_symndx != 0
+      if (r_symndx != STN_UNDEF
          && r_type != R_PPC64_NONE
          && (h == NULL
              || h->elf.root.type == bfd_link_hash_defined
@@ -11393,13 +11714,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        default:
          break;
 
+       case R_PPC64_LO_DS_OPT:
+         insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+         if ((insn & (0x3f << 26)) != 58u << 26)
+           abort ();
+         insn += (14u << 26) - (58u << 26);
+         bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+         r_type = R_PPC64_TOC16_LO;
+         rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+         break;
+
        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;
+           unsigned char *toc_tls;
            int retval;
 
            retval = get_tls_mask (&toc_tls, &toc_symndx, &toc_addend,
@@ -11437,6 +11768,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          }
          break;
 
+       case R_PPC64_GOT_TPREL16_HI:
+       case R_PPC64_GOT_TPREL16_HA:
+         if (tls_mask != 0
+             && (tls_mask & TLS_TPREL) == 0)
+           {
+             rel->r_offset -= d_offset;
+             bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+             r_type = R_PPC64_NONE;
+             rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+           }
+         break;
+
        case R_PPC64_GOT_TPREL16_DS:
        case R_PPC64_GOT_TPREL16_LO_DS:
          if (tls_mask != 0
@@ -11506,8 +11849,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                          + R_PPC64_GOT_TPREL16_DS);
              else
                {
-                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  rel->r_offset -= d_offset;
+                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  r_type = R_PPC64_NONE;
                }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
@@ -11574,9 +11917,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        if (local_sections[r_symndx] == sec)
                          break;
                      if (r_symndx >= symtab_hdr->sh_info)
-                       r_symndx = 0;
+                       r_symndx = STN_UNDEF;
                      rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-                     if (r_symndx != 0)
+                     if (r_symndx != STN_UNDEF)
                        rel->r_addend -= (local_syms[r_symndx].st_value
                                          + sec->output_offset
                                          + sec->output_section->vma);
@@ -11682,9 +12025,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (local_sections[r_symndx] == sec)
                  break;
              if (r_symndx >= symtab_hdr->sh_info)
-               r_symndx = 0;
+               r_symndx = STN_UNDEF;
              rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-             if (r_symndx != 0)
+             if (r_symndx != STN_UNDEF)
                rel->r_addend -= (local_syms[r_symndx].st_value
                                  + sec->output_offset
                                  + sec->output_section->vma);
@@ -11782,23 +12125,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
             linkage stubs needs to be followed by a nop, as the nop
             will be replaced with an instruction to restore the TOC
             base pointer.  */
-         stub_entry = NULL;
          fdh = h;
          if (h != NULL
              && h->oh != NULL
              && h->oh->is_func_descriptor)
            fdh = ppc_follow_link (h->oh);
-         if (((fdh != NULL
-               && fdh->elf.plt.plist != NULL)
-              || (sec != NULL
-                  && sec->output_section != NULL
-                  && sec->id <= htab->top_id
-                  && (htab->stub_group[sec->id].toc_off
-                      != htab->stub_group[input_section->id].toc_off))
-              || (h == NULL
-                  && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
-             && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh,
-                                                  rel, htab)) != NULL
+         stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab);
+         if (stub_entry != NULL
              && (stub_entry->stub_type == ppc_stub_plt_call
                  || stub_entry->stub_type == ppc_stub_plt_branch_r2off
                  || stub_entry->stub_type == ppc_stub_long_branch_r2off))
@@ -11883,7 +12216,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                unresolved_reloc = FALSE;
            }
 
-         if (stub_entry == NULL
+         if ((stub_entry == NULL
+              || stub_entry->stub_type == ppc_stub_long_branch
+              || stub_entry->stub_type == ppc_stub_plt_branch)
              && get_opd_info (sec) != NULL)
            {
              /* The branch destination is the value of the opd entry. */
@@ -11904,13 +12239,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  + input_section->output_offset
                  + input_section->output_section->vma);
 
-         if (stub_entry == NULL
-             && (relocation + addend - from + max_br_offset
-                 >= 2 * max_br_offset)
-             && r_type != R_PPC64_ADDR14_BRTAKEN
-             && r_type != R_PPC64_ADDR14_BRNTAKEN)
-           stub_entry = ppc_get_stub_entry (input_section, sec, h, rel,
-                                            htab);
+         if (stub_entry != NULL
+             && (stub_entry->stub_type == ppc_stub_long_branch
+                 || stub_entry->stub_type == ppc_stub_plt_branch)
+             && (r_type == R_PPC64_ADDR14_BRTAKEN
+                 || r_type == R_PPC64_ADDR14_BRNTAKEN
+                 || (relocation + addend - from + max_br_offset
+                     < 2 * max_br_offset)))
+           /* Don't use the stub if this branch is in range.  */
+           stub_entry = NULL;
 
          if (stub_entry != NULL)
            {
@@ -12044,7 +12381,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared,
                                                          &h->elf)
                        || (info->shared
-                           && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+                           && SYMBOL_CALLS_LOCAL (info, &h->elf)))
                      /* This is actually a static link, or it is a
                         -Bsymbolic link and the symbol is defined
                         locally, or the symbol was forced to be local
@@ -12232,7 +12569,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TOC:
          /* Relocation value is TOC base.  */
          relocation = TOCstart;
-         if (r_symndx == 0)
+         if (r_symndx == STN_UNDEF)
            relocation += htab->stub_group[input_section->id].toc_off;
          else if (unresolved_reloc)
            ;
@@ -12421,7 +12758,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
+             else if (!SYMBOL_CALLS_LOCAL (info, &h->elf)
                       && !is_opd
                       && r_type != R_PPC64_TOC)
                outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
@@ -12482,7 +12819,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                             sym_name);
                          ret = FALSE;
                        }
-                     else if (r_symndx == 0 || bfd_is_abs_section (sec))
+                     else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec))
                        ;
                      else if (sec == NULL || sec->owner == NULL)
                        {
@@ -12589,6 +12926,100 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          continue;
        }
 
+      /* Multi-instruction sequences that access the TOC can be
+        optimized, eg. addis ra,r2,0; addi rb,ra,x;
+        to             nop;           addi rb,r2,x;  */
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case R_PPC64_GOT_TLSLD16_HI:
+       case R_PPC64_GOT_TLSGD16_HI:
+       case R_PPC64_GOT_TPREL16_HI:
+       case R_PPC64_GOT_DTPREL16_HI:
+       case R_PPC64_GOT16_HI:
+       case R_PPC64_TOC16_HI:
+         /* These relocs would only be useful if building up an
+            offset to later add to r2, perhaps in an indexed
+            addressing mode instruction.  Don't try to optimize.
+            Unfortunately, the possibility of someone building up an
+            offset like this or even with the HA relocs, means that
+            we need to check the high insn when optimizing the low
+            insn.  */
+         break;
+
+       case R_PPC64_GOT_TLSLD16_HA:
+       case R_PPC64_GOT_TLSGD16_HA:
+       case R_PPC64_GOT_TPREL16_HA:
+       case R_PPC64_GOT_DTPREL16_HA:
+       case R_PPC64_GOT16_HA:
+       case R_PPC64_TOC16_HA:
+         /* nop is done later.  */
+         break;
+
+       case R_PPC64_GOT_TLSLD16_LO:
+       case R_PPC64_GOT_TLSGD16_LO:
+       case R_PPC64_GOT_TPREL16_LO_DS:
+       case R_PPC64_GOT_DTPREL16_LO_DS:
+       case R_PPC64_GOT16_LO:
+       case R_PPC64_GOT16_LO_DS:
+       case R_PPC64_TOC16_LO:
+       case R_PPC64_TOC16_LO_DS:
+         if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000)
+           {
+             bfd_byte *p = contents + (rel->r_offset & ~3);
+             insn = bfd_get_32 (input_bfd, p);
+             if ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+                 || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+                 || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+                 || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+                 || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+                 || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+                 || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+                 || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+                 || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+                 || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+                 || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+                 || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+                 || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+                 || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+                 || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+                     && (insn & 3) != 1)
+                 || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+                     && ((insn & 3) == 0 || (insn & 3) == 3)))
+               {
+                 unsigned int reg = (insn >> 16) & 0x1f;
+                 const Elf_Internal_Rela *ha;
+                 bfd_boolean match_addend;
+
+                 match_addend = (sym != NULL
+                                 && ELF_ST_TYPE (sym->st_info) == STT_SECTION);
+                 ha = ha_reloc_match (relocs, rel, &reg, match_addend,
+                                      input_bfd, contents);
+                 if (ha != NULL)
+                   {
+                     insn &= ~(0x1f << 16);
+                     insn |= reg << 16;
+                     bfd_put_32 (input_bfd, insn, p);
+                     if (ha_opt == NULL)
+                       {
+                         ha_opt = bfd_zmalloc (input_section->reloc_count);
+                         if (ha_opt == NULL)
+                           return FALSE;
+                       }
+                     ha_opt[ha - relocs] = 1;
+                   }
+                 else
+                   /* If we don't find a matching high part insn,
+                      something is fishy.  Refuse to nop any high
+                      part insn in this section.  */
+                   no_ha_opt = TRUE;
+               }
+           }
+         break;
+       }
+
       /* Do any further special processing.  */
       switch (r_type)
        {
@@ -12741,6 +13172,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
     }
 
+  if (ha_opt != NULL)
+    {
+      if (!no_ha_opt)
+       {
+         unsigned char *opt = ha_opt;
+         rel = relocs;
+         relend = relocs + input_section->reloc_count;
+         for (; rel < relend; opt++, rel++)
+           if (*opt != 0)
+             {
+               bfd_byte *p = contents + (rel->r_offset & ~3);
+               bfd_put_32 (input_bfd, NOP, p);
+             }
+       }
+      free (ha_opt);
+    }
+
   /* If we're emitting relocations, then shortly after this function
      returns, reloc offsets and addends for this section will be
      adjusted.  Worse, reloc symbol indices will be for the output
This page took 0.053596 seconds and 4 git commands to generate.