Merge with binutils-2_27-branch rev b5d3ac20

Change-Id: Iaa5f379bb904c3eb5b6e041c62c24076d2838063
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 035353b..b161a20 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,53 @@
+2017-03-09  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-09-26  Alan Modra  <amodra@gmail.com>
+	* elf-bfd.h (_bfd_elf_ppc_merge_fp_attributes): Declare.
+	* elf32-ppc.c (_bfd_elf_ppc_merge_fp_attributes): New function.
+	(ppc_elf_merge_obj_attributes): Use it.  Don't copy first file
+	attributes, merge them.  Don't warn about undefined tag bits,
+	or copy unknown values to output.
+	* elf64-ppc.c (ppc64_elf_merge_private_bfd_data): Call
+	_bfd_elf_ppc_merge_fp_attributes.
+
+2017-02-22  Alan Modra  <amodra@gmail.com>
+
+	* elf64-ppc.c (ppc64_elf_finish_dynamic_sections): Don't segfault
+	on .got or .plt output section being discarded by script.
+	* elf32-ppc.c (ppc_elf_finish_dynamic_sections): Likewise.  Move
+	vxworks splt temp.
+
+2017-02-21  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-12-06  Alan Modra  <amodra@gmail.com>
+	* elf64-ppc.c (ok_lo_toc_insn): Add r_type param.  Recognize
+	lq,lfq,lxv,lxsd,lxssp,lfdp,stq,stfq,stxv,stxsd,stxssp,stfdp.
+	Don't match lmd and stmd.
+
+2017-02-15  H.J. Lu  <hongjiu.lu@intel.com>
+
+	PR ld/21168
+	* elf32-i386.c (elf_i386_relocate_section): Allow
+	"lea foo@GOT, %reg" in PIC.
+
+2016-12-23  Maciej W. Rozycki  <macro@imgtec.com>
+
+	* bfd/elfxx-mips.c (_bfd_mips_post_process_headers): Revert
+	2016-02-23 change and remove EI_ABIVERSION 5 support.
+
+2016-11-30  Alan Modra  <amodra@gmail.com>
+
+	PR ld/20886
+	* elf64-ppc.c (ppc64_elf_size_stubs): Make rawsize max size seen
+	on any pass past STUB_SHRINK_ITER.
+
+2016-10-31  Alan Modra  <amodra@gmail.com>
+
+	PR 20748
+	* elf32-microblaze.c (microblaze_elf_finish_dynamic_sections): Revert
+	2016-05-13 change.
+
 2016-10-10  Christophe Lyon  <christophe.lyon@linaro.org>
 	Backport from mainline
 	2016-09-28  Christophe Lyon  <christophe.lyon@linaro.org>
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 163ef35..9b87037 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2376,6 +2376,8 @@
   (unsigned int, unsigned int);
 /* PowerPC elf_object_p tweak.  */
 extern bfd_boolean _bfd_elf_ppc_set_arch (bfd *);
+/* PowerPC .gnu.attributes handling common to both 32-bit and 64-bit.  */
+extern void _bfd_elf_ppc_merge_fp_attributes (bfd *, bfd *);
 
 /* Exported interface for writing elf corefile notes. */
 extern char *elfcore_write_note
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 4179572..d549ffe 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -3993,7 +3993,9 @@
 			      - gotplt->output_section->vma
 			      - gotplt->output_offset);
 
-	      if ((*(contents + rel->r_offset - 1) & 0xc7) == 0x5)
+	      if (rel->r_offset > 1
+		  && (*(contents + rel->r_offset - 1) & 0xc7) == 0x5
+		  && *(contents + rel->r_offset - 2) != 0x8d)
 		{
 		  if (bfd_link_pic (info))
 		    goto disallow_got32;
@@ -4263,13 +4265,15 @@
 
 	  relocation = (htab->elf.sgot->output_section->vma
 			+ htab->elf.sgot->output_offset + off);
-	  if ((*(contents + rel->r_offset - 1) & 0xc7) == 0x5)
+	  if (rel->r_offset > 1
+	      && (*(contents + rel->r_offset - 1) & 0xc7) == 0x5
+	      && *(contents + rel->r_offset - 2) != 0x8d)
 	    {
 	      if (bfd_link_pic (info))
 		{
 		  /* For PIC, disallow R_386_GOT32 without a base
-		     register since we don't know what the GOT base
-		     is.  */
+		     register, except for "lea foo@GOT, %reg", since
+		     we don't know what the GOT base is.  */
 		  const char *name;
 
 disallow_got32:
diff --git a/bfd/elf32-microblaze.c b/bfd/elf32-microblaze.c
index 5496d16..d964e17 100644
--- a/bfd/elf32-microblaze.c
+++ b/bfd/elf32-microblaze.c
@@ -3400,13 +3400,13 @@
             {
               asection *s;
 
-              s = bfd_get_linker_section (dynobj, name);
+              s = bfd_get_section_by_name (output_bfd, name);
               if (s == NULL)
                 dyn.d_un.d_val = 0;
               else
                 {
                   if (! size)
-                    dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+                    dyn.d_un.d_ptr = s->vma;
                   else
                     dyn.d_un.d_val = s->size;
                 }
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index e42ef1c..d42e2cd 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -4647,8 +4647,76 @@
   return TRUE;
 }
 
+/* Warn for conflicting Tag_GNU_Power_ABI_FP attributes between IBFD
+   and OBFD, and merge non-conflicting ones.  */
+void
+_bfd_elf_ppc_merge_fp_attributes (bfd *ibfd, bfd *obfd)
+{
+  obj_attribute *in_attr, *in_attrs;
+  obj_attribute *out_attr, *out_attrs;
 
-/* Merge object attributes from IBFD into OBFD.  Raise an error if
+  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+
+  in_attr = &in_attrs[Tag_GNU_Power_ABI_FP];
+  out_attr = &out_attrs[Tag_GNU_Power_ABI_FP];
+
+  if (in_attr->i != out_attr->i)
+    {
+      int in_fp = in_attr->i & 3;
+      int out_fp = out_attr->i & 3;
+
+      if (in_fp == 0)
+	;
+      else if (out_fp == 0)
+	{
+	  out_attr->type = 1;
+	  out_attr->i ^= in_fp;
+	}
+      else if (out_fp != 2 && in_fp == 2)
+	_bfd_error_handler
+	  (_("Warning: %B uses hard float, %B uses soft float"), obfd, ibfd);
+      else if (out_fp == 2 && in_fp != 2)
+	_bfd_error_handler
+	  (_("Warning: %B uses hard float, %B uses soft float"), ibfd, obfd);
+      else if (out_fp == 1 && in_fp == 3)
+	_bfd_error_handler
+	  (_("Warning: %B uses double-precision hard float, "
+	     "%B uses single-precision hard float"), obfd, ibfd);
+      else if (out_fp == 3 && in_fp == 1)
+	_bfd_error_handler
+	  (_("Warning: %B uses double-precision hard float, "
+	     "%B uses single-precision hard float"), ibfd, obfd);
+
+      in_fp = in_attr->i & 0xc;
+      out_fp = out_attr->i & 0xc;
+      if (in_fp == 0)
+	;
+      else if (out_fp == 0)
+	{
+	  out_attr->type = 1;
+	  out_attr->i ^= in_fp;
+	}
+      else if (out_fp != 2 * 4 && in_fp == 2 * 4)
+	_bfd_error_handler
+	  (_("Warning: %B uses 64-bit long double, "
+	     "%B uses 128-bit long double"), ibfd, obfd);
+      else if (in_fp != 2 * 4 && out_fp == 2 * 4)
+	_bfd_error_handler
+	  (_("Warning: %B uses 64-bit long double, "
+	     "%B uses 128-bit long double"), obfd, ibfd);
+      else if (out_fp == 1 * 4 && in_fp == 3 * 4)
+	_bfd_error_handler
+	  (_("Warning: %B uses IBM long double, "
+	     "%B uses IEEE long double"), ibfd, obfd);
+      else if (out_fp == 3 * 4 && in_fp == 1 * 4)
+	_bfd_error_handler
+	  (_("Warning: %B uses IBM long double, "
+	     "%B uses IEEE long double"), obfd, ibfd);
+    }
+}
+
+/* Merge object attributes from IBFD into OBFD.  Warn if
    there are conflicting attributes.  */
 static bfd_boolean
 ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
@@ -4656,108 +4724,47 @@
   obj_attribute *in_attr, *in_attrs;
   obj_attribute *out_attr, *out_attrs;
 
-  if (!elf_known_obj_attributes_proc (obfd)[0].i)
-    {
-      /* This is the first object.  Copy the attributes.  */
-      _bfd_elf_copy_obj_attributes (ibfd, obfd);
-
-      /* Use the Tag_null value to indicate the attributes have been
-	 initialized.  */
-      elf_known_obj_attributes_proc (obfd)[0].i = 1;
-
-      return TRUE;
-    }
+  _bfd_elf_ppc_merge_fp_attributes (ibfd, obfd);
 
   in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
   out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
 
-  /* Check for conflicting Tag_GNU_Power_ABI_FP attributes and merge
-     non-conflicting ones.  */
-  in_attr = &in_attrs[Tag_GNU_Power_ABI_FP];
-  out_attr = &out_attrs[Tag_GNU_Power_ABI_FP];
-  if (in_attr->i != out_attr->i)
-    {
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-	out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
-	;
-      else if (out_attr->i == 1 && in_attr->i == 2)
-	_bfd_error_handler
-	  (_("Warning: %B uses hard float, %B uses soft float"), obfd, ibfd);
-      else if (out_attr->i == 1 && in_attr->i == 3)
-	_bfd_error_handler
-	  (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
-	  obfd, ibfd);
-      else if (out_attr->i == 3 && in_attr->i == 1)
-	_bfd_error_handler
-	  (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
-	  ibfd, obfd);
-      else if (out_attr->i == 3 && in_attr->i == 2)
-	_bfd_error_handler
-	  (_("Warning: %B uses soft float, %B uses single-precision hard float"),
-	  ibfd, obfd);
-      else if (out_attr->i == 2 && (in_attr->i == 1 || in_attr->i == 3))
-	_bfd_error_handler
-	  (_("Warning: %B uses hard float, %B uses soft float"), ibfd, obfd);
-      else if (in_attr->i > 3)
-	_bfd_error_handler
-	  (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
-	   in_attr->i);
-      else
-	_bfd_error_handler
-	  (_("Warning: %B uses unknown floating point ABI %d"), obfd,
-	   out_attr->i);
-    }
-
   /* Check for conflicting Tag_GNU_Power_ABI_Vector attributes and
      merge non-conflicting ones.  */
   in_attr = &in_attrs[Tag_GNU_Power_ABI_Vector];
   out_attr = &out_attrs[Tag_GNU_Power_ABI_Vector];
   if (in_attr->i != out_attr->i)
     {
-      const char *in_abi = NULL, *out_abi = NULL;
+      int in_vec = in_attr->i & 3;
+      int out_vec = out_attr->i & 3;
 
-      switch (in_attr->i)
-	{
-	case 1: in_abi = "generic"; break;
-	case 2: in_abi = "AltiVec"; break;
-	case 3: in_abi = "SPE"; break;
-	}
-
-      switch (out_attr->i)
-	{
-	case 1: out_abi = "generic"; break;
-	case 2: out_abi = "AltiVec"; break;
-	case 3: out_abi = "SPE"; break;
-	}
-
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-	out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
+      if (in_vec == 0)
 	;
+      else if (out_vec == 0)
+	{
+	  out_attr->type = 1;
+	  out_attr->i = in_vec;
+	}
       /* For now, allow generic to transition to AltiVec or SPE
 	 without a warning.  If GCC marked files with their stack
 	 alignment and used don't-care markings for files which are
 	 not affected by the vector ABI, we could warn about this
 	 case too.  */
-      else if (out_attr->i == 1)
-	out_attr->i = in_attr->i;
-      else if (in_attr->i == 1)
+      else if (in_vec == 1)
 	;
-      else if (in_abi == NULL)
+      else if (out_vec == 1)
+	{
+	  out_attr->type = 1;
+	  out_attr->i = in_vec;
+	}
+      else if (out_vec < in_vec)
 	_bfd_error_handler
-	  (_("Warning: %B uses unknown vector ABI %d"), ibfd,
-	   in_attr->i);
-      else if (out_abi == NULL)
+	  (_("Warning: %B uses AltiVec vector ABI, %B uses SPE vector ABI"),
+	   obfd, ibfd);
+      else if (out_vec > in_vec)
 	_bfd_error_handler
-	  (_("Warning: %B uses unknown vector ABI %d"), obfd,
-	   in_attr->i);
-      else
-	_bfd_error_handler
-	  (_("Warning: %B uses vector ABI \"%s\", %B uses \"%s\""),
-	   ibfd, obfd, in_abi, out_abi);
+	  (_("Warning: %B uses AltiVec vector ABI, %B uses SPE vector ABI"),
+	   ibfd, obfd);
     }
 
   /* Check for conflicting Tag_GNU_Power_ABI_Struct_Return attributes
@@ -4766,25 +4773,24 @@
   out_attr = &out_attrs[Tag_GNU_Power_ABI_Struct_Return];
   if (in_attr->i != out_attr->i)
     {
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-       out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
+      int in_struct = in_attr->i & 3;
+      int out_struct = out_attr->i & 3;
+
+      if (in_struct == 0 || in_struct == 3)
        ;
-      else if (out_attr->i == 1 && in_attr->i == 2)
-       _bfd_error_handler
-         (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), obfd, ibfd);
-      else if (out_attr->i == 2 && in_attr->i == 1)
-       _bfd_error_handler
-         (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), ibfd, obfd);
-      else if (in_attr->i > 2)
-       _bfd_error_handler
-         (_("Warning: %B uses unknown small structure return convention %d"), ibfd,
-          in_attr->i);
-      else
-       _bfd_error_handler
-         (_("Warning: %B uses unknown small structure return convention %d"), obfd,
-          out_attr->i);
+      else if (out_struct == 0)
+	{
+	  out_attr->type = 1;
+	  out_attr->i = in_struct;
+	}
+      else if (out_struct < in_struct)
+	_bfd_error_handler
+	  (_("Warning: %B uses r3/r4 for small structure returns, "
+	     "%B uses memory"), obfd, ibfd);
+      else if (out_struct > in_struct)
+	_bfd_error_handler
+	  (_("Warning: %B uses r3/r4 for small structure returns, "
+	     "%B uses memory"), ibfd, obfd);
     }
 
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
@@ -10303,11 +10309,6 @@
   htab = ppc_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
   sdyn = bfd_get_linker_section (dynobj, ".dynamic");
-  if (htab->is_vxworks)
-    splt = bfd_get_linker_section (dynobj, ".plt");
-  else
-    splt = NULL;
-
   got = 0;
   if (htab->elf.hgot != NULL)
     got = SYM_VAL (htab->elf.hgot);
@@ -10370,7 +10371,8 @@
 	}
     }
 
-  if (htab->got != NULL)
+  if (htab->got != NULL
+      && htab->got->output_section != bfd_abs_section_ptr)
     {
       if (htab->elf.hgot->root.u.def.section == htab->got
 	  || htab->elf.hgot->root.u.def.section == htab->sgotplt)
@@ -10410,7 +10412,12 @@
     }
 
   /* Fill in the first entry in the VxWorks procedure linkage table.  */
-  if (splt && splt->size > 0)
+  splt = NULL;
+  if (htab->is_vxworks)
+    splt = bfd_get_linker_section (dynobj, ".plt");
+  if (splt != NULL
+      && splt->size != 0
+      && splt->output_section != bfd_abs_section_ptr)
     {
       /* Use the right PLT. */
       const bfd_vma *plt_entry = (bfd_link_pic (info)
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index d236732..ee3c3b2 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -5998,6 +5998,8 @@
       return FALSE;
     }
 
+  _bfd_elf_ppc_merge_fp_attributes (ibfd, obfd);
+
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
   _bfd_elf_merge_object_attributes (ibfd, obfd);
 
@@ -8803,12 +8805,14 @@
   return TRUE;
 }
 
-/* Return TRUE iff INSN is one we expect on a _LO variety toc/got reloc.  */
+/* Return TRUE iff INSN with a relocation of R_TYPE is one we expect
+   on a _LO variety toc/got reloc.  */
 
 static bfd_boolean
-ok_lo_toc_insn (unsigned int insn)
+ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type)
 {
-  return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+  return ((insn & (0x3f << 26)) == 12u << 26 /* addic */
+	  || (insn & (0x3f << 26)) == 14u << 26 /* addi */
 	  || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
 	  || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
 	  || (insn & (0x3f << 26)) == 36u << 26 /* stw */
@@ -8822,11 +8826,20 @@
 	  || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
 	  || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
 	  || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
-	  || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
-	      && (insn & 3) != 1)
-	  || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
-	      && ((insn & 3) == 0 || (insn & 3) == 3))
-	  || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+	  || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */
+	  || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+	      /* Exclude lfqu by testing reloc.  If relocs are ever
+		 defined for the reduced D field in psq_lu then those
+		 will need testing too.  */
+	      && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+	  || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */
+	      && (insn & 1) == 0)
+	  || (insn & (0x3f << 26)) == 60u << 26 /* stfq */
+	  || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+	      /* Exclude stfqu.  psq_stu as above for psq_lu.  */
+	      && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+	  || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */
+	      && (insn & 1) == 0));
 }
 
 /* Examine all relocs referencing .toc sections in order to remove
@@ -9131,7 +9144,7 @@
 			}
 		      insn = bfd_get_32 (ibfd, buf);
 		      if (insn_check == check_lo
-			  ? !ok_lo_toc_insn (insn)
+			  ? !ok_lo_toc_insn (insn, r_type)
 			  : ((insn & ((0x3f << 26) | 0x1f << 16))
 			     != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
 			{
@@ -12540,7 +12553,10 @@
 	   stub_sec = stub_sec->next)
 	if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
 	  {
-	    stub_sec->rawsize = stub_sec->size;
+	    if (htab->stub_iteration <= STUB_SHRINK_ITER
+		|| stub_sec->rawsize < stub_sec->size)
+	      /* Past STUB_SHRINK_ITER, rawsize is the max size seen.  */
+	      stub_sec->rawsize = stub_sec->size;
 	    stub_sec->size = 0;
 	    stub_sec->reloc_count = 0;
 	    stub_sec->flags &= ~SEC_RELOC;
