01917d
commit b80eed39e2e813c37cffcb873dc4fdd03381383c
01917d
Author: Alan Modra <amodra@gmail.com>
01917d
Date:   Fri Mar 7 10:14:30 2014 +1030
01917d
01917d
    Better overflow checking for powerpc64 relocations
01917d
    
01917d
    R_PPC64_ADDR16 is used in three contexts:
01917d
    - .short data relocation
01917d
    - 16-bit signed insn fields, eg. addi
01917d
    - 16-bit unsigned insn fields, eg. ori
01917d
    In the first case we want to allow both signed and unsigned 16-bit
01917d
    values, the latter two ought to error if the field exceeds the range
01917d
    of values allowed for 16-bit signed and unsigned integers
01917d
    respectively.  These conflicting requirements meant that ld had to
01917d
    choose the least restrictive overflow checks, and thus it is possible
01917d
    to construct testcases where an addi field overflows but is not
01917d
    reported by ld.  Many relocations dealing with 16-bit insn fields have
01917d
    this problem.  What's more, some relocations that are only ever used
01917d
    for signed fields of instructions woodenly copied the lax overflow
01917d
    checking of R_PPC64_ADDR16.
01917d
    
01917d
    bfd/
01917d
            * elf64-ppc.c (ppc64_elf_howto_raw): Use complain_overflow_signed
01917d
            for R_PPC64_ADDR14, R_PPC64_ADDR14_BRTAKEN, R_PPC64_ADDR14_BRNTAKEN,
01917d
            R_PPC64_SECTOFF, R_PPC64_ADDR16_DS, R_PPC64_SECTOFF_DS,
01917d
            R_PPC64_REL16 entries.  Use complain_overflow_dont for R_PPC64_TOC.
01917d
            (ppc64_elf_relocate_section): Modify overflow test for 16-bit
01917d
            fields in instructions to signed/unsigned according to whether
01917d
            the field takes a signed or unsigned value.
01917d
    gold/
01917d
            * powerpc.cc (Powerpc_relocate_functions::Overflow_check): Add
01917d
            CHECK_UNSIGNED, CHECK_LOW_INSN, CHECK_HIGH_INSN.
01917d
            (Powerpc_relocate_functions::has_overflow_unsigned): New function.
01917d
            (Powerpc_relocate_functions::has_overflow_bitfield,
01917d
            overflowed): Use the above.
01917d
            (Target_powerpc::Relocate::relocate): Correct overflow checking
01917d
            for a number of relocations.  Modify overflow test for 16-bit
01917d
            fields in instructions to signed/unsigned according to whether
01917d
            the field takes a signed or unsigned value.
01917d
01917d
### a/bfd/ChangeLog
01917d
### b/bfd/ChangeLog
01917d
## -1,3 +1,13 @@
01917d
+2014-03-08  Alan Modra  <amodra@gmail.com>
01917d
+
01917d
+	* elf64-ppc.c (ppc64_elf_howto_raw): Use complain_overflow_signed
01917d
+	for R_PPC64_ADDR14, R_PPC64_ADDR14_BRTAKEN, R_PPC64_ADDR14_BRNTAKEN,
01917d
+	R_PPC64_SECTOFF, R_PPC64_ADDR16_DS, R_PPC64_SECTOFF_DS,
01917d
+	R_PPC64_REL16 entries.  Use complain_overflow_dont for R_PPC64_TOC.
01917d
+	(ppc64_elf_relocate_section): Modify overflow test for 16-bit
01917d
+	fields in instructions to signed/unsigned according to whether
01917d
+	the field takes a signed or unsigned value.
01917d
+
01917d
 2014-03-07  Pedro Alves  <palves@redhat.com>
01917d
 
01917d
 	* rs6000-core.c (rs6000coff_core_p): Cast pointers to bfd_vma
01917d
--- a/bfd/elf64-ppc.c
01917d
+++ b/bfd/elf64-ppc.c
01917d
@@ -357,7 +357,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 ppc64_elf_branch_reloc, /* special_function */
01917d
 	 "R_PPC64_ADDR14",	/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -374,7 +374,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 ppc64_elf_brtaken_reloc, /* special_function */
01917d
 	 "R_PPC64_ADDR14_BRTAKEN",/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -391,7 +391,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 ppc64_elf_brtaken_reloc, /* special_function */
01917d
 	 "R_PPC64_ADDR14_BRNTAKEN",/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -632,7 +632,6 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 32,			/* bitsize */
01917d
 	 TRUE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 /* FIXME: Verify.  Was complain_overflow_bitfield.  */
01917d
 	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 bfd_elf_generic_reloc,	/* special_function */
01917d
 	 "R_PPC64_REL32",	/* name */
01917d
@@ -727,7 +726,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 ppc64_elf_sectoff_reloc, /* special_function */
01917d
 	 "R_PPC64_SECTOFF",	/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -1015,7 +1014,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 64,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_dont, /* complain_on_overflow */
01917d
 	 ppc64_elf_toc64_reloc,	/* special_function */
01917d
 	 "R_PPC64_TOC",		/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -1103,7 +1102,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 bfd_elf_generic_reloc,	/* special_function */