@@ -15518,7 +15534,8 @@
 	}
     }
 
-  if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0)
+  if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0
+      && htab->elf.sgot->output_section != bfd_abs_section_ptr)
     {
       /* Fill in the first entry in the global offset table.
 	 We use it to hold the link-time TOCbase.  */
@@ -15530,7 +15547,8 @@
       elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 8;
     }
 
-  if (htab->elf.splt != NULL && htab->elf.splt->size != 0)
+  if (htab->elf.splt != NULL && htab->elf.splt->size != 0
+      && htab->elf.splt->output_section != bfd_abs_section_ptr)
     {
       /* Set .plt entry size.  */
       elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index e47276b..80e171d 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -16187,9 +16187,6 @@
   if (mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64
       || mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64A)
     i_ehdrp->e_ident[EI_ABIVERSION] = 3;
-
-  if (elf_stack_flags (abfd) && !(elf_stack_flags (abfd) & PF_X))
-    i_ehdrp->e_ident[EI_ABIVERSION] = 5;
 }
 
 int
diff --git a/bfd/version.h b/bfd/version.h
index b959b37..28e0359 100644
--- a/bfd/version.h
+++ b/bfd/version.h
@@ -1,4 +1,4 @@
-#define BFD_VERSION_DATE 20161019
+#define BFD_VERSION_DATE 20161229
 #define BFD_VERSION @bfd_version@
 #define BFD_VERSION_STRING  @bfd_version_package@ @bfd_version_string@
 #define REPORT_BUGS_TO @report_bugs_to@
diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index a70bdb7..c9b8130 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -1,3 +1,12 @@
+2017-03-09  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-09-26  Alan Modra  <amodra@gmail.com>
+	* readelf.c (display_power_gnu_attribute): Catch truncated section
+	for all powerpc attributes.  Display long double ABI.  Don't
+	capitalize words, except for names.  Show known bits of tag values
+	when some unknown bits are present.  Whitespace fixes.
+
 2016-08-03  Tristan Gingold  <gingold@adacore.com>
 
 	* configure: Regenerate.
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 274ddd1..98fc196 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -13237,47 +13237,77 @@
 			     const unsigned char * const end)
 {
   unsigned int len;
-  int val;
+  unsigned int val;
 
   if (tag == Tag_GNU_Power_ABI_FP)
     {
       val = read_uleb128 (p, &len, end);
       p += len;
       printf ("  Tag_GNU_Power_ABI_FP: ");
+      if (len == 0)
+	{
+	  printf (_("<corrupt>\n"));
+	  return p;
+	}
 
-      switch (val)
+      if (val > 15)
+	printf ("(%#x), ", val);
+
+      switch (val & 3)
 	{
 	case 0:
-	  printf (_("Hard or soft float\n"));
+	  printf (_("unspecified hard/soft float, "));
 	  break;
 	case 1:
-	  printf (_("Hard float\n"));
+	  printf (_("hard float, "));
 	  break;
 	case 2:
-	  printf (_("Soft float\n"));
+	  printf (_("soft float, "));
 	  break;
 	case 3:
-	  printf (_("Single-precision hard float\n"));
+	  printf (_("single-precision hard float, "));
 	  break;
-	default:
-	  printf ("??? (%d)\n", val);
+	}
+
+      switch (val & 0xC)
+	{
+	case 0:
+	  printf (_("unspecified long double\n"));
+	  break;
+	case 4:
+	  printf (_("128-bit IBM long double\n"));
+	  break;
+	case 8:
+	  printf (_("64-bit long double\n"));
+	  break;
+	case 12:
+	  printf (_("128-bit IEEE long double\n"));
 	  break;
 	}
       return p;
-   }
+    }
 
   if (tag == Tag_GNU_Power_ABI_Vector)
     {
       val = read_uleb128 (p, &len, end);
       p += len;
       printf ("  Tag_GNU_Power_ABI_Vector: ");
-      switch (val)
+      if (len == 0)
+	{
+	  printf (_("<corrupt>\n"));
+	  return p;
+	}
+
+      if (val > 3)
+	printf ("(%#x), ", val);
+
+      switch (val & 3)
 	{
 	case 0:
-	  printf (_("Any\n"));
+	  printf (_("unspecified\n"));
 	  break;
 	case 1:
-	  printf (_("Generic\n"));
+	  printf (_("generic\n"));
 	  break;
 	case 2:
 	  printf ("AltiVec\n");
@@ -13285,39 +13315,39 @@
 	case 3:
 	  printf ("SPE\n");
 	  break;
-	default:
-	  printf ("??? (%d)\n", val);
-	  break;
 	}
       return p;
-   }
+    }
 
   if (tag == Tag_GNU_Power_ABI_Struct_Return)
     {
-      if (p == end)
-	{
-	  warn (_("corrupt Tag_GNU_Power_ABI_Struct_Return\n"));
-	  return p;
-	}
-
       val = read_uleb128 (p, &len, end);
       p += len;
       printf ("  Tag_GNU_Power_ABI_Struct_Return: ");
-      switch (val)
-       {
-       case 0:
-         printf (_("Any\n"));
-         break;
-       case 1:
-         printf ("r3/r4\n");
-         break;
-       case 2:
-         printf (_("Memory\n"));
-         break;
-       default:
-         printf ("??? (%d)\n", val);
-         break;
-       }
+      if (len == 0)
+	{
+	  printf (_("<corrupt>\n"));
+	  return p;
+	}
+
+      if (val > 2)
+	printf ("(%#x), ", val);
+
+      switch (val & 3)
+	{
+	case 0:
+	  printf (_("unspecified\n"));
+	  break;
+	case 1:
+	  printf ("r3/r4\n");
+	  break;
+	case 2:
+	  printf (_("memory\n"));
+	  break;
+	case 3:
+	  printf ("???\n");
+	  break;
+	}
       return p;
     }
 
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 50fcf52..a786f29 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,29 @@
+2017-03-08  Peter Bergner <bergner@vnet.ibm.com>
+
+	* testsuite/gas/ppc/altivec2.d (as): Use the -mpower8 option.
+	(objdump): Use the -Mpower8 option.
+
+2017-03-09  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-09-26  Alan Modra  <amodra@gmail.com>
+	* config/tc-ppc.c (ppc_elf_gnu_attribute): New function.
+	(md_pseudo_table <ELF>): Handle "gnu_attribute".
+
+2017-03-08  Peter Bergner  <bergner@vnet.ibm.com>
+
+	Apply from master.
+	2017-03-08  Peter Bergner  <bergner@vnet.ibm.com>
+	* testsuite/gas/ppc/power9.d <lnia> New test.
+	* testsuite/gas/ppc/power9.s: Likewise.
+
+2017-22-16  Peter Bergner <bergner@vnet.ibm.com>
+
+	Apply from master.
+	2017-02-10  Nicholas Piggin  <npiggin@gmail.com>
+
+	* testsuite/gas/ppc/power9.d <scv, rfscv>: New tests.
+
 2016-09-16  Peter Bergner <bergner@vnet.ibm.com>
 
 	Apply from master.
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index fc2a045..975e8eb 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -133,6 +133,7 @@
 static void ppc_elf_lcomm (int);
 static void ppc_elf_localentry (int);
 static void ppc_elf_abiversion (int);
+static void ppc_elf_gnu_attribute (int);
 #endif
 
 #ifdef TE_PE
@@ -270,6 +271,7 @@
   { "lcomm",	ppc_elf_lcomm,	0 },
   { "localentry", ppc_elf_localentry,	0 },
   { "abiversion", ppc_elf_abiversion,	0 },
+  { "gnu_attribute", ppc_elf_gnu_attribute, 0},
 #endif
 
 #ifdef TE_PE
@@ -2314,6 +2316,28 @@
   demand_empty_rest_of_line ();
 }
 
+/* Parse a .gnu_attribute directive.  */
+static void
+ppc_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  int tag = obj_elf_vendor_attribute (OBJ_ATTR_GNU);
+
+  /* Check validity of defined powerpc tags.  */
+  if (tag == Tag_GNU_Power_ABI_FP
+      || tag == Tag_GNU_Power_ABI_Vector
+      || tag == Tag_GNU_Power_ABI_Struct_Return)
+    {
+      unsigned int val;
+
+      val = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, tag);
+
+      if ((tag == Tag_GNU_Power_ABI_FP && val > 15)
+	  || (tag == Tag_GNU_Power_ABI_Vector && val > 3)
+	  || (tag == Tag_GNU_Power_ABI_Struct_Return && val > 2))
+	as_warn (_("unknown .gnu_attribute value"));
+    }
+}
+
 /* Set ABI version in output file.  */
 void
 ppc_elf_end (void)
diff --git a/gas/testsuite/gas/ppc/altivec2.d b/gas/testsuite/gas/ppc/altivec2.d
index fc10fb5..26f9afa 100644
--- a/gas/testsuite/gas/ppc/altivec2.d
+++ b/gas/testsuite/gas/ppc/altivec2.d
@@ -1,5 +1,5 @@
-#as: -maltivec
-#objdump: -dr -Maltivec
+#as: -mpower8
+#objdump: -dr -Mpower8
 #name: Altivec ISA 2.07 instructions
 
 .*
diff --git a/gas/testsuite/gas/ppc/power9.d b/gas/testsuite/gas/ppc/power9.d
index 31e4530..3585387 100644
--- a/gas/testsuite/gas/ppc/power9.d
+++ b/gas/testsuite/gas/ppc/power9.d
@@ -312,8 +312,9 @@
 .*:	(f1 31 9d 6f|6f 9d 31 f1) 	xscvdphp vs41,vs51
 .*:	(f1 58 a7 6f|6f a7 58 f1) 	xvcvhpsp vs42,vs52
 .*:	(f1 79 af 6f|6f af 79 f1) 	xvcvsphp vs43,vs53
-.*:	(4c 60 00 04|04 00 60 4c) 	addpcis r3,0
-.*:	(4c 60 00 04|04 00 60 4c) 	addpcis r3,0
+.*:	(4c 60 00 04|04 00 60 4c) 	lnia    r3
+.*:	(4c 60 00 04|04 00 60 4c) 	lnia    r3
+.*:	(4c 60 00 04|04 00 60 4c) 	lnia    r3
 .*:	(4c 80 00 05|05 00 80 4c) 	addpcis r4,1
 .*:	(4c 80 00 05|05 00 80 4c) 	addpcis r4,1
 .*:	(4c bf ff c4|c4 ff bf 4c) 	addpcis r5,-2
@@ -391,4 +392,7 @@
 .*:	(ff d7 04 8e|8e 04 d7 ff) 	mffscrni f30,0
 .*:	(ff d7 1c 8e|8e 1c d7 ff) 	mffscrni f30,3
 .*:	(ff f8 04 8e|8e 04 f8 ff) 	mffsl   f31
+.*:	(01 00 00 44|44 00 00 01) 	scv     0
+.*:	(e1 0f 00 44|44 00 0f e1) 	scv     127
+.*:	(a4 00 00 4c|4c 00 00 a4) 	rfscv
 #pass
diff --git a/gas/testsuite/gas/ppc/power9.s b/gas/testsuite/gas/ppc/power9.s
index 469435d..4e3530f 100644
--- a/gas/testsuite/gas/ppc/power9.s
+++ b/gas/testsuite/gas/ppc/power9.s
@@ -303,6 +303,7 @@
 	xscvdphp    41,51
 	xvcvhpsp    42,52
 	xvcvsphp    43,53
+	lnia        3
 	addpcis     3,0
 	subpcis     3,0
 	addpcis     4,1
@@ -382,3 +383,6 @@
 	mffscrni    30,0
 	mffscrni    30,3
 	mffsl       31
+	scv         0
+	scv         127
+	rfscv
diff --git a/gold/ChangeLog b/gold/ChangeLog
index f5005ef..59c60e7 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,134 @@
+2017-02-22  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	* powerpc.cc (Target_powerpc::make_iplt_section): Check that
+	output_section exists before attempting add_output_section_data.
+	(Target_powerpc::make_brlt_section): Likewise.
+
+	2017-02-03  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (Powerpc_relobj::make_toc_relative): Don't crash
+	when no .toc section exists.
+
+	2017-01-13  H.J. Lu  <hongjiu.lu@intel.com>
+	PR gold/21040
+	* powerpc.cc (Powerpc_relobj<size, big_endian>::make_toc_relative):
+	Cast 0x80008000 to uint64_t.
+
+	2017-01-11  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (class Powerpc_copy_relocs): New.
+	(Powerpc_copy_relocs::emit): New function.
+	(Powerpc_relobj::relatoc_, toc_, no_toc_opt_): New variables.
+	(Powerpc_relobj::toc_shndx, set_no_toc_opt, no_toc_opt): New inlines.
+	(Powerpc_relobj::do_relocate_sections): New function.
+	(Powerpc_relobj::make_toc_relative): Likewise.
+	(Powerpc_relobj::do_find_special_sections): Stash away .rela.toc
+	and .toc too.
+	(ok_lo_toc_insn): Move earlier, and handle more insns.
+	(Target_powerpc::Scan::local): If optimizing toc accesses, set
+	no_toc_opt for entries we can't edit.  Check insn validity.
+	Emit "toc optimization is not supported" warning, downgraded
+	from error.
+	(Target_powerpc::Scan::global): Likewise.
+	(Target_powerpc::Relocate::relocate): Edit TOC indirect code
+	to TOC relative.  Don't emit "toc optimization is not supported"
+	error here.
+
+	2017-01-10  Cary Coutant  <ccoutant@gmail.com>
+	* aarch64.cc (AArch64_relobj::do_relocate_sections): Call
+	Sized_relobj_file::relocate_section_range().
+	* arm.cc (Arm_relobj::do_relocate_sections): Likewise.
+	* object.h (Sized_relobj_file::relocate_section_range): New method.
+	* reloc.cc (Sized_relobj_file::do_relocate_sections): Move
+	implementation...
+	(Sized_relobj_file::relocate_section_range): ...to new method.
+
+	2017-01-10  Alan Modra  <amodra@gmail.com>
+	* options.h: Add --secure-plt option.
+	* powerpc.cc (Target_powerpc::Scan::local): Detect and error
+	on -fPIC -mbss-plt code.
+	(Target_powerpc::Scan::global): Likewise.
+
+	2017-01-09  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (Target_powerpc::make_plt_section): Point sh_info of
+	".rela.plt" at ".plt".
+
+	2017-01-07  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc: Use shorter equivalent elfcpp typedef for
+	Reltype and reloc_size throughout.
+	(Target_powerpc::symval_for_branch): Exclude dynamic symbols.
+	(Target_powerpc::Scan::local): Use local var r_sym.
+	(Target_powerpc::Scan::global: Likewise.
+	(Target_powerpc::Relocate::relocate): Delete shadowing r_sym.
+
+	2016-12-08  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (Powerpc_relobj::stub_table): Return NULL rather
+	then asserting.
+
+	2016-12-08  Alan Modra  <amodra@gmail.com>
+	* options.h (--stub-group-multi): Fix typo.
+
+	2016-12-07  Alan Modra  <amodra@gmail.com>
+	* options.h (--stub-group-multi): New PowerPC option.
+	* powerpc.cc (Stub_control): Add multi_os_ var and param
+	to constructor.  Sort start_ var later.  Comment State.
+	(Stub_control::can_add_to_stub_group): Heed multi_os_.
+	(Target_powerpc::group_sections): Update.
+
+	2016-12-07  Alan Modra  <amodra@gmail.com>
+	PR gold/20878
+	* powerpc.cc (Stub_control): Replace stubs_always_before_branch_
+	with stubs_always_after_branch_, group_end_addr_ with
+	group_start_addr_.
+	(Stub_control::can_add_to_stub_group): Rewrite to suit scanning
+	sections by increasing address.
+	(Target_powerpc::group_sections): Scan that way.  Delete corner
+	case.
+	* options.h (--stub-group-size): Update help string.
+
+	2016-12-07  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (Stub_table_owner): Provide constructor.
+	(Powerpc_relobj::set_stub_table): Resize fill with -1.
+	(Target_powerpc::Branch_info::make_stub): Provide target debug
+	output on returning false.
+
+	2016-12-01  Cary Coutant  <ccoutant@gmail.com>
+	PR gold/20807
+	* aarch64.cc (Target_aarch64::scan_reloc_section_for_stubs): Handle
+	section symbols correctly.
+	* arm.cc (Target_arm): Likewise.
+	* powerpc.cc (Target_powerpc): Likewise.
+
+	2016-08-31  Alan Modra  <amodra@gmail.com>
+	* powerpc.cc (class Stub_control): Delete stub14_group_size_
+	and has14_.  Add group_size_.
+	(Stub_control::can_add_to_stub_group): Adjust to suit.  Print
+	debug info when switching to adding sections before stubs.
+
+	2016-08-31  Alan Modra  <amodra@gmail.com>
+	* debug.h (DEBUG_TARGET): New.
+	(DEBUG_ALL): Add DEBUG_TARGET.
+	(gold_debug): Delete FORMAT param.
+	* powerpc.cc (Stub_control::can_add_to_stub_group): Print debug ourput.
+
+	2016-08-30  Alan Modra  <amodra@gmail.com>
+	PR 20523
+	* powerpc.cc (class Stub_control): Add has14_.  Comment owner_.
+	(Stub_control::can_add_to_stub_group): Correct grouping of
+	sections containing 14-bit external branches.  When returning
+	false, set state_ to reflect the fact that we have one section
+	for the next group.  Rewrite most of function for clarity.
+	Add and expand comments.
+	(Target_powerpc::do_relax): Print stub group size retry in hex.
+
+	2016-08-26  Han Shen  <shenhan@google.com>
+	PR gold/20529 - relaxing loop never ends.
+	* powerpc.cc (Stub_table::min_size_threshold_): New member to
+	limit size.
+	(Stub_table::set_min_size_threshold): New member function.
+	(Stub_table::set_address_and_size): Add code to only allow size
+	increase.
+	(Target_powerpc::do_relax): Add code to record last size.
+
 2016-09-26  Cary Coutant  <ccoutant@gmail.com>
 
 	PR gold/20238
diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index db9f06c..96ed66e 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -2044,9 +2044,9 @@
     const unsigned char* pshdrs, Output_file* of,
     typename Sized_relobj_file<size, big_endian>::Views* pviews)
 {
-  // Call parent to relocate sections.
-  Sized_relobj_file<size, big_endian>::do_relocate_sections(symtab, layout,
-							    pshdrs, of, pviews);
+  // Relocate the section data.
+  this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+			       1, this->shnum() - 1);
 
   // We do not generate stubs if doing a relocatable link.
   if (parameters->options().relocatable())
@@ -3865,6 +3865,8 @@
 	  if (!is_defined_in_discarded_section)
 	    {
 	      typedef Sized_relobj_file<size, big_endian> ObjType;
+	      if (psymval->is_section_symbol())
+		symval.set_is_section_symbol();
 	      typename ObjType::Compute_final_local_value_status status =
 		object->compute_final_local_value(r_sym, psymval, &symval,
 						  relinfo->symtab);
diff --git a/gold/arm.cc b/gold/arm.cc
index c47b002..d9c0a2b 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -6555,9 +6555,9 @@
     Output_file* of,
     typename Sized_relobj_file<32, big_endian>::Views* pviews)
 {
-  // Call parent to relocate sections.
-  Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout,
-							  pshdrs, of, pviews);
+  // Relocate the section data.
+  this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+			       1, this->shnum() - 1);
 
   // We do not generate stubs if doing a relocatable link.
   if (parameters->options().relocatable())
@@ -11998,6 +11998,8 @@
 	  if (!is_defined_in_discarded_section)
 	    {
 	      typedef Sized_relobj_file<32, big_endian> ObjType;
+	      if (psymval->is_section_symbol())
+		symval.set_is_section_symbol();
 	      typename ObjType::Compute_final_local_value_status status =
 		arm_object->compute_final_local_value(r_sym, psymval, &symval,
 						      relinfo->symtab);
diff --git a/gold/debug.h b/gold/debug.h
index e95408f..6fd72c2 100644
--- a/gold/debug.h
+++ b/gold/debug.h
@@ -39,10 +39,11 @@
 const int DEBUG_RELAXATION = 0x8;
 const int DEBUG_INCREMENTAL = 0x10;
 const int DEBUG_LOCATION = 0x20;
+const int DEBUG_TARGET = 0x40;
 
 const int DEBUG_ALL = (DEBUG_TASK | DEBUG_SCRIPT | DEBUG_FILES
 		       | DEBUG_RELAXATION | DEBUG_INCREMENTAL
-		       | DEBUG_LOCATION);
+		       | DEBUG_LOCATION | DEBUG_TARGET);
 
 // Convert a debug string to the appropriate enum.
 inline int
@@ -57,6 +58,7 @@
     { "relaxation", DEBUG_RELAXATION },
     { "incremental", DEBUG_INCREMENTAL },
     { "location", DEBUG_LOCATION },
+    { "target", DEBUG_TARGET },
     { "all", DEBUG_ALL }
   };
 
@@ -70,11 +72,11 @@
 // Print a debug message if TYPE is enabled.  This is a macro so that
 // we only evaluate the arguments if necessary.
 
-#define gold_debug(TYPE, FORMAT, ...)				\
+#define gold_debug(TYPE, ...)					\
   do								\
     {								\
       if (is_debugging_enabled(TYPE))				\
-	parameters->errors()->debug(FORMAT, __VA_ARGS__);	\
+	parameters->errors()->debug(__VA_ARGS__);		\
     }								\
   while (0)
 
diff --git a/gold/object.h b/gold/object.h
index 95f6d56..aa1a815 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -2562,6 +2562,13 @@
 		       const unsigned char* pshdrs, Output_file* of,
 		       Views* pviews);
 
+  // Relocate section data for a range of sections.
+  void
+  relocate_section_range(const Symbol_table* symtab, const Layout* layout,
+			 const unsigned char* pshdrs, Output_file* of,
+			 Views* pviews, unsigned int start_shndx,
+			 unsigned int end_shndx);
+
   // Adjust this local symbol value.  Return false if the symbol
   // should be discarded from the output file.
   virtual bool
diff --git a/gold/options.h b/gold/options.h
index 4c5b2ae..6786e93 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -1069,6 +1069,9 @@
   DEFINE_special(section_start, options::TWO_DASHES, '\0',
 		 N_("Set address of section"), N_("SECTION=ADDRESS"));
 
+  DEFINE_bool(secure_plt, options::TWO_DASHES , '\0', true,
+	      N_("(PowerPC only) Use new-style PLT"), NULL);
+
   DEFINE_optional_string(sort_common, options::TWO_DASHES, '\0', NULL,
 			 N_("Sort common symbols by alignment"),
 			 N_("[={ascending,descending}]"));
@@ -1097,11 +1100,14 @@
 
   DEFINE_int(stub_group_size, options::TWO_DASHES , '\0', 1,
 	     N_("(ARM, PowerPC only) The maximum distance from instructions "
-		"in a group of sections to their stubs.  Negative values mean "
-		"stubs are always after (PowerPC before) the group.  1 means "
-		"use default size.\n"),
+		"in a group of sections to their stubs. Negative values mean "
+		"stubs are always after the group. 1 means use default size"),
 	     N_("SIZE"));
 
+  DEFINE_bool(stub_group_multi, options::TWO_DASHES, '\0', false,
+	      N_("(PowerPC only) Allow a group of stubs to serve multiple "
+		 "output sections"), NULL);
+
   DEFINE_bool(no_keep_memory, options::TWO_DASHES, '\0', false,
 	      N_("Use less memory and more disk I/O "
 		 "(included only for compatibility with GNU ld)"), NULL);
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 60530ba..2c70f9f 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -70,6 +70,10 @@
 
 struct Stub_table_owner
 {
+  Stub_table_owner()
+    : output_section(NULL), owner(NULL)
+  { }
+
   Output_section* output_section;
   const Output_section::Input_section* owner;
 };
@@ -88,8 +92,9 @@
   Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
 		 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
-      special_(0), has_small_toc_reloc_(false), opd_valid_(false),
-      opd_ent_(), access_from_map_(), has14_(), stub_table_index_(),
+      special_(0), relatoc_(0), toc_(0), no_toc_opt_(),
+      has_small_toc_reloc_(false), opd_valid_(false), opd_ent_(),
+      access_from_map_(), has14_(), stub_table_index_(),
       e_flags_(ehdr.get_e_flags()), st_other_()
   {
     this->set_abiversion(0);
@@ -102,6 +107,52 @@
   void
   do_read_symbols(Read_symbols_data*);
 
+  // Arrange to always relocate .toc first.
+  virtual void
+  do_relocate_sections(
+      const Symbol_table* symtab, const Layout* layout,
+      const unsigned char* pshdrs, Output_file* of,
+      typename Sized_relobj_file<size, big_endian>::Views* pviews);
+
+  // The .toc section index.
+  unsigned int
+  toc_shndx() const
+  {
+    return this->toc_;
+  }
+
+  // Mark .toc entry at OFF as not optimizable.
+  void
+  set_no_toc_opt(Address off)
+  {
+    if (this->no_toc_opt_.empty())
+      this->no_toc_opt_.resize(this->section_size(this->toc_shndx())
+			       / (size / 8));
+    off /= size / 8;
+    if (off < this->no_toc_opt_.size())
+      this->no_toc_opt_[off] = true;
+  }
+
+  // Mark the entire .toc as not optimizable.
+  void
+  set_no_toc_opt()
+  {
+    this->no_toc_opt_.resize(1);
+    this->no_toc_opt_[0] = true;
+  }
+
+  // Return true if code using the .toc entry at OFF should not be edited.
+  bool
+  no_toc_opt(Address off) const
+  {
+    if (this->no_toc_opt_.empty())
+      return false;
+    off /= size / 8;
+    if (off >= this->no_toc_opt_.size())
+      return true;
+    return this->no_toc_opt_[off];
+  }
+
   // The .got2 section shndx.
   unsigned int
   got2_shndx() const
@@ -184,6 +235,12 @@
 		  const unsigned char* prelocs,
 		  const unsigned char* plocal_syms);
 
+  // Returns true if a code sequence loading a TOC entry can be
+  // converted into code calculating a TOC pointer relative offset.
+  bool
+  make_toc_relative(Target_powerpc<size, big_endian>* target,
+		    Address* value);
+
   // Perform the Sized_relobj_file method, then set up opd info from
   // .opd relocs.
   void
@@ -275,7 +332,7 @@
   set_stub_table(unsigned int shndx, unsigned int stub_index)
   {
     if (shndx >= this->stub_table_index_.size())
-      this->stub_table_index_.resize(shndx + 1);
+      this->stub_table_index_.resize(shndx + 1, -1);
     this->stub_table_index_[shndx] = stub_index;
   }
 
@@ -288,8 +345,8 @@
 	  = static_cast<Target_powerpc<size, big_endian>*>(
 	      parameters->sized_target<size, big_endian>());
 	unsigned int indx = this->stub_table_index_[shndx];
-	gold_assert(indx < target->stub_tables().size());
-	return target->stub_tables()[indx];
+	if (indx < target->stub_tables().size())
+	  return target->stub_tables()[indx];
       }
     return NULL;
   }
@@ -342,6 +399,14 @@
   // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
   unsigned int special_;
 
+  // For 64-bit the .rela.toc and .toc section shdnx.
+  unsigned int relatoc_;
+  unsigned int toc_;
+
+  // For 64-bit, an array with one entry per 64-bit word in the .toc
+  // section, set if accesses using that word cannot be optimised.
+  std::vector<bool> no_toc_opt_;
+
   // For 64-bit, whether this object uses small model relocs to access
   // the toc.
   bool has_small_toc_reloc_;
@@ -493,6 +558,23 @@
   elfcpp::Elf_Word e_flags_;
 };
 
+// Powerpc_copy_relocs class.  Needed to peek at dynamic relocs the
+// base class will emit.
+
+template<int sh_type, int size, bool big_endian>
+class Powerpc_copy_relocs : public Copy_relocs<sh_type, size, big_endian>
+{
+ public:
+  Powerpc_copy_relocs()
+    : Copy_relocs<sh_type, size, big_endian>(elfcpp::R_POWERPC_COPY)
+  { }
+
+  // Emit any saved relocations which turn out to be needed.  This is
+  // called after all the relocs have been scanned.
+  void
+  emit(Output_data_reloc<sh_type, true, size, big_endian>*);
+};
+
 template<int size, bool big_endian>
 class Target_powerpc : public Sized_target<size, big_endian>
 {
@@ -509,7 +591,7 @@
   Target_powerpc()
     : Sized_target<size, big_endian>(&powerpc_info),
       got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
-      glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY),
+      glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
       tlsld_got_offset_(-1U),
       stub_tables_(), branch_lookup_table_(), branch_info_(),
       plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0),
@@ -1310,7 +1392,7 @@
   // The dynamic reloc section.
   Reloc_section* rela_dyn_;
   // Relocs saved to avoid a COPY reloc.
-  Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
+  Powerpc_copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
   // Offset of the GOT entry for local dynamic __tls_get_addr calls.
   unsigned int tlsld_got_offset_;
 
@@ -1768,8 +1850,8 @@
     }
 }
 
-// Stash away the index of .got2 or .opd in a relocatable object, if
-// such a section exists.
+// Stash away the index of .got2, .opd, .rela.toc, and .toc in a
+// relocatable object, if such sections exists.
 
 template<int size, bool big_endian>
 bool
@@ -1798,6 +1880,18 @@
 		       this->name().c_str(), this->abiversion());
 	}
     }
+  if (size == 64)
+    {
+      s = this->template find_shdr<size, big_endian>(pshdrs, ".rela.toc",
+						     names, names_size, NULL);
+      if (s != NULL)
+	{
+	  unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
+	  this->relatoc_ = ndx;
+	  typename elfcpp::Shdr<size, big_endian> shdr(s);
+	  this->toc_ = this->adjust_shndx(shdr.get_sh_info());
+	}
+    }
   return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
 }
 
@@ -1812,10 +1906,8 @@
 {
   if (size == 64)
     {
-      typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
-	Reltype;
-      const int reloc_size
-	= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+      typedef typename elfcpp::Rela<size, big_endian> Reltype;
+      const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
       const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
       Address expected_off = 0;
       bool regular = true;
@@ -1880,6 +1972,60 @@
     }
 }
 