01917d
 	 "R_PPC64_ADDR16_DS",	/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -1178,7 +1177,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 FALSE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 ppc64_elf_sectoff_reloc, /* special_function */
01917d
 	 "R_PPC64_SECTOFF_DS",	/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -1950,7 +1949,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
01917d
 	 16,			/* bitsize */
01917d
 	 TRUE,			/* pc_relative */
01917d
 	 0,			/* bitpos */
01917d
-	 complain_overflow_bitfield, /* complain_on_overflow */
01917d
+	 complain_overflow_signed, /* complain_on_overflow */
01917d
 	 bfd_elf_generic_reloc,	/* special_function */
01917d
 	 "R_PPC64_REL16",	/* name */
01917d
 	 FALSE,			/* partial_inplace */
01917d
@@ -12943,6 +12942,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
01917d
       bfd_vma max_br_offset;
01917d
       bfd_vma from;
01917d
       const Elf_Internal_Rela orig_rel = *rel;
01917d
+      reloc_howto_type *howto;
01917d
+      struct reloc_howto_struct alt_howto;
01917d
 
01917d
       r_type = ELF64_R_TYPE (rel->r_info);
01917d
       r_symndx = ELF64_R_SYM (rel->r_info);
01917d
@@ -14507,6 +14508,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
01917d
 	}
01917d
 
01917d
       /* Do any further special processing.  */
01917d
+      howto = ppc64_elf_howto_table[(int) r_type];
01917d
       switch (r_type)
01917d
 	{
01917d
 	default:
01917d
@@ -14581,7 +14583,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
01917d
 	      info->callbacks->einfo
01917d
 		(_("%P: %H: error: %s not a multiple of %u\n"),
01917d
 		 input_bfd, input_section, rel->r_offset,
01917d
-		 ppc64_elf_howto_table[r_type]->name,
01917d
+		 howto->name,
01917d
 		 mask + 1);
01917d
 	      bfd_set_error (bfd_error_bad_value);
01917d
 	      ret = FALSE;
01917d
@@ -14602,23 +14604,45 @@ ppc64_elf_relocate_section (bfd *output_bfd,
01917d
 	  info->callbacks->einfo
01917d
 	    (_("%P: %H: unresolvable %s against `%T'\n"),
01917d
 	     input_bfd, input_section, rel->r_offset,
01917d
-	     ppc64_elf_howto_table[(int) r_type]->name,
01917d
+	     howto->name,
01917d
 	     h->elf.root.root.string);
01917d
 	  ret = FALSE;
01917d
 	}
01917d
 
01917d
-      r = _bfd_final_link_relocate (ppc64_elf_howto_table[(int) r_type],
01917d
-				    input_bfd,
01917d
-				    input_section,
01917d
-				    contents,
01917d
-				    rel->r_offset,
01917d
-				    relocation,
01917d
-				    addend);
01917d
+      /* 16-bit fields in insns mostly have signed values, but a
01917d
+	 few insns have 16-bit unsigned values.  Really, we should
01917d
+	 have different reloc types.  */
01917d
+      if (howto->complain_on_overflow != complain_overflow_dont
01917d
+	  && howto->dst_mask == 0xffff
01917d
+	  && (input_section->flags & SEC_CODE) != 0)
01917d
+	{
01917d
+	  enum complain_overflow complain = complain_overflow_signed;
01917d
+
01917d
+	  insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
01917d
+	  if (howto->rightshift == 0
01917d
+	      ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
01917d
+		 || (insn & (0x3f << 26)) == 24u << 26 /* ori */
01917d
+		 || (insn & (0x3f << 26)) == 26u << 26 /* xori */
01917d
+		 || (insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
01917d
+	      : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
01917d
+		 || (insn & (0x3f << 26)) == 25u << 26 /* oris */
01917d
+		 || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
01917d
+	    complain = complain_overflow_unsigned;
01917d
+	  if (howto->complain_on_overflow != complain)
01917d
+	    {
01917d
+	      alt_howto = *howto;
01917d
+	      alt_howto.complain_on_overflow = complain;
01917d
+	      howto = &alt_howto;
01917d
+	    }
01917d
+	}
01917d
+
01917d
+      r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
01917d
+				    rel->r_offset, relocation, addend);
01917d
 
01917d
       if (r != bfd_reloc_ok)
01917d
 	{
01917d
 	  char *more_info = NULL;
01917d
-	  const char *reloc_name = ppc64_elf_howto_table[r_type]->name;
01917d
+	  const char *reloc_name = howto->name;
01917d
 
01917d
 	  if (reloc_dest != DEST_NORMAL)
01917d
 	    {
01917d
@@ -14638,7 +14662,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
01917d
 		continue;
01917d
 	      if (h != NULL
01917d
 		  && h->elf.root.type == bfd_link_hash_undefweak
01917d
-		  && ppc64_elf_howto_table[r_type]->pc_relative)
01917d
+		  && howto->pc_relative)
01917d
 		{
01917d
 		  /* Assume this is a call protected by other code that
01917d
 		     detects the symbol is undefined.  If this is the case,