+// Returns true if a code sequence loading the TOC entry at VALUE
+// relative to the TOC pointer can be converted into code calculating
+// a TOC pointer relative offset.
+// If so, the TOC pointer relative offset is stored to VALUE.
+
+template<int size, bool big_endian>
+bool
+Powerpc_relobj<size, big_endian>::make_toc_relative(
+    Target_powerpc<size, big_endian>* target,
+    Address* value)
+{
+  if (size != 64)
+    return false;
+
+  // With -mcmodel=medium code it is quite possible to have
+  // toc-relative relocs referring to objects outside the TOC.
+  // Don't try to look at a non-existent TOC.
+  if (this->toc_shndx() == 0)
+    return false;
+
+  // Convert VALUE back to an address by adding got_base (see below),
+  // then to an offset in the TOC by subtracting the TOC output
+  // section address and the TOC output offset.  Since this TOC output
+  // section and the got output section are one and the same, we can
+  // omit adding and subtracting the output section address.
+  Address off = (*value + this->toc_base_offset()
+		 - this->output_section_offset(this->toc_shndx()));
+  // Is this offset in the TOC?  -mcmodel=medium code may be using
+  // TOC relative access to variables outside the TOC.  Those of
+  // course can't be optimized.  We also don't try to optimize code
+  // that is using a different object's TOC.
+  if (off >= this->section_size(this->toc_shndx()))
+    return false;
+
+  if (this->no_toc_opt(off))
+    return false;
+
+  section_size_type vlen;
+  unsigned char* view = this->get_output_view(this->toc_shndx(), &vlen);
+  Address addr = elfcpp::Swap<size, big_endian>::readval(view + off);
+  // The TOC pointer
+  Address got_base = (target->got_section()->output_section()->address()
+		      + this->toc_base_offset());
+  addr -= got_base;
+  if (addr + (uint64_t) 0x80008000 >= (uint64_t) 1 << 32)
+    return false;
+
+  *value = addr;
+  return true;
+}
+
+// Perform the Sized_relobj_file method, then set up opd info from
+// .opd relocs.
+
 template<int size, bool big_endian>
 void
 Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
@@ -2075,6 +2221,31 @@
     }
 }
 
+// Relocate sections.
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::do_relocate_sections(
+    const Symbol_table* symtab, const Layout* layout,
+    const unsigned char* pshdrs, Output_file* of,
+    typename Sized_relobj_file<size, big_endian>::Views* pviews)
+{
+  unsigned int start = 1;
+  if (size == 64
+      && this->relatoc_ != 0
+      && !parameters->options().relocatable())
+    {
+      // Relocate .toc first.
+      this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+				   this->relatoc_, this->relatoc_);
+      this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+				   1, this->relatoc_ - 1);
+      start = this->relatoc_ + 1;
+    }
+  this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+			       start, this->shnum() - 1);
+}
+
 // Set up some symbols.
 
 template<int size, bool big_endian>
@@ -2435,14 +2606,13 @@
  public:
   // Determine the stub group size.  The group size is the absolute
   // value of the parameter --stub-group-size.  If --stub-group-size
-  // is passed a negative value, we restrict stubs to be always before
+  // is passed a negative value, we restrict stubs to be always after
   // the stubbed branches.
-  Stub_control(int32_t size, bool no_size_errors)
-    : state_(NO_GROUP), stub_group_size_(abs(size)),
-      stub14_group_size_(abs(size) >> 10),
-      stubs_always_before_branch_(size < 0),
-      suppress_size_errors_(no_size_errors),
-      group_end_addr_(0), owner_(NULL), output_section_(NULL)
+  Stub_control(int32_t size, bool no_size_errors, bool multi_os)
+    : stub_group_size_(abs(size)), stubs_always_after_branch_(size < 0),
+      suppress_size_errors_(no_size_errors), multi_os_(multi_os),
+      state_(NO_GROUP), group_size_(0), group_start_addr_(0),
+      owner_(NULL), output_section_(NULL)
   {
   }
 
@@ -2472,31 +2642,40 @@
  private:
   typedef enum
   {
+    // Initial state.
     NO_GROUP,
+    // Adding group sections before the stubs.
     FINDING_STUB_SECTION,
+    // Adding group sections after the stubs.
     HAS_STUB_SECTION
   } State;
 
-  State state_;
   uint32_t stub_group_size_;
-  uint32_t stub14_group_size_;
-  bool stubs_always_before_branch_;
+  bool stubs_always_after_branch_;
   bool suppress_size_errors_;
-  uint64_t group_end_addr_;
+  // True if a stub group can serve multiple output sections.
+  bool multi_os_;
+  State state_;
+  // Current max size of group.  Starts at stub_group_size_ but is
+  // reduced to stub_group_size_/1024 on seeing a section with
+  // external conditional branches.
+  uint32_t group_size_;
+  uint64_t group_start_addr_;
+  // owner_ and output_section_ specify the section to which stubs are
+  // attached.  The stubs are placed at the end of this section.
   const Output_section::Input_section* owner_;
   Output_section* output_section_;
 };
 
 // Return true iff input section can be handled by current stub
-// group.
+// group.  Sections are presented to this function in order,
+// so the first section is the head of the group.
 
 bool
 Stub_control::can_add_to_stub_group(Output_section* o,
 				    const Output_section::Input_section* i,
 				    bool has14)
 {
-  uint32_t group_size
-    = has14 ? this->stub14_group_size_ : this->stub_group_size_;
   bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI;
   uint64_t this_size;
   uint64_t start_addr = o->address();
@@ -2510,46 +2689,88 @@
       start_addr += i->relobj()->output_section_offset(i->shndx());
       this_size = i->data_size();
     }
-  uint64_t end_addr = start_addr + this_size;
-  bool toobig = this_size > group_size;
 
-  if (toobig && !this->suppress_size_errors_)
+  uint64_t end_addr = start_addr + this_size;
+  uint32_t group_size = this->stub_group_size_;
+  if (has14)
+    this->group_size_ = group_size = group_size >> 10;
+
+  if (this_size > group_size && !this->suppress_size_errors_)
     gold_warning(_("%s:%s exceeds group size"),
 		 i->relobj()->name().c_str(),
 		 i->relobj()->section_name(i->shndx()).c_str());
 
-  if (this->state_ != HAS_STUB_SECTION
-      && (!whole_sec || this->output_section_ != o)
-      && (this->state_ == NO_GROUP
-	  || this->group_end_addr_ - end_addr < group_size))
-    {
-      this->owner_ = i;
-      this->output_section_ = o;
-    }
+  gold_debug(DEBUG_TARGET, "maybe add%s %s:%s size=%#llx total=%#llx",
+	     has14 ? " 14bit" : "",
+	     i->relobj()->name().c_str(),
+	     i->relobj()->section_name(i->shndx()).c_str(),
+	     (long long) this_size,
+	     (this->state_ == NO_GROUP
+	      ? this_size
+	      : (long long) end_addr - this->group_start_addr_));
 
   if (this->state_ == NO_GROUP)
     {
+      // Only here on very first use of Stub_control
+      this->owner_ = i;
+      this->output_section_ = o;
       this->state_ = FINDING_STUB_SECTION;
-      this->group_end_addr_ = end_addr;
+      this->group_size_ = group_size;
+      this->group_start_addr_ = start_addr;
+      return true;
     }
-  else if (this->group_end_addr_ - start_addr < group_size)
+  else if (!this->multi_os_ && this->output_section_ != o)
     ;
-  // Adding this section would make the group larger than GROUP_SIZE.
-  else if (this->state_ == FINDING_STUB_SECTION
-	   && !this->stubs_always_before_branch_
-	   && !toobig)
+  else if (this->state_ == HAS_STUB_SECTION)
     {
-      // But wait, there's more!  Input sections up to GROUP_SIZE
-      // bytes before the stub table can be handled by it too.
-      this->state_ = HAS_STUB_SECTION;
-      this->group_end_addr_ = end_addr;
+      // Can we add this section, which is after the stubs, to the
+      // group?
+      if (end_addr - this->group_start_addr_ <= this->group_size_)
+	return true;
+    }
+  else if (this->state_ == FINDING_STUB_SECTION)
+    {
+      if ((whole_sec && this->output_section_ == o)
+	  || end_addr - this->group_start_addr_ <= this->group_size_)
+	{
+	  // Stubs are added at the end of "owner_".
+	  this->owner_ = i;
+	  this->output_section_ = o;
+	  return true;
+	}
+      // The group before the stubs has reached maximum size.
+      // Now see about adding sections after the stubs to the
+      // group.  If the current section has a 14-bit branch and
+      // the group before the stubs exceeds group_size_ (because
+      // they didn't have 14-bit branches), don't add sections
+      // after the stubs:  The size of stubs for such a large
+      // group may exceed the reach of a 14-bit branch.
+      if (!this->stubs_always_after_branch_
+	  && this_size <= this->group_size_
+	  && start_addr - this->group_start_addr_ <= this->group_size_)
+	{
+	  gold_debug(DEBUG_TARGET, "adding after stubs");
+	  this->state_ = HAS_STUB_SECTION;
+	  this->group_start_addr_ = start_addr;
+	  return true;
+	}
     }
   else
-    {
-      this->state_ = NO_GROUP;
-      return false;
-    }
-  return true;
+    gold_unreachable();
+
+  gold_debug(DEBUG_TARGET,
+	     !this->multi_os_ && this->output_section_ != o
+	     ? "nope, new output section\n"
+	     : "nope, didn't fit\n");
+
+  // The section fails to fit in the current group.  Set up a few
+  // things for the next group.  owner_ and output_section_ will be
+  // set later after we've retrieved those values for the current
+  // group.
+  this->state_ = FINDING_STUB_SECTION;
+  this->group_size_ = group_size;
+  this->group_start_addr_ = start_addr;
+  return false;
 }
 
 // Look over all the input sections, deciding where to place stubs.
@@ -2560,7 +2781,8 @@
 						 const Task*,
 						 bool no_size_errors)
 {
-  Stub_control stub_control(this->stub_group_size_, no_size_errors);
+  Stub_control stub_control(this->stub_group_size_, no_size_errors,
+			    parameters->options().stub_group_multi());
 
   // Group input sections and insert stub table
   Stub_table_owner* table_owner = NULL;
@@ -2568,14 +2790,14 @@
   Layout::Section_list section_list;
   layout->get_executable_sections(&section_list);
   std::stable_sort(section_list.begin(), section_list.end(), Sort_sections());
-  for (Layout::Section_list::reverse_iterator o = section_list.rbegin();
-       o != section_list.rend();
+  for (Layout::Section_list::iterator o = section_list.begin();
+       o != section_list.end();
        ++o)
     {
       typedef Output_section::Input_section_list Input_section_list;
-      for (Input_section_list::const_reverse_iterator i
-	     = (*o)->input_sections().rbegin();
-	   i != (*o)->input_sections().rend();
+      for (Input_section_list::const_iterator i
+	     = (*o)->input_sections().begin();
+	   i != (*o)->input_sections().end();
 	   ++i)
 	{
 	  if (i->is_input_section()
@@ -2602,26 +2824,8 @@
     }
   if (table_owner != NULL)
     {
-      const Output_section::Input_section* i = stub_control.owner();
-
-      if (tables.size() >= 2 && tables[tables.size() - 2]->owner == i)
-	{
-	  // Corner case.  A new stub group was made for the first
-	  // section (last one looked at here) for some reason, but
-	  // the first section is already being used as the owner for
-	  // a stub table for following sections.  Force it into that
-	  // stub group.
-	  tables.pop_back();
-	  delete table_owner;
-	  Powerpc_relobj<size, big_endian>* ppcobj = static_cast
-	    <Powerpc_relobj<size, big_endian>*>(i->relobj());
-	  ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
-	}
-      else
-	{
-	  table_owner->output_section = stub_control.output_section();
-	  table_owner->owner = i;
-	}
+      table_owner->output_section = stub_control.output_section();
+      table_owner->owner = stub_control.owner();;
     }
   for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
        t != tables.end();
@@ -2673,6 +2877,8 @@
   Target_powerpc<size, big_endian>* target =
     static_cast<Target_powerpc<size, big_endian>*>(
       parameters->sized_target<size, big_endian>());
+  bool ok = true;
+
   if (gsym != NULL
       ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
       : this->object_->local_has_plt_offset(this->r_sym_))
@@ -2698,13 +2904,13 @@
 	    from += (this->object_->output_section(this->shndx_)->address()
 		     + this->offset_);
 	  if (gsym != NULL)
-	    return stub_table->add_plt_call_entry(from,
-						  this->object_, gsym,
-						  this->r_type_, this->addend_);
+	    ok = stub_table->add_plt_call_entry(from,
+						this->object_, gsym,
+						this->r_type_, this->addend_);
 	  else
-	    return stub_table->add_plt_call_entry(from,
-						  this->object_, this->r_sym_,
-						  this->r_type_, this->addend_);
+	    ok = stub_table->add_plt_call_entry(from,
+						this->object_, this->r_sym_,
+						this->r_type_, this->addend_);
 	}
     }
   else
@@ -2752,6 +2958,8 @@
 	  const Symbol_value<size>* psymval
 	    = this->object_->local_symbol(this->r_sym_);
 	  Symbol_value<size> symval;
+	  if (psymval->is_section_symbol())
+	    symval.set_is_section_symbol();
 	  typedef Sized_relobj_file<size, big_endian> ObjType;
 	  typename ObjType::Compute_final_local_value_status status
 	    = this->object_->compute_final_local_value(this->r_sym_, psymval,
@@ -2789,12 +2997,22 @@
 			   && gsym != NULL
 			   && gsym->source() == Symbol::IN_OUTPUT_DATA
 			   && gsym->output_data() == target->savres_section());
-	  return stub_table->add_long_branch_entry(this->object_,
-						   this->r_type_,
-						   from, to, save_res);
+	  ok = stub_table->add_long_branch_entry(this->object_,
+						 this->r_type_,
+						 from, to, save_res);
 	}
     }
-  return true;
+  if (!ok)
+    gold_debug(DEBUG_TARGET,
+	       "branch at %s:%s+%#lx\n"
+	       "can't reach stub attached to %s:%s",
+	       this->object_->name().c_str(),
+	       this->object_->section_name(this->shndx_).c_str(),
+	       (unsigned long) this->offset_,
+	       stub_table->relobj()->name().c_str(),
+	       stub_table->relobj()->section_name(stub_table->shndx()).c_str());
+
+  return ok;
 }
 
 // Relaxation hook.  This is where we do stub generation.
@@ -2887,7 +3105,7 @@
 	}
       this->stub_tables_.clear();
       this->stub_group_size_ = this->stub_group_size_ / 4 * 3;
-      gold_info(_("%s: stub group size is too large; retrying with %d"),
+      gold_info(_("%s: stub group size is too large; retrying with %#x"),
 		program_name, this->stub_group_size_);
       this->group_sections(layout, task, true);
     }
@@ -2982,7 +3200,13 @@
 	      Stub_table<size, big_endian>* stub_table
 		= static_cast<Stub_table<size, big_endian>*>(
 		    i->relaxed_input_section());
-	      off += stub_table->set_address_and_size(os, off);
+	      Address stub_table_size = stub_table->set_address_and_size(os, off);
+	      off += stub_table_size;
+	      // After a few iterations, set current stub table size
+	      // as min size threshold, so later stub tables can only
+	      // grow in size.
+	      if (pass >= 4)
+		stub_table->set_min_size_threshold(stub_table_size);
 	    }
 	  else
 	    off += i->data_size();
@@ -3366,6 +3590,9 @@
 				       ? ORDER_SMALL_DATA
 				       : ORDER_SMALL_BSS),
 				      false);
+
+      Output_section* rela_plt_os = plt_rel->output_section();
+      rela_plt_os->set_info_section(this->plt_->output_section());
     }
 }
 
@@ -3381,11 +3608,13 @@
       this->make_plt_section(symtab, layout);
 
       Reloc_section* iplt_rel = new Reloc_section(false);
-      this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
+      if (this->rela_dyn_->output_section())
+	this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
       this->iplt_
 	= new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
 							"** IPLT");
-      this->plt_->output_section()->add_output_section_data(this->iplt_);
+      if (this->plt_->output_section())
+	this->plt_->output_section()->add_output_section_data(this->iplt_);
     }
 }
 
@@ -3436,8 +3665,7 @@
     os->set_section_offsets_need_adjustment();
     if (this->rel_ != NULL)
       {
-	unsigned int reloc_size
-	  = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+	const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
 	this->rel_->reset_address_and_file_offset();
 	this->rel_->set_current_data_size(num_branches * reloc_size);
 	this->rel_->finalize_data_size();
@@ -3482,14 +3710,16 @@
 	{
 	  // When PIC we can't fill in .branch_lt (like .plt it can be
 	  // a bss style section) but must initialise at runtime via
-	  // dynamic relocats.
+	  // dynamic relocations.
 	  this->rela_dyn_section(layout);
 	  brlt_rel = new Reloc_section(false);
-	  this->rela_dyn_->output_section()->add_output_section_data(brlt_rel);
+	  if (this->rela_dyn_->output_section())
+	    this->rela_dyn_->output_section()
+	      ->add_output_section_data(brlt_rel);
 	}
       this->brlt_section_
 	= new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel);
-      if (this->plt_ && is_pic)
+      if (this->plt_ && is_pic && this->plt_->output_section())
 	this->plt_->output_section()
 	  ->add_output_section_data(this->brlt_section_);
       else
@@ -3634,8 +3864,8 @@
       targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
       orig_data_size_(owner->current_data_size()),
       plt_size_(0), last_plt_size_(0),
-      branch_size_(0), last_branch_size_(0), eh_frame_added_(false),
-      need_save_res_(false)
+      branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
+      eh_frame_added_(false), need_save_res_(false)
   {
     this->set_output_section(output_section);
 
@@ -3726,6 +3956,11 @@
       off = align_address(off, this->stub_align());
     // Include original section size and alignment padding in size
     my_size += off - start_off;
+    // Ensure new size is always larger than min size
+    // threshold. Alignment requirement is included in "my_size", so
+    // increase "my_size" does not invalidate alignment.
+    if (my_size < this->min_size_threshold_)
+      my_size = this->min_size_threshold_;
     this->reset_address_and_file_offset();
     this->set_current_data_size(my_size);
     this->set_address_and_file_offset(os->address() + start_off,
@@ -3751,6 +3986,9 @@
   plt_size() const
   { return this->plt_size_; }
 
+  void set_min_size_threshold(Address min_size)
+  { this->min_size_threshold_ = min_size; }
+
   bool
   size_update()
   {
@@ -4015,6 +4253,13 @@
   section_size_type orig_data_size_;
   // size of stubs
   section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+  // Some rare cases cause (PR/20529) fluctuation in stub table
+  // size, which leads to an endless relax loop. This is to be fixed
+  // by, after the first few iterations, allowing only increase of
+  // stub table size. This variable sets the minimal possible size of
+  // a stub table, it is zero for the first few iterations, then
+  // increases monotonically.
+  Address min_size_threshold_;
   // Whether .eh_frame info has been created for this stub section.
   bool eh_frame_added_;
   // Set if this stub group needs a copy of out-of-line register
@@ -5545,6 +5790,45 @@
   return false;
 }
 
+// Return TRUE iff INSN is one we expect on a _LO variety toc/got
+// reloc.
+
+static bool
+ok_lo_toc_insn(uint32_t insn, unsigned int r_type)
+{
+  return ((insn & (0x3f << 26)) == 12u << 26 /* addic */
+	  || (insn & (0x3f << 26)) == 14u << 26 /* addi */
+	  || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+	  || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+	  || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+	  || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+	  || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+	  || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+	  || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+	  || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+	  || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+	  || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+	  || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+	  || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+	  || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+	  || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */
+	  || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+	      /* Exclude lfqu by testing reloc.  If relocs are ever
+		 defined for the reduced D field in psq_lu then those
+		 will need testing too.  */
+	      && r_type != elfcpp::R_PPC64_TOC16_LO
+	      && r_type != elfcpp::R_POWERPC_GOT16_LO)
+	  || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */
+	      && (insn & 1) == 0)
+	  || (insn & (0x3f << 26)) == 60u << 26 /* stfq */
+	  || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+	      /* Exclude stfqu.  psq_stu as above for psq_lu.  */
+	      && r_type != elfcpp::R_PPC64_TOC16_LO
+	      && r_type != elfcpp::R_POWERPC_GOT16_LO)
+	  || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */
+	      && (insn & 1) == 0));
+}
+
 // Scan a relocation for a local symbol.
 
 template<int size, bool big_endian>
@@ -5708,9 +5992,11 @@
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
       if (!is_ifunc)
-	target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			    r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			    reloc.get_r_addend());
+	{
+	  unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+	  target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+			      r_type, r_sym, reloc.get_r_addend());
+	}
       break;
 
     case elfcpp::R_PPC64_REL64:
@@ -5898,6 +6184,150 @@
       break;
     }
 
+  if (size == 64
+      && parameters->options().toc_optimize())
+    {
+      if (data_shndx == ppc_object->toc_shndx())
+	{
+	  bool ok = true;
+	  if (r_type != elfcpp::R_PPC64_ADDR64
+	      || (is_ifunc && target->abiversion() < 2))
+	    ok = false;
+	  else if (parameters->options().output_is_position_independent())
+	    {
+	      if (is_ifunc)
+		ok = false;
+	      else
+		{
+		  unsigned int shndx = lsym.get_st_shndx();
+		  if (shndx >= elfcpp::SHN_LORESERVE
+		      && shndx != elfcpp::SHN_XINDEX)
+		    ok = false;
+		}
+	    }
+	  if (!ok)
+	    ppc_object->set_no_toc_opt(reloc.get_r_offset());
+	}
+
+      enum {no_check, check_lo, check_ha} insn_check;
+      switch (r_type)
+	{
+	default:
+	  insn_check = no_check;
+	  break;
+
+	case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+	case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+	case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+	case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+	case elfcpp::R_POWERPC_GOT16_HA:
+	case elfcpp::R_PPC64_TOC16_HA:
+	  insn_check = check_ha;
+	  break;
+
+	case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+	case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+	case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+	case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+	case elfcpp::R_POWERPC_GOT16_LO:
+	case elfcpp::R_PPC64_GOT16_LO_DS:
+	case elfcpp::R_PPC64_TOC16_LO:
+	case elfcpp::R_PPC64_TOC16_LO_DS:
+	  insn_check = check_lo;
+	  break;
+	}
+
+      section_size_type slen;
+      const unsigned char* view = NULL;
+      if (insn_check != no_check)
+	{
+	  view = ppc_object->section_contents(data_shndx, &slen, false);
+	  section_size_type off =
+	    convert_to_section_size_type(reloc.get_r_offset()) & -4;
+	  if (off < slen)
+	    {
+	      uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+	      if (insn_check == check_lo
+		  ? !ok_lo_toc_insn(insn, r_type)
+		  : ((insn & ((0x3f << 26) | 0x1f << 16))
+		     != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+		{
+		  ppc_object->set_no_toc_opt();
+		  gold_warning(_("%s: toc optimization is not supported "
+				 "for %#08x instruction"),
+			       ppc_object->name().c_str(), insn);
+		}
+	    }
+	}
+
+      switch (r_type)
+	{
+	default:
+	  break;
+	case elfcpp::R_PPC64_TOC16:
+	case elfcpp::R_PPC64_TOC16_LO:
+	case elfcpp::R_PPC64_TOC16_HI:
+	case elfcpp::R_PPC64_TOC16_HA:
+	case elfcpp::R_PPC64_TOC16_DS:
+	case elfcpp::R_PPC64_TOC16_LO_DS:
+	  unsigned int shndx = lsym.get_st_shndx();
+	  unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+	  bool is_ordinary;
+	  shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+	  if (is_ordinary && shndx == ppc_object->toc_shndx())
+	    {
+	      Address dst_off = lsym.get_st_value() + reloc.get_r_offset();
+	      if (dst_off < ppc_object->section_size(shndx))
+		{
+		  bool ok = false;
+		  if (r_type == elfcpp::R_PPC64_TOC16_HA)
+		    ok = true;
+		  else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS)
+		    {
+		      // Need to check that the insn is a ld
+		      if (!view)
+			view = ppc_object->section_contents(data_shndx,
+							    &slen,
+							    false);
+		      section_size_type off =
+			(convert_to_section_size_type(reloc.get_r_offset())
+			 + (big_endian ? -2 : 3));
+		      if (off < slen
+			  && (view[off] & (0x3f << 2)) == 58u << 2)
+			ok = true;
+		    }
+		  if (!ok)
+		    ppc_object->set_no_toc_opt(dst_off);
+		}
+	    }
+	  break;
+	}
+    }
+
+  if (size == 32)
+    {
+      switch (r_type)
+	{
+	case elfcpp::R_POWERPC_REL32:
+	  if (ppc_object->got2_shndx() != 0
+	      && parameters->options().output_is_position_independent())
+	    {
+	      unsigned int shndx = lsym.get_st_shndx();
+	      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+	      bool is_ordinary;
+	      shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+	      if (is_ordinary && shndx == ppc_object->got2_shndx()
+		  && (ppc_object->section_flags(data_shndx)
+		      & elfcpp::SHF_EXECINSTR) != 0)
+		gold_error(_("%s: unsupported -mbss-plt code"),
+			   ppc_object->name().c_str());
+	    }
+	  break;
+	default:
+	  break;
+	}
+    }
+
   switch (r_type)
     {
     case elfcpp::R_POWERPC_GOT_TLSLD16:
@@ -5971,9 +6401,9 @@
   bool pushed_ifunc = false;
   if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true))
     {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			  reloc.get_r_addend());
+			  r_type, r_sym, reloc.get_r_addend());
       target->make_plt_entry(symtab, layout, gsym);
       pushed_ifunc = true;
     }
@@ -6063,9 +6493,9 @@
 	      }
 	    if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt))
 	      {
+		unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
 		target->push_branch(ppc_object, data_shndx,
-				    reloc.get_r_offset(), r_type,
-				    elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+				    reloc.get_r_offset(), r_type, r_sym,
 				    reloc.get_r_addend());
 		target->make_plt_entry(symtab, layout, gsym);
 	      }
@@ -6111,6 +6541,11 @@
 				     object, data_shndx,
 				     reloc.get_r_offset(),
 				     reloc.get_r_addend());
+
+		if (size == 64
+		    && parameters->options().toc_optimize()
+		    && data_shndx == ppc_object->toc_shndx())
+		  ppc_object->set_no_toc_opt(reloc.get_r_offset());
 	      }
 	  }
       }
@@ -6120,10 +6555,9 @@
     case elfcpp::R_POWERPC_REL24:
       if (!is_ifunc)
 	{
+	  unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
 	  target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			      r_type,
-			      elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			      reloc.get_r_addend());
+			      r_type, r_sym, reloc.get_r_addend());
 	  if (gsym->needs_plt_entry()
 	      || (!gsym->final_value_is_known()
 		  && (gsym->is_undefined()
@@ -6161,9 +6595,11 @@
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
       if (!is_ifunc)
-	target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			    r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			    reloc.get_r_addend());
+	{
+	  unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+	  target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+			      r_type, r_sym, reloc.get_r_addend());
+	}
       break;
 
     case elfcpp::R_POWERPC_REL16:
@@ -6398,6 +6834,136 @@
       break;
     }
 
+  if (size == 64
+      && parameters->options().toc_optimize())
+    {
+      if (data_shndx == ppc_object->toc_shndx())
+	{
+	  bool ok = true;
+	  if (r_type != elfcpp::R_PPC64_ADDR64
+	      || (is_ifunc && target->abiversion() < 2))
+	    ok = false;
+	  else if (parameters->options().output_is_position_independent()
+		   && (is_ifunc || gsym->is_absolute() || gsym->is_undefined()))
+	    ok = false;
+	  if (!ok)
+	    ppc_object->set_no_toc_opt(reloc.get_r_offset());
+	}
+
+      enum {no_check, check_lo, check_ha} insn_check;
+      switch (r_type)
+	{
+	default:
+	  insn_check = no_check;
+	  break;
+
+	case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+	case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+	case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+	case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+	case elfcpp::R_POWERPC_GOT16_HA:
+	case elfcpp::R_PPC64_TOC16_HA:
+	  insn_check = check_ha;
+	  break;
+
+	case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+	case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+	case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+	case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+	case elfcpp::R_POWERPC_GOT16_LO:
+	case elfcpp::R_PPC64_GOT16_LO_DS:
+	case elfcpp::R_PPC64_TOC16_LO:
+	case elfcpp::R_PPC64_TOC16_LO_DS:
+	  insn_check = check_lo;
+	  break;
+	}
+
+      section_size_type slen;
+      const unsigned char* view = NULL;
+      if (insn_check != no_check)
+	{
+	  view = ppc_object->section_contents(data_shndx, &slen, false);
+	  section_size_type off =
+	    convert_to_section_size_type(reloc.get_r_offset()) & -4;
+	  if (off < slen)
+	    {
+	      uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+	      if (insn_check == check_lo
+		  ? !ok_lo_toc_insn(insn, r_type)
+		  : ((insn & ((0x3f << 26) | 0x1f << 16))
+		     != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+		{
+		  ppc_object->set_no_toc_opt();
+		  gold_warning(_("%s: toc optimization is not supported "
+				 "for %#08x instruction"),
+			       ppc_object->name().c_str(), insn);
+		}
+	    }
+	}
+
+      switch (r_type)
+	{
+	default:
+	  break;
+	case elfcpp::R_PPC64_TOC16:
+	case elfcpp::R_PPC64_TOC16_LO:
+	case elfcpp::R_PPC64_TOC16_HI:
+	case elfcpp::R_PPC64_TOC16_HA:
+	case elfcpp::R_PPC64_TOC16_DS:
+	case elfcpp::R_PPC64_TOC16_LO_DS:
+	  if (gsym->source() == Symbol::FROM_OBJECT
+	      && !gsym->object()->is_dynamic())
+	    {
+	      Powerpc_relobj<size, big_endian>* sym_object
+		= static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
+	      bool is_ordinary;
+	      unsigned int shndx = gsym->shndx(&is_ordinary);
+	      if (shndx == sym_object->toc_shndx())
+		{
+		  Sized_symbol<size>* sym = symtab->get_sized_symbol<size>(gsym);
+		  Address dst_off = sym->value() + reloc.get_r_offset();
+		  if (dst_off < sym_object->section_size(shndx))
+		    {
+		      bool ok = false;
+		      if (r_type == elfcpp::R_PPC64_TOC16_HA)
+			ok = true;
+		      else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS)
+			{
+			  // Need to check that the insn is a ld
+			  if (!view)
+			    view = ppc_object->section_contents(data_shndx,
+								&slen,
+								false);
+			  section_size_type off =
+			    (convert_to_section_size_type(reloc.get_r_offset())
+			     + (big_endian ? -2 : 3));
+			  if (off < slen
+			      && (view[off] & (0x3f << 2)) == (58u << 2))
+			    ok = true;
+			}
+		      if (!ok)
+			sym_object->set_no_toc_opt(dst_off);
+		    }
+		}
+	    }
+	  break;
+	}
+    }
+
+  if (size == 32)
+    {
+      switch (r_type)
+	{
+	case elfcpp::R_PPC_LOCAL24PC:
+	  if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
+	    gold_error(_("%s: unsupported -mbss-plt code"),
+		       ppc_object->name().c_str());
+	  break;
+	default:
+	  break;
+	}
+    }
+
   switch (r_type)
     {
     case elfcpp::R_POWERPC_GOT_TLSLD16:
@@ -6918,31 +7484,40 @@
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
 }
 
-// Return TRUE iff INSN is one we expect on a _LO variety toc/got
-// reloc.
+// Emit any saved relocs, and mark toc entries using any of these
+// relocs as not optimizable.
 
-static bool
-ok_lo_toc_insn(uint32_t insn)
+template<int sh_type, int size, bool big_endian>
+void
+Powerpc_copy_relocs<sh_type, size, big_endian>::emit(
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
 {
-  return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
-	  || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
-	  || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
-	  || (insn & (0x3f << 26)) == 36u << 26 /* stw */
-	  || (insn & (0x3f << 26)) == 38u << 26 /* stb */
-	  || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
-	  || (insn & (0x3f << 26)) == 42u << 26 /* lha */
-	  || (insn & (0x3f << 26)) == 44u << 26 /* sth */
-	  || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
-	  || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
-	  || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
-	  || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
-	  || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
-	  || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
-	  || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
-	      && (insn & 3) != 1)
-	  || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
-	      && ((insn & 3) == 0 || (insn & 3) == 3))
-	  || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+  if (size == 64
+      && parameters->options().toc_optimize())
+    {
+      for (typename Copy_relocs<sh_type, size, big_endian>::
+	     Copy_reloc_entries::iterator p = this->entries_.begin();
+	   p != this->entries_.end();
+	   ++p)
+	{
+	  typename Copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry&
+	    entry = *p;
+
+	  // If the symbol is no longer defined in a dynamic object,
+	  // then we emitted a COPY relocation.  If it is still
+	  // dynamic then we'll need dynamic relocations and thus
+	  // can't optimize toc entries.
+	  if (entry.sym_->is_from_dynobj())
+	    {
+	      Powerpc_relobj<size, big_endian>* ppc_object
+		= static_cast<Powerpc_relobj<size, big_endian>*>(entry.relobj_);
+	      if (entry.shndx_ == ppc_object->toc_shndx())
+		ppc_object->set_no_toc_opt(entry.address_);
+	    }
+	}
+    }
+
+  Copy_relocs<sh_type, size, big_endian>::emit(reloc_section);
 }
 
 // Return the value to use for a branch relocation.
@@ -6964,7 +7539,8 @@
   // descriptor, use the function descriptor code entry address
   Powerpc_relobj<size, big_endian>* symobj = object;
   if (gsym != NULL
-      && gsym->source() != Symbol::FROM_OBJECT)
+      && (gsym->source() != Symbol::FROM_OBJECT
+	  || gsym->object()->is_dynamic()))
     return true;
   if (gsym != NULL)
     symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
@@ -7035,8 +7611,7 @@
 
   typedef Powerpc_relocate_functions<size, big_endian> Reloc;
   typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
-  typedef typename Reloc_types<elfcpp::SHT_RELA,
-			       size, big_endian>::Reloc Reltype;
+  typedef typename elfcpp::Rela<size, big_endian> Reltype;
   // Offset from start of insn to d-field reloc.
   const int d_offset = big_endian ? 2 : 0;
 
@@ -7110,7 +7685,6 @@
 	}
       else
 	{
-	  unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
 	  gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
 	  value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
 	}
@@ -7212,7 +7786,6 @@
 	    }
 	  else
 	    {
-	      unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
 	      gold_assert(object->local_has_got_offset(r_sym, got_type));
 	      value = object->local_got_offset(r_sym, got_type);
 	    }
@@ -7312,7 +7885,6 @@
 	}
       else
 	{
-	  unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
 	  gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL));
 	  value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
 	}
@@ -7335,7 +7907,6 @@
 	    }
 	  else
 	    {
-	      unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
 	      gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL));
 	      value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
 	    }
@@ -7607,14 +8178,24 @@
 
   if (size == 64)
     {
-      // 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;
 
+	  // Multi-instruction sequences that access the GOT/TOC can
+	  // be optimized, eg.
+	  //     addis ra,r2,x@got@ha; ld rb,x@got@l(ra);
+	  // to  addis ra,r2,x@toc@ha; addi rb,ra,x@toc@l;
+	  // and
+	  //     addis ra,r2,0; addi rb,ra,x@toc@l;
+	  // to  nop;           addi rb,r2,x@toc;
+	  // FIXME: the @got sequence shown above is not yet
+	  // optimized.  Note that gcc as of 2017-01-07 doesn't use
+	  // the ELF @got relocs except for TLS, instead using the
+	  // PowerOpen variant of a compiler managed GOT (called TOC).
+	  // The PowerOpen TOC sequence equivalent to the first
+	  // example is optimized.
 	case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
 	case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
 	case elfcpp::R_POWERPC_GOT_TPREL16_HA:
@@ -7625,12 +8206,15 @@
 	    {
 	      Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
 	      Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-	      if ((insn & ((0x3f << 26) | 0x1f << 16))
-		  != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
-		gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-				       _("toc optimization is not supported "
-					 "for %#08x instruction"), insn);
-	      else if (value + 0x8000 < 0x10000)
+	      if (r_type == elfcpp::R_PPC64_TOC16_HA
+		  && object->make_toc_relative(target, &value))
+		{
+		  gold_assert((insn & ((0x3f << 26) | 0x1f << 16))
+			      == ((15u << 26) | (2 << 16)));
+		}
+	      if (((insn & ((0x3f << 26) | 0x1f << 16))
+		   == ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
+		  && value + 0x8000 < 0x10000)
 		{
 		  elfcpp::Swap<32, big_endian>::writeval(iview, nop);
 		  return true;
@@ -7650,11 +8234,17 @@
 	    {
 	      Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
 	      Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-	      if (!ok_lo_toc_insn(insn))
-		gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-				       _("toc optimization is not supported "
-					 "for %#08x instruction"), insn);
-	      else if (value + 0x8000 < 0x10000)
+	      bool changed = false;
+	      if (r_type == elfcpp::R_PPC64_TOC16_LO_DS
+		  && object->make_toc_relative(target, &value))
+		{
+		  gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */);
+		  insn ^= (14u << 26) ^ (58u << 26);
+		  r_type = elfcpp::R_PPC64_TOC16_LO;
+		  changed = true;
+		}
+	      if (ok_lo_toc_insn(insn, r_type)
+		  && value + 0x8000 < 0x10000)
 		{
 		  if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
 		    {
@@ -7667,8 +8257,10 @@
 		      insn &= ~(0x1f << 16);
 		      insn |= 2 << 16;
 		    }
-		  elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+		  changed = true;
 		}
+	      if (changed)
+		elfcpp::Swap<32, big_endian>::writeval(iview, insn);
 	    }
 	  break;
 
@@ -7731,8 +8323,7 @@
 	      && gsym != NULL
 	      && strcmp(gsym->name(), ".TOC.") == 0)
 	    {
-	      const int reloc_size
-		= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+	      const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
 	      Reltype prev_rela(preloc - reloc_size);
 	      if ((prev_rela.get_r_info()
 		   == elfcpp::elf_r_info<size>(r_sym,
@@ -8205,10 +8796,8 @@
 class Powerpc_scan_relocatable_reloc
 {
 public:
-  typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
-      Reltype;
-  static const int reloc_size =
-      Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+  typedef typename elfcpp::Rela<size, big_endian> Reltype;
+  static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
   static const int sh_type = elfcpp::SHT_RELA;
 
   // Return the symbol referred to by the relocation.
@@ -8348,12 +8937,9 @@
 {
   gold_assert(sh_type == elfcpp::SHT_RELA);
 
-  typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
-    Reltype;
-  typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc_write
-    Reltype_write;
-  const int reloc_size
-    = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+  typedef typename elfcpp::Rela<size, big_endian> Reltype;
+  typedef typename elfcpp::Rela_write<size, big_endian> Reltype_write;
+  const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
   // Offset from start of insn to d-field reloc.
   const int d_offset = big_endian ? 2 : 0;
 
diff --git a/gold/reloc.cc b/gold/reloc.cc
index ca54f15..8bbb07d 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -871,7 +871,30 @@
     Output_file* of,
     Views* pviews)
 {
-  unsigned int shnum = this->shnum();
+  this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+			       1, this->shnum() - 1);
+}
+
+// Relocate section data for the range of sections START_SHNDX through
+// END_SHNDX.
+
+template<int size, bool big_endian>
+void
+Sized_relobj_file<size, big_endian>::relocate_section_range(
+    const Symbol_table* symtab,
+    const Layout* layout,
+    const unsigned char* pshdrs,
+    Output_file* of,
+    Views* pviews,
+    unsigned int start_shndx,
+    unsigned int end_shndx)
+{
+  gold_assert(start_shndx >= 1);
+  gold_assert(end_shndx < this->shnum());
+
+  if (end_shndx < start_shndx)
+    return;
+
   Sized_target<size, big_endian>* target =
     parameters->sized_target<size, big_endian>();
 
@@ -883,8 +906,8 @@
   relinfo.layout = layout;
   relinfo.object = this;
 
-  const unsigned char* p = pshdrs + This::shdr_size;
-  for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
+  const unsigned char* p = pshdrs + start_shndx * This::shdr_size;
+  for (unsigned int i = start_shndx; i <= end_shndx; ++i, p += This::shdr_size)
     {
       typename This::Shdr shdr(p);
 
@@ -1718,6 +1741,17 @@
     Views* pviews);
 
 template
+void
+Sized_relobj_file<32, false>::relocate_section_range(
+    const Symbol_table* symtab,
+    const Layout* layout,
+    const unsigned char* pshdrs,
+    Output_file* of,
+    Views* pviews,
+    unsigned int start_shndx,
+    unsigned int end_shndx);
+
+template
 unsigned char*
 Sized_relobj_file<32, false>::do_get_output_view(
     unsigned int shndx,
@@ -1735,6 +1769,17 @@
     Views* pviews);
 
 template
+void
+Sized_relobj_file<32, true>::relocate_section_range(
+    const Symbol_table* symtab,
+    const Layout* layout,
+    const unsigned char* pshdrs,
+    Output_file* of,
+    Views* pviews,
+    unsigned int start_shndx,
+    unsigned int end_shndx);
+
+template
 unsigned char*
 Sized_relobj_file<32, true>::do_get_output_view(
     unsigned int shndx,
@@ -1752,6 +1797,17 @@
     Views* pviews);
 
 template
+void
+Sized_relobj_file<64, false>::relocate_section_range(
+    const Symbol_table* symtab,
+    const Layout* layout,
+    const unsigned char* pshdrs,
+    Output_file* of,
+    Views* pviews,
+    unsigned int start_shndx,
+    unsigned int end_shndx);
+
+template
 unsigned char*
 Sized_relobj_file<64, false>::do_get_output_view(
     unsigned int shndx,
@@ -1769,6 +1825,17 @@
     Views* pviews);
 
 template
+void
+Sized_relobj_file<64, true>::relocate_section_range(
+    const Symbol_table* symtab,
+    const Layout* layout,
+    const unsigned char* pshdrs,
+    Output_file* of,
+    Views* pviews,
+    unsigned int start_shndx,
+    unsigned int end_shndx);
+
+template
 unsigned char*
 Sized_relobj_file<64, true>::do_get_output_view(
     unsigned int shndx,
diff --git a/include/ChangeLog b/include/ChangeLog
index a766ecf..c3d9db7 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,9 @@
+2017-03-09  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-09-26  Alan Modra  <amodra@gmail.com>
+	* elf/ppc.h (Tag_GNU_Power_ABI_FP): Comment on new values.
+
 2016-06-30  Matthew Wahab  <matthew.wahab@arm.com>
 
 	* opcode/arm.h (ARM_ARCH_V8_2a): Add FPU_NEON_EXT_RDMA to the set
diff --git a/include/elf/ppc.h b/include/elf/ppc.h
index f4a6bbd..5f94a34 100644
--- a/include/elf/ppc.h
+++ b/include/elf/ppc.h
@@ -219,11 +219,18 @@
 enum
 {
   /* 0-3 are generic.  */
-  Tag_GNU_Power_ABI_FP = 4, /* Value 1 for hard-float, 2 for
-			       soft-float, 3 for single=precision 
-			       hard-float; 0 for not tagged or not
-			       using any ABIs affected by the
-			       differences.  */
+
+  /* FP ABI, low 2 bits:
+     1 for double precision hard-float,
+     2 for soft-float,
+     3 for single precision hard-float.
+     0 for not tagged or not using any ABIs affected by the differences.
+     Next 2 bits:
+     1 for ibm long double
+     2 for 64-bit long double
+     3 for IEEE long double.
+     0 for not tagged or not using any ABIs affected by the differences.  */
+  Tag_GNU_Power_ABI_FP = 4,
 
   /* Value 1 for general purpose registers only, 2 for AltiVec
      registers, 3 for SPE registers; 0 for not tagged or not using any
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 6cc35c7..9ecd8c1 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,43 @@
+2017-03-09  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-09-26  Alan Modra  <amodra@gmail.com>
+	* testsuite/ld-powerpc/attr-gnu-4-4.s: Delete.
+	* testsuite/ld-powerpc/attr-gnu-4-14.d: Delete.
+	* testsuite/ld-powerpc/attr-gnu-4-24.d: Delete.
+	* testsuite/ld-powerpc/attr-gnu-4-34.d: Delete.
+	* testsuite/ld-powerpc/attr-gnu-4-41.d: Delete.
+	* testsuite/ld-powerpc/attr-gnu-4-32.d: Adjust expected warning.
+	* testsuite/ld-powerpc/attr-gnu-8-23.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-01.d: Adjust expected output.
+	* testsuite/ld-powerpc/attr-gnu-4-02.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-03.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-10.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-11.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-20.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-22.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-4-33.d: Likewise.
+	* testsuite/ld-powerpc/attr-gnu-8-11.d: Likewise.
+	* testsuite/ld-powerpc/powerpc.exp: Don't run deleted tests.
+
+2017-02-21  Alan Modra  <amodra@gmail.com>
+
+	Apply from master
+	2016-12-06  Alan Modra  <amodra@gmail.com>
+	* testsuite/ld-powerpc/tocopt7.s,
+	* testsuite/ld-powerpc/tocopt7.out,
+	* testsuite/ld-powerpc/tocopt7.d: New test.
+	* testsuite/ld-powerpc/tocopt8.s,
+	* testsuite/ld-powerpc/tocopt8.d: New test.
+	* testsuite/ld-powerpc/powerpc.exp: Run them.
+
+2017-02-15  H.J. Lu  <hongjiu.lu@intel.com>
+
+	PR ld/21168
+	* testsuite/ld-i386/i386.exp: Run pr21168.
+	* testsuite/ld-i386/pr21168a.c: New file.
+	* testsuite/ld-i386/pr21168b.S: Likewise.
+
 2016-10-14  Alan Modra  <amodra@gmail.com>
 
 	* scripttempl/DWARF.sc: Add .debug_addr.
diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp
index 9efe406..6eb7299 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -831,6 +831,20 @@
 	    "-fPIC -O2 -g" \
 	    { ifunc-1a.c ifunc-1b.S ifunc-1c.S ifunc-1d.S } \
 	] \
+	[list \
+	    "Build pr21168a.o" \
+	    "" \
+	    "" \
+	    { pr21168a.c } \
+	] \
+	[list \
+	    "Build pr21168.so" \
+	    "-shared" \
+	    "" \
+	    { pr21168b.S } \
+	    "" \
+	    "pr21168.so" \
+	] \
     ]
 
     run_ld_link_exec_tests [] [list \
@@ -852,6 +866,14 @@
 	    "ifunc-1b" \
 	    "pass.out" \
 	] \
+	[list \
+	    "Run pr21168" \
+	    "tmpdir/pr21168a.o tmpdir/pr21168.so" \
+	    "" \
+	    { dummy.c } \
+	    "pr21168" \
+	    "pass.out" \
+	] \
     ]
 }
 
diff --git a/ld/testsuite/ld-i386/pr21168a.c b/ld/testsuite/ld-i386/pr21168a.c
new file mode 100644
index 0000000..a6c0da1
--- /dev/null
+++ b/ld/testsuite/ld-i386/pr21168a.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+int foo = 1;
+
+extern int *bar (void);
+extern int bar_ifunc (void);
+
+int
+main (void)
+{
+  if (bar () == &foo && bar_ifunc () == 0xbadbeef)
+    printf ("PASS\n");
+  return 0;
+}
diff --git a/ld/testsuite/ld-i386/pr21168b.S b/ld/testsuite/ld-i386/pr21168b.S
new file mode 100644
index 0000000..a4ea5c9
--- /dev/null
+++ b/ld/testsuite/ld-i386/pr21168b.S
@@ -0,0 +1,29 @@
+	.text
+	.globl	bar
+	.type	bar, @function
+bar:
+	call	__x86.get_pc_thunk.ax
+	addl	$_GLOBAL_OFFSET_TABLE_, %eax
+	lea	foo@GOT, %ecx
+	mov	(%eax,%ecx,1), %eax
+	ret
+	.globl	bar_ifunc
+	.type	bar_ifunc, @function
+bar_ifunc:
+	call	__x86.get_pc_thunk.ax
+	addl	$_GLOBAL_OFFSET_TABLE_, %eax
+	lea	ifunc@GOT, %ecx
+	mov	(%eax,%ecx,1), %eax
+	ret
+	.type ifunc, @gnu_indirect_function
+ifunc:
+	mov	$0xbadbeef, %eax
+	ret
+	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
+	.globl	__x86.get_pc_thunk.ax
+	.hidden	__x86.get_pc_thunk.ax
+	.type	__x86.get_pc_thunk.ax, @function
+__x86.get_pc_thunk.ax:
+	movl	(%esp), %eax
+	ret
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-01.d b/ld/testsuite/ld-powerpc/attr-gnu-4-01.d
index 212e0c4..62dbec0 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-01.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-01.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Hard float
+  Tag_GNU_Power_ABI_FP: hard float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-02.d b/ld/testsuite/ld-powerpc/attr-gnu-4-02.d
index 9bd42b5..ae270f1 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-02.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-02.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Soft float
+  Tag_GNU_Power_ABI_FP: soft float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-03.d b/ld/testsuite/ld-powerpc/attr-gnu-4-03.d
index 03b0c3c..d79febf 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-03.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-03.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Single-precision hard float
+  Tag_GNU_Power_ABI_FP: single-precision hard float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-10.d b/ld/testsuite/ld-powerpc/attr-gnu-4-10.d
index 93297c2..1e01549 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-10.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-10.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Hard float
+  Tag_GNU_Power_ABI_FP: hard float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-11.d b/ld/testsuite/ld-powerpc/attr-gnu-4-11.d
index fb2b76e..5027b78 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-11.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-11.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Hard float
+  Tag_GNU_Power_ABI_FP: hard float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-14.d b/ld/testsuite/ld-powerpc/attr-gnu-4-14.d
deleted file mode 100644
index 3bb6661..0000000
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-14.d
+++ /dev/null
@@ -1,6 +0,0 @@
-#source: attr-gnu-4-1.s
-#source: attr-gnu-4-4.s
-#as: -a32
-#ld: -r -melf32ppc
-#warning: Warning: .* uses unknown floating point ABI 4
-#target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-20.d b/ld/testsuite/ld-powerpc/attr-gnu-4-20.d
index 3d83893..fca9de6 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-20.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-20.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Soft float
+  Tag_GNU_Power_ABI_FP: soft float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-22.d b/ld/testsuite/ld-powerpc/attr-gnu-4-22.d
index f6bd198..80a209c 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-22.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-22.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Soft float
+  Tag_GNU_Power_ABI_FP: soft float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-24.d b/ld/testsuite/ld-powerpc/attr-gnu-4-24.d
deleted file mode 100644
index fc17f91..0000000
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-24.d
+++ /dev/null
@@ -1,6 +0,0 @@
-#source: attr-gnu-4-2.s
-#source: attr-gnu-4-4.s
-#as: -a32
-#ld: -r -melf32ppc
-#warning: Warning: .* uses unknown floating point ABI 4
-#target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-32.d b/ld/testsuite/ld-powerpc/attr-gnu-4-32.d
index 3b7cb29..924a3af 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-32.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-32.d
@@ -2,5 +2,5 @@
 #source: attr-gnu-4-2.s
 #as: -a32
 #ld: -r -melf32ppc
-#warning: Warning: .* uses soft float, .* uses single-precision hard float
+#warning: Warning: .* uses hard float, .* uses soft float
 #target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-33.d b/ld/testsuite/ld-powerpc/attr-gnu-4-33.d
index 88367ae..6951b42 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-33.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-4-33.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_FP: Single-precision hard float
+  Tag_GNU_Power_ABI_FP: single-precision hard float, unspecified long double
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-34.d b/ld/testsuite/ld-powerpc/attr-gnu-4-34.d
deleted file mode 100644
index 6f6e1fe..0000000
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-34.d
+++ /dev/null
@@ -1,6 +0,0 @@
-#source: attr-gnu-4-3.s
-#source: attr-gnu-4-4.s
-#as: -a32
-#ld: -r -melf32ppc
-#warning: Warning: .* uses unknown floating point ABI 4
-#target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-4.s b/ld/testsuite/ld-powerpc/attr-gnu-4-4.s
deleted file mode 100644
index 3ff129a..0000000
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-4.s
+++ /dev/null
@@ -1 +0,0 @@
-.gnu_attribute 4,4
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-4-41.d b/ld/testsuite/ld-powerpc/attr-gnu-4-41.d
deleted file mode 100644
index b909476..0000000
--- a/ld/testsuite/ld-powerpc/attr-gnu-4-41.d
+++ /dev/null
@@ -1,6 +0,0 @@
-#source: attr-gnu-4-4.s
-#source: attr-gnu-4-1.s
-#as: -a32
-#ld: -r -melf32ppc
-#warning: Warning: .* uses unknown floating point ABI 4
-#target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-8-11.d b/ld/testsuite/ld-powerpc/attr-gnu-8-11.d
index 7e49d4a..06d7e88 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-8-11.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-8-11.d
@@ -7,4 +7,4 @@
 
 Attribute Section: gnu
 File Attributes
-  Tag_GNU_Power_ABI_Vector: Generic
+  Tag_GNU_Power_ABI_Vector: generic
diff --git a/ld/testsuite/ld-powerpc/attr-gnu-8-23.d b/ld/testsuite/ld-powerpc/attr-gnu-8-23.d
index b22e4bd..b442884 100644
--- a/ld/testsuite/ld-powerpc/attr-gnu-8-23.d
+++ b/ld/testsuite/ld-powerpc/attr-gnu-8-23.d
@@ -2,5 +2,5 @@
 #source: attr-gnu-8-3.s
 #as: -a32
 #ld: -r -melf32ppc
-#warning: Warning: .* uses vector ABI "SPE", .* uses "AltiVec"
+#warning: Warning: .* uses AltiVec vector ABI, .* uses SPE vector ABI
 #target: powerpc*-*-*
diff --git a/ld/testsuite/ld-powerpc/powerpc.exp b/ld/testsuite/ld-powerpc/powerpc.exp
index 37e53fd..8cb4b09 100644
--- a/ld/testsuite/ld-powerpc/powerpc.exp
+++ b/ld/testsuite/ld-powerpc/powerpc.exp
@@ -227,6 +227,10 @@
 	{{objdump -s tocopt5.d}} "tocopt5"}
     {"TOC opt6" "-melf64ppc" "" "-a64"  {tocopt6a.s tocopt6b.s tocopt6c.s}
 	{{objdump -d tocopt6.d}} "tocopt6"}
+    {"TOC opt7" "-melf64ppc" "" "-a64 -mpower9"  {tocopt7.s}
+	{{ld tocopt7.out} {objdump -s tocopt7.d}} "tocopt7"}
+    {"TOC opt8" "-melf64ppc" "" "-a64 -mpower9"  {tocopt8.s}
+	{{objdump -s tocopt8.d}} "tocopt8"}
     {"ambig shared v1" "-shared -melf64ppc" "" "-a64" {funv1.s} {} "funv1.so"}
     {"ambig shared v2" "-shared -melf64ppc" "" "-a64" {funv2.s} {} "funv2.so"}
 }
@@ -319,17 +323,13 @@
 run_dump_test "attr-gnu-4-11"
 run_dump_test "attr-gnu-4-12"
 run_dump_test "attr-gnu-4-13"
-run_dump_test "attr-gnu-4-14"
 run_dump_test "attr-gnu-4-20"
 run_dump_test "attr-gnu-4-21"
 run_dump_test "attr-gnu-4-22"
 run_dump_test "attr-gnu-4-23"
-run_dump_test "attr-gnu-4-24"
 run_dump_test "attr-gnu-4-31"
 run_dump_test "attr-gnu-4-32"
 run_dump_test "attr-gnu-4-33"
-run_dump_test "attr-gnu-4-34"
-run_dump_test "attr-gnu-4-41"
 
 run_dump_test "attr-gnu-8-11"
 run_dump_test "attr-gnu-8-23"
diff --git a/ld/testsuite/ld-powerpc/tocopt7.d b/ld/testsuite/ld-powerpc/tocopt7.d
new file mode 100644
index 0000000..7d4638f
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tocopt7.d
@@ -0,0 +1,54 @@
+
+.*:     file format .*
+
+Contents of section \.text:
+ 100000b0 (0000223d|3d220000) (288029e9|e9298028) (0000823c|3c820000) (3080a438|38a48030) .*
+ 100000c0 (0000c5e8|e8c50000) (0000823f|3f820000) (29803ce9|e93c8029) (0000223d|3d220000) .*
+ 100000d0 (108029e9|e9298010) (0000823c|3c820000) (1880a438|38a48018) (0000c5e8|e8c50000) .*
+ 100000e0 (0000823f|3f820000) (11803ce9|e93c8011) (0000823c|3c820000) (3080a438|38a48030) .*
+ 100000f0 (0000c580|80c50000) (0000223d|3d220000) (10802981|81298010) (0000823c|3c820000) .*
+ 10000100 (1880a438|38a48018) (0000c580|80c50000) (0000823f|3f820000) (10803c85|853c8010) .*
+ 10000110 (0000823c|3c820000) (3080a438|38a48030) (0200c5e8|e8c50002) (0000223d|3d220000) .*
+ 10000120 (128029e9|e9298012) (0000823c|3c820000) (1880a438|38a48018) (0200c5e8|e8c50002) .*
+ 10000130 (0000823c|3c820000) (3080a438|38a48030) (0000c5a0|a0c50000) (0000223d|3d220000) .*
+ 10000140 (108029a1|a1298010) (0000823c|3c820000) (1880a438|38a48018) (0000c5a0|a0c50000) .*
+ 10000150 (0000823f|3f820000) (10803ca5|a53c8010) (0000823c|3c820000) (3080a438|38a48030) .*
+ 10000160 (0000c5a8|a8c50000) (0000223d|3d220000) (108029a9|a9298010) (0000823c|3c820000) .*
+ 10000170 (1880a438|38a48018) (0000c5a8|a8c50000) (0000823c|3c820000) (3080a438|38a48030) .*
+ 10000180 (0000c588|88c50000) (0000223d|3d220000) (10802989|89298010) (0000823c|3c820000) .*
+ 10000190 (1880a438|38a48018) (0000c588|88c50000) (0000823f|3f820000) (10803c8d|8d3c8010) .*
+ 100001a0 (0000823c|3c820000) (3080a438|38a48030) (0000c5c0|c0c50000) (0000223d|3d220000) .*
+ 100001b0 (108029c1|c1298010) (0000823c|3c820000) (1880a438|38a48018) (0000c5c0|c0c50000) .*
+ 100001c0 (0000823f|3f820000) (10803cc5|c53c8010) (0000823c|3c820000) (3080a438|38a48030) .*
+ 100001d0 (0000c5c8|c8c50000) (0000223d|3d220000) (108029c9|c9298010) (0000823c|3c820000) .*
+ 100001e0 (1880a438|38a48018) (0000c5c8|c8c50000) (0000823f|3f820000) (10803ccd|cd3c8010) .*
+ 100001f0 (0000823c|3c820000) (3080a438|38a48030) (0100c5f4|f4c50001) (0000223d|3d220000) .*
+ 10000200 (118029f5|f5298011) (0000823c|3c820000) (2080a438|38a48020) (0100c5f4|f4c50001) .*
+ 10000210 (0000823c|3c820000) (3080a438|38a48030) (0200c5e4|e4c50002) (0000223d|3d220000) .*
+ 10000220 (128029e5|e5298012) (0000823c|3c820000) (1880a438|38a48018) (0200c5e4|e4c50002) .*
+ 10000230 (0000823c|3c820000) (3080a438|38a48030) (0300c5e4|e4c50003) (0000223d|3d220000) .*
+ 10000240 (138029e5|e5298013) (0000823c|3c820000) (1880a438|38a48018) (0300c5e4|e4c50003) .*
+ 10000250 (0000223d|3d220000) (108029f9|f9298010) (0000823c|3c820000) (1880a438|38a48018) .*
+ 10000260 (0000c5f8|f8c50000) (0000823f|3f820000) (11803cf9|f93c8011) (0000223d|3d220000) .*
+ 10000270 (10802991|91298010) (0000823c|3c820000) (1880a438|38a48018) (0000c590|90c50000) .*
+ 10000280 (0000823f|3f820000) (10803c95|953c8010) (0000223d|3d220000) (108029b1|b1298010) .*
+ 10000290 (0000823c|3c820000) (1880a438|38a48018) (0000c5b0|b0c50000) (0000823f|3f820000) .*
+ 100002a0 (10803cb5|b53c8010) (0000223d|3d220000) (10802999|99298010) (0000823c|3c820000) .*
+ 100002b0 (1880a438|38a48018) (0000c598|98c50000) (0000823f|3f820000) (10803c9d|9d3c8010) .*
+ 100002c0 (0000223d|3d220000) (108029d1|d1298010) (0000823c|3c820000) (1880a438|38a48018) .*
+ 100002d0 (0000c5d0|d0c50000) (0000823f|3f820000) (10803cd5|d53c8010) (0000223d|3d220000) .*
+ 100002e0 (108029d9|d9298010) (0000823c|3c820000) (1880a438|38a48018) (0000c5d8|d8c50000) .*
+ 100002f0 (0000823f|3f820000) (10803cdd|dd3c8010) (0000223d|3d220000) (158029f5|f5298015) .*
+ 10000300 (0000823c|3c820000) (2080a438|38a48020) (0500c5f4|f4c50005) (0000223d|3d220000) .*
+ 10000310 (128029f5|f5298012) (0000823c|3c820000) (1880a438|38a48018) (0200c5f4|f4c50002) .*
+ 10000320 (0000223d|3d220000) (138029f5|f5298013) (0000823c|3c820000) (1880a438|38a48018) .*
+ 10000330 (0300c5f4|f4c50003) .*
+Contents of section \.got:
+ 10010400 (00840110|00000000) (00000000|10018400) (00000000|00000000) (00000000|00000000) .*
+ 10010410 (58040110|00000000) (00000000|10010458) (60040110|00000000) (00000000|10010460) .*
+ 10010420 (68040110|00000000) (00000000|10010468) (40040110|00000000) (00000000|10010440) .*
+ 10010430 (48040110|00000000) (00000000|10010448) .*
+Contents of section \.sdata:
+ 10010440 (01000000|00000000) (00000000|00000001) (02000000|00000000) (00000000|00000002) .*
+ 10010450 (03000000|00000000) (00000000|00000003) (04000000|00000000) (00000000|00000004) .*
+ 10010460 (05000000|00000000) (00000000|00000005) (06000000|00000000) (00000000|00000006) .*
diff --git a/ld/testsuite/ld-powerpc/tocopt7.out b/ld/testsuite/ld-powerpc/tocopt7.out
new file mode 100644
index 0000000..f74d91d
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tocopt7.out
@@ -0,0 +1,26 @@
+.*
+\(\.text\+0x18\): .*
+.*
+\(\.text\+0x34\): .*
+.*
+\(\.text\+0x5c\): .*
+.*
+\(\.text\+0xa4\): .*
+.*
+\(\.text\+0xec\): .*
+.*
+\(\.text\+0x114\): .*
+.*
+\(\.text\+0x13c\): .*
+.*
+\(\.text\+0x1b8\): .*
+.*
+\(\.text\+0x1d4\): .*
+.*
+\(\.text\+0x1f0\): .*
+.*
+\(\.text\+0x20c\): .*
+.*
+\(\.text\+0x228\): .*
+.*
+\(\.text\+0x244\): .*
diff --git a/ld/testsuite/ld-powerpc/tocopt7.s b/ld/testsuite/ld-powerpc/tocopt7.s
new file mode 100644
index 0000000..dbd8eb1
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tocopt7.s
@@ -0,0 +1,216 @@
+ .section .toc,"aw"
+ .p2align 4
+x4t:
+ .quad x4
+x5t:
+ .quad x5
+x6t:
+ .quad x6
+
+ .section .sdata,"aw"
+ .p2align 4
+x1:
+ .quad 1
+x2:
+ .quad 2
+x3:
+ .quad 3
+x4:
+ .quad 4
+x5:
+ .quad 5
+x6:
+ .quad 6
+
+ .globl _start
+ .text
+_start:
+# no need for got entry, optimise to nop,addi
+# note: ld doesn't yet do got optimisation, so we get nop,ld
+ addis 9,2,x1@got@ha
+ ld 9,x1@got@l(9)
+# must keep got entry, optimise to nop,addi,ld
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ ld 6,0(5)
+# can't optimise due to possibility that r28 isn't dead
+ addis 28,2,x1@got@ha
+ ldu 9,x1@got@l(28)
+# no need for toc entry, optimise to nop,addi
+ addis 9,2,x4t@toc@ha
+ ld 9,x4t@toc@l(9)
+# must keep toc entry, optimise to nop,addi,ld
+# if we had a reloc tying the ld to x5/x5t then we could throw away
+# the toc entry and optimise to nop,nop,addi
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ ld 6,0(5)
+# can't optimise due to possibility that r28 isn't dead
+ addis 28,2,x4t@toc@ha
+ ldu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lwz 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lwz 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lwz 6,0(5)
+ addis 28,2,x4t@toc@ha
+ lwzu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lwa 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lwa 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lwa 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lhz 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lhz 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lhz 6,0(5)
+ addis 28,2,x4t@toc@ha
+ lhzu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lha 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lha 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lha 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lbz 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lbz 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lbz 6,0(5)
+ addis 28,2,x4t@toc@ha
+ lbzu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lfs 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lfs 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lfs 6,0(5)
+ addis 28,2,x4t@toc@ha
+ lfsu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lfd 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lfd 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lfd 6,0(5)
+ addis 28,2,x4t@toc@ha
+ lfdu 9,x4t@toc@l(28)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxv 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lxv 9,x4t@toc@l(9)
+ addis 4,2,x6t@toc@ha
+ addi 5,4,x6t@toc@l
+ lxv 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxsd 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lxsd 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lxsd 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxssp 6,0(5)
+ addis 9,2,x4t@toc@ha
+ lxssp 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lxssp 6,0(5)
+
+ addis 9,2,x4t@toc@ha
+ std 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ std 6,0(5)
+ addis 28,2,x4t@toc@ha
+ stdu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ stw 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stw 6,0(5)
+ addis 28,2,x4t@toc@ha
+ stwu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ sth 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ sth 6,0(5)
+ addis 28,2,x4t@toc@ha
+ sthu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ stb 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stb 6,0(5)
+ addis 28,2,x4t@toc@ha
+ stbu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ stfs 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stfs 6,0(5)
+ addis 28,2,x4t@toc@ha
+ stfsu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ stfd 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stfd 6,0(5)
+ addis 28,2,x4t@toc@ha
+ stfdu 9,x4t@toc@l(28)
+
+ addis 9,2,x4t@toc@ha
+ stxv 9,x4t@toc@l(9)
+ addis 4,2,x6t@toc@ha
+ addi 5,4,x6t@toc@l
+ stxv 6,0(5)
+
+ addis 9,2,x4t@toc@ha
+ stxsd 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stxsd 6,0(5)
+
+ addis 9,2,x4t@toc@ha
+ stxssp 9,x4t@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stxssp 6,0(5)
diff --git a/ld/testsuite/ld-powerpc/tocopt8.d b/ld/testsuite/ld-powerpc/tocopt8.d
new file mode 100644
index 0000000..c9eff5a
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tocopt8.d
@@ -0,0 +1,46 @@
+
+.*:     file format .*
+
+Contents of section \.text:
+ 100000b0 (00000060|60000000) (108022e9|e9228010) (00000060|60000000) (1880a238|38a28018) .*
+ 100000c0 (0000c5e8|e8c50000) (00000060|60000000) (38802239|39228038) (00000060|60000000) .*
+ 100000d0 (0880a238|38a28008) (0000c5e8|e8c50000) (00000060|60000000) (088022e9|e9228008) .*
+ 100000e0 (00000060|60000000) (1880a238|38a28018) (0000c580|80c50000) (00000060|60000000) .*
+ 100000f0 (20802281|81228020) (00000060|60000000) (0880a238|38a28008) (0000c580|80c50000) .*
+ 10000100 (00000060|60000000) (1880a238|38a28018) (0200c5e8|e8c50002) (00000060|60000000) .*
+ 10000110 (228022e9|e9228022) (00000060|60000000) (0880a238|38a28008) (0200c5e8|e8c50002) .*
+ 10000120 (00000060|60000000) (1880a238|38a28018) (0000c5a0|a0c50000) (00000060|60000000) .*
+ 10000130 (208022a1|a1228020) (00000060|60000000) (0880a238|38a28008) (0000c5a0|a0c50000) .*
+ 10000140 (00000060|60000000) (1880a238|38a28018) (0000c5a8|a8c50000) (00000060|60000000) .*
+ 10000150 (208022a9|a9228020) (00000060|60000000) (0880a238|38a28008) (0000c5a8|a8c50000) .*
+ 10000160 (00000060|60000000) (1880a238|38a28018) (0000c588|88c50000) (00000060|60000000) .*
+ 10000170 (20802289|89228020) (00000060|60000000) (0880a238|38a28008) (0000c588|88c50000) .*
+ 10000180 (00000060|60000000) (1880a238|38a28018) (0000c5c0|c0c50000) (00000060|60000000) .*
+ 10000190 (208022c1|c1228020) (00000060|60000000) (0880a238|38a28008) (0000c5c0|c0c50000) .*
+ 100001a0 (00000060|60000000) (1880a238|38a28018) (0000c5c8|c8c50000) (00000060|60000000) .*
+ 100001b0 (208022c9|c9228020) (00000060|60000000) (0880a238|38a28008) (0000c5c8|c8c50000) .*
+ 100001c0 (00000060|60000000) (1880a238|38a28018) (0100c5f4|f4c50001) (00000060|60000000) .*
+ 100001d0 (218022f5|f5228021) (00000060|60000000) (2080a238|38a28020) (0100c5f4|f4c50001) .*
+ 100001e0 (00000060|60000000) (1880a238|38a28018) (0200c5e4|e4c50002) (00000060|60000000) .*
+ 100001f0 (228022e5|e5228022) (00000060|60000000) (0880a238|38a28008) (0200c5e4|e4c50002) .*
+ 10000200 (00000060|60000000) (1880a238|38a28018) (0300c5e4|e4c50003) (00000060|60000000) .*
+ 10000210 (238022e5|e5228023) (00000060|60000000) (0880a238|38a28008) (0300c5e4|e4c50003) .*
+ 10000220 (00000060|60000000) (208022f9|f9228020) (00000060|60000000) (0880a238|38a28008) .*
+ 10000230 (0000c5f8|f8c50000) (00000060|60000000) (20802291|91228020) (00000060|60000000) .*
+ 10000240 (0880a238|38a28008) (0000c590|90c50000) (00000060|60000000) (208022b1|b1228020) .*
+ 10000250 (00000060|60000000) (0880a238|38a28008) (0000c5b0|b0c50000) (00000060|60000000) .*
+ 10000260 (20802299|99228020) (00000060|60000000) (0880a238|38a28008) (0000c598|98c50000) .*
+ 10000270 (00000060|60000000) (208022d1|d1228020) (00000060|60000000) (0880a238|38a28008) .*
+ 10000280 (0000c5d0|d0c50000) (00000060|60000000) (208022d9|d9228020) (00000060|60000000) .*
+ 10000290 (0880a238|38a28008) (0000c5d8|d8c50000) (00000060|60000000) (258022f5|f5228025) .*
+ 100002a0 (00000060|60000000) (2080a238|38a28020) (0500c5f4|f4c50005) (00000060|60000000) .*
+ 100002b0 (228022f5|f5228022) (00000060|60000000) (0880a238|38a28008) (0200c5f4|f4c50002) .*
+ 100002c0 (00000060|60000000) (238022f5|f5228023) (00000060|60000000) (0880a238|38a28008) .*
+ 100002d0 (0300c5f4|f4c50003) .*
+Contents of section \.got:
+ 10010300 (00830110|00000000) (00000000|10018300) (40030110|00000000) (00000000|10010340) .*
+ 10010310 (20030110|00000000) (00000000|10010320) (28030110|00000000) (00000000|10010328) .*
+Contents of section \.sdata:
+ 10010320 (01000000|00000000) (00000000|00000001) (02000000|00000000) (00000000|00000002) .*
+ 10010330 (03000000|00000000) (00000000|00000003) (04000000|00000000) (00000000|00000004) .*
+ 10010340 (05000000|00000000) (00000000|00000005) (06000000|00000000) (00000000|00000006) .*
diff --git a/ld/testsuite/ld-powerpc/tocopt8.s b/ld/testsuite/ld-powerpc/tocopt8.s
new file mode 100644
index 0000000..219de5e
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tocopt8.s
@@ -0,0 +1,188 @@
+ .section .toc,"aw"
+x4t:
+ .quad x4
+x5t:
+ .quad x5
+
+ .section .sdata,"aw"
+ .p2align 4
+x1:
+ .quad 1
+x2:
+ .quad 2
+x3:
+ .quad 3
+x4:
+ .quad 4
+x5:
+ .quad 5
+x6:
+ .quad 6
+
+ .globl _start
+ .text
+_start:
+# no need for got entry, optimise to nop,addi
+# note: ld doesn't yet do got optimisation, so we get nop,ld
+ addis 9,2,x1@got@ha
+ ld 9,x1@got@l(9)
+# must keep got entry, optimise to nop,addi,ld
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ ld 6,0(5)
+# no need for toc entry, optimise to nop,addi
+ addis 9,2,x4t@toc@ha
+ ld 9,x4t@toc@l(9)
+# must keep toc entry, optimise to nop,addi,ld
+# if we had a reloc tying the ld to x5/x5t then we could throw away
+# the toc entry and optimise to nop,nop,addi
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ ld 6,0(5)
+# keep toc entry due to other accesses to x5t, optimise to nop,ld
+ addis 9,2,x5t@toc@ha
+ ld 9,x5t@toc@l(9)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lwz 6,0(5)
+ addis 9,2,x1@toc@ha
+ lwz 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lwz 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lwa 6,0(5)
+ addis 9,2,x1@toc@ha
+ lwa 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lwa 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lhz 6,0(5)
+ addis 9,2,x1@toc@ha
+ lhz 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lhz 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lha 6,0(5)
+ addis 9,2,x1@toc@ha
+ lha 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lha 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lbz 6,0(5)
+ addis 9,2,x1@toc@ha
+ lbz 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lbz 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lfs 6,0(5)
+ addis 9,2,x1@toc@ha
+ lfs 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lfs 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lfd 6,0(5)
+ addis 9,2,x1@toc@ha
+ lfd 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lfd 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxv 6,0(5)
+ addis 9,2,x1@toc@ha
+ lxv 9,x1@toc@l(9)
+ addis 4,2,x1@toc@ha
+ addi 5,4,x1@toc@l
+ lxv 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxsd 6,0(5)
+ addis 9,2,x1@toc@ha
+ lxsd 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lxsd 6,0(5)
+
+ addis 4,2,x2@got@ha
+ addi 5,4,x2@got@l
+ lxssp 6,0(5)
+ addis 9,2,x1@toc@ha
+ lxssp 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ lxssp 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ std 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ std 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stw 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stw 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ sth 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ sth 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stb 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stb 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stfs 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stfs 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stfd 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stfd 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stxv 9,x1@toc@l(9)
+ addis 4,2,x1@toc@ha
+ addi 5,4,x1@toc@l
+ stxv 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stxsd 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stxsd 6,0(5)
+
+ addis 9,2,x1@toc@ha
+ stxssp 9,x1@toc@l(9)
+ addis 4,2,x5t@toc@ha
+ addi 5,4,x5t@toc@l
+ stxssp 6,0(5)
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
index 43841f5..e5917d4 100644
--- a/opcodes/ChangeLog
+++ b/opcodes/ChangeLog
@@ -1,3 +1,21 @@
+2017-03-08  Peter Bergner  <bergner@vnet.ibm.com>
+
+	* ppc-dis.c (ppc_opts) <altivec>: Do not use PPC_OPCODE_ALTIVEC2;
+	<vsx>: Do not use PPC_OPCODE_VSX3;
+
+2017-03-08  Peter Bergner  <bergner@vnet.ibm.com>
+
+	Apply from master.
+	2017-03-08  Peter Bergner  <bergner@vnet.ibm.com>
+	* ppc-opc.c (powerpc_opcodes) <lnia>: New extended mnemonic.
+
+2017-22-28  Peter Bergner <bergner@vnet.ibm.com>
+
+	Apply from master.
+	2017-02-10  Nicholas Piggin  <npiggin@gmail.com>
+
+	* ppc-opc.c (powerpc_opcodes) <scv, rfscv>: New mnemonics.
+
 2016-09-16  Peter Bergner <bergner@vnet.ibm.com>
 
 	Apply from master.
diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c
index 77a2a60..e7a59b3 100644
--- a/opcodes/ppc-dis.c
+++ b/opcodes/ppc-dis.c
@@ -93,7 +93,7 @@
 		| PPC_OPCODE_A2),
     0 },
   { "altivec", PPC_OPCODE_PPC,
-    PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 },
+    PPC_OPCODE_ALTIVEC },
   { "any",     0,
     PPC_OPCODE_ANY },
   { "booke",   PPC_OPCODE_PPC | PPC_OPCODE_BOOKE,
@@ -216,7 +216,7 @@
 		| PPC_OPCODE_E500),
     PPC_OPCODE_VLE },
   { "vsx",     PPC_OPCODE_PPC,
-    PPC_OPCODE_VSX | PPC_OPCODE_VSX3 },
+    PPC_OPCODE_VSX },
   { "htm",     PPC_OPCODE_PPC,
     PPC_OPCODE_HTM },
 };
diff --git a/opcodes/ppc-opc.c b/opcodes/ppc-opc.c
index 7003e0c..5926db0 100644
--- a/opcodes/ppc-opc.c
+++ b/opcodes/ppc-opc.c
@@ -441,7 +441,7 @@
 #define L1 L0 + 1
   { 0x1, 21, insert_l1, extract_l1, 0 },
 
-  /* The LEV field in a POWER SVC form instruction.  */
+  /* The LEV field in a POWER SVC / POWER9 SCV form instruction.  */
 #define SVC_LEV L1 + 1
   { 0x7f, 5, NULL, NULL, 0 },
 
@@ -2487,6 +2487,8 @@
 /* An DX form instruction.  */
 #define DX(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1))
 #define DX_MASK DX (0x3f, 0x1f)
+/* An DX form instruction with the D bits specified.  */
+#define NODX_MASK (DX_MASK | 0x1fffc1)
 
 /* An EVSEL form instruction.  */
 #define EVSEL(op, xop) (OP (op) | (((unsigned long)(xop)) & 0xff) << 3)
@@ -4185,6 +4187,7 @@
 {"bcla",	B(16,1,1),	B_MASK,	     COM,	PPCVLE,		{BO, BI, BDA}},
 
 {"svc",		SC(17,0,0),	SC_MASK,     POWER,	PPCVLE,		{SVC_LEV, FL1, FL2}},
+{"scv",		SC(17,0,1),	SC_MASK,     POWER9,	PPCVLE,		{SVC_LEV}},
 {"svcl",	SC(17,0,1),	SC_MASK,     POWER,	PPCVLE,		{SVC_LEV, FL1, FL2}},
 {"sc",		SC(17,1,0),	SC_MASK,     PPC,	PPCVLE,		{LEV}},
 {"svca",	SC(17,1,0),	SC_MASK,     PWRCOM,	PPCVLE,		{SV}},
@@ -4197,6 +4200,7 @@
 
 {"mcrf",     XL(19,0), XLBB_MASK|(3<<21)|(3<<16), COM,	PPCVLE,		{BF, BFA}},
 
+{"lnia",     DX(19,2),		NODX_MASK,   POWER9,	PPCVLE,		{RT}},
 {"addpcis",  DX(19,2),		DX_MASK,     POWER9,	PPCVLE,		{RT, DXD}},
 {"subpcis",  DX(19,2),		DX_MASK,     POWER9,	PPCVLE,		{RT, NDXD}},
 
@@ -4434,6 +4438,7 @@
 {"rfi",		XL(19,50),	0xffffffff,  COM,	PPCVLE,		{0}},
 {"rfci",	XL(19,51), 0xffffffff, PPC403|BOOKE|PPCE300|PPCA2|PPC476, PPCVLE, {0}},
 
+{"rfscv",	XL(19,82),	0xffffffff,  POWER9,	PPCVLE,		{0}},
 {"rfsvc",	XL(19,82),	0xffffffff,  POWER,	PPCVLE,		{0}},
 
 {"rfgi",	XL(19,102),   0xffffffff, E500MC|PPCA2,	PPCVLE,		{0}},