Relocation overflow handling

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Relocation overflow handling

Alan Modra
This combines code testing for signed and bitfield reloc overflow.  For
the purpose of overflow, a bitfield is now just like a signed field one
bit wider.  In particular, we now always mask off bits outside the
address size, fixing a reported problem with 16-bit relocs when
compiling with a 64-bit bfd.

Now, if some of the coff support didn't use howto->bitsize for it's own
murky purposes we could do without complain_overflow_bitfield entirely
as it would be just a matter of using complain_over_signed and setting
bitsize one larger.  As it happens, the d10v backend did the reverse to
work around peculiarities of the signed overflow checking at one stage.
I've reverted that patch now that it is no longer needed.

        * reloc.c (enum complain_overflow): Correct comments.
        (bfd_check_overflow): Combine complain_overflow_bitfield and
        complain_overflow_signed code.
        (_bfd_relocate_contents): Likewise.
        (bfd_howto_32): Use complain_overflow_dont.
        * elf32-d10v.c (elf_d10v_howto_table): Revert 2002-06-17 change.
        * bfd-in2.h: Regenerate.

Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.142
diff -u -p -r1.142 reloc.c
--- bfd/reloc.c 8 Nov 2005 11:15:12 -0000 1.142
+++ bfd/reloc.c 8 Dec 2005 10:10:52 -0000
@@ -255,11 +255,12 @@ CODE_FRAGMENT
 .  {* Do not complain on overflow.  *}
 .  complain_overflow_dont,
 .
-.  {* Complain if the bitfield overflows, whether it is considered
-.     as signed or unsigned.  *}
+.  {* Complain if the value overflows when considered as a signed
+.     number one bit larger than the field.  ie. A bitfield of N bits
+.     is allowed to represent -2**n to 2**n-1.  *}
 .  complain_overflow_bitfield,
 .
-.  {* Complain if the value overflows when considered as signed
+.  {* Complain if the value overflows when considered as a signed
 .     number.  *}
 .  complain_overflow_signed,
 .
@@ -496,14 +497,14 @@ bfd_check_overflow (enum complain_overfl
   bfd_vma fieldmask, addrmask, signmask, ss, a;
   bfd_reloc_status_type flag = bfd_reloc_ok;
 
-  a = relocation;
-
   /* Note: BITSIZE should always be <= ADDRSIZE, but in case it's not,
      we'll be permissive: extra bits in the field mask will
      automatically extend the address mask for purposes of the
      overflow check.  */
   fieldmask = N_ONES (bitsize);
+  signmask = ~fieldmask;
   addrmask = N_ONES (addrsize) | fieldmask;
+  a = (relocation & addrmask) >> rightshift;;
 
   switch (how)
     {
@@ -513,19 +514,8 @@ bfd_check_overflow (enum complain_overfl
     case complain_overflow_signed:
       /* If any sign bits are set, all sign bits must be set.  That
          is, A must be a valid negative address after shifting.  */
-      a = (a & addrmask) >> rightshift;
       signmask = ~ (fieldmask >> 1);
-      ss = a & signmask;
-      if (ss != 0 && ss != ((addrmask >> rightshift) & signmask))
- flag = bfd_reloc_overflow;
-      break;
-
-    case complain_overflow_unsigned:
-      /* We have an overflow if the address does not fit in the field.  */
-      a = (a & addrmask) >> rightshift;
-      if ((a & ~ fieldmask) != 0)
- flag = bfd_reloc_overflow;
-      break;
+      /* Fall thru */
 
     case complain_overflow_bitfield:
       /* Bitfields are sometimes signed, sometimes unsigned.  We
@@ -533,9 +523,14 @@ bfd_check_overflow (enum complain_overfl
  of n bits is allowed to store -2**n to 2**n-1.  Thus overflow
  if the value has some, but not all, bits set outside the
  field.  */
-      a >>= rightshift;
-      ss = a & ~ fieldmask;
-      if (ss != 0 && ss != (((bfd_vma) -1 >> rightshift) & ~ fieldmask))
+      ss = a & signmask;
+      if (ss != 0 && ss != ((addrmask >> rightshift) & signmask))
+ flag = bfd_reloc_overflow;
+      break;
+
+    case complain_overflow_unsigned:
+      /* We have an overflow if the address does not fit in the field.  */
+      if ((a & signmask) != 0)
  flag = bfd_reloc_overflow;
       break;
 
@@ -1437,19 +1432,26 @@ _bfd_relocate_contents (reloc_howto_type
          the size of an address.  For bitfields, all the bits matter.
          See also bfd_check_overflow.  */
       fieldmask = N_ONES (howto->bitsize);
+      signmask = ~fieldmask;
       addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
-      a = relocation;
-      b = x & howto->src_mask;
+      a = (relocation & addrmask) >> rightshift;
+      b = (x & howto->src_mask & addrmask) >> bitpos;
 
       switch (howto->complain_on_overflow)
  {
  case complain_overflow_signed:
-  a = (a & addrmask) >> rightshift;
-
   /* If any sign bits are set, all sign bits must be set.
      That is, A must be a valid negative address after
      shifting.  */
-  signmask = ~ (fieldmask >> 1);
+  signmask = ~(fieldmask >> 1);
+  /* Fall thru */
+
+ case complain_overflow_bitfield:
+  /* Much like the signed check, but for a field one bit
+     wider.  We allow a bitfield to represent numbers in the
+     range -2**n to 2**n-1, where n is the number of bits in the
+     field.  Note that when bfd_vma is 32 bits, a 32-bit reloc
+     can't overflow, which is exactly what we want.  */
   ss = a & signmask;
   if (ss != 0 && ss != ((addrmask >> rightshift) & signmask))
     flag = bfd_reloc_overflow;
@@ -1460,12 +1462,11 @@ _bfd_relocate_contents (reloc_howto_type
              SRC_MASK has more bits than BITSIZE, we can get into
              trouble; we would need to verify that B is in range, as
              we do for A above.  */
-  signmask = ((~ howto->src_mask) >> 1) & howto->src_mask;
+  ss = ((~howto->src_mask) >> 1) & howto->src_mask;
+  ss >>= bitpos;
 
   /* Set all the bits above the sign bit.  */
-  b = (b ^ signmask) - signmask;
-
-  b = (b & addrmask) >> bitpos;
+  b = (b ^ ss) - ss;
 
   /* Now we can do the addition.  */
   sum = a + b;
@@ -1477,11 +1478,14 @@ _bfd_relocate_contents (reloc_howto_type
              positive inputs.  The test below looks only at the sign
              bits, and it really just
          SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM)
-     */
-  signmask = (fieldmask >> 1) + 1;
-  if (((~ (a ^ b)) & (a ^ sum)) & signmask)
-    flag = bfd_reloc_overflow;
 
+     We mask with addrmask here to explicitly allow an address
+     wrap-around.  The Linux kernel relies on it, and it is
+     the only way to write assembler code which can run when
+     loaded at a location 0x80000000 away from the location at
+     which it is linked.  */
+  if (((~(a ^ b)) & (a ^ sum)) & signmask & addrmask)
+    flag = bfd_reloc_overflow;
   break;
 
  case complain_overflow_unsigned:
@@ -1496,44 +1500,9 @@ _bfd_relocate_contents (reloc_howto_type
              separate test, we can check for this by or-ing in the
              operands when testing for the sum overflowing its final
              field.  */
-  a = (a & addrmask) >> rightshift;
-  b = (b & addrmask) >> bitpos;
   sum = (a + b) & addrmask;
-  if ((a | b | sum) & ~ fieldmask)
-    flag = bfd_reloc_overflow;
-
-  break;
-
- case complain_overflow_bitfield:
-  /* Much like the signed check, but for a field one bit
-     wider, and no trimming inputs with addrmask.  We allow a
-     bitfield to represent numbers in the range -2**n to
-     2**n-1, where n is the number of bits in the field.
-     Note that when bfd_vma is 32 bits, a 32-bit reloc can't
-     overflow, which is exactly what we want.  */
-  a >>= rightshift;
-
-  signmask = ~ fieldmask;
-  ss = a & signmask;
-  if (ss != 0 && ss != (((bfd_vma) -1 >> rightshift) & signmask))
+  if ((a | b | sum) & signmask)
     flag = bfd_reloc_overflow;
-
-  signmask = ((~ howto->src_mask) >> 1) & howto->src_mask;
-  b = (b ^ signmask) - signmask;
-
-  b >>= bitpos;
-
-  sum = a + b;
-
-  /* We mask with addrmask here to explicitly allow an address
-     wrap-around.  The Linux kernel relies on it, and it is
-     the only way to write assembler code which can run when
-     loaded at a location 0x80000000 away from the location at
-     which it is linked.  */
-  signmask = fieldmask + 1;
-  if (((~ (a ^ b)) & (a ^ sum)) & signmask & addrmask)
-    flag = bfd_reloc_overflow;
-
   break;
 
  default:
@@ -4625,7 +4594,7 @@ bfd_reloc_type_lookup (bfd *abfd, bfd_re
 }
 
 static reloc_howto_type bfd_howto_32 =
-HOWTO (0, 00, 2, 32, FALSE, 0, complain_overflow_bitfield, 0, "VRT32", FALSE, 0xffffffff, 0xffffffff, TRUE);
+HOWTO (0, 00, 2, 32, FALSE, 0, complain_overflow_dont, 0, "VRT32", FALSE, 0xffffffff, 0xffffffff, TRUE);
 
 /*
 INTERNAL_FUNCTION
Index: bfd/elf32-d10v.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-d10v.c,v
retrieving revision 1.29
diff -u -p -r1.29 elf32-d10v.c
--- bfd/elf32-d10v.c 1 Jul 2005 11:16:29 -0000 1.29
+++ bfd/elf32-d10v.c 8 Dec 2005 10:10:28 -0000
@@ -50,10 +50,10 @@ static reloc_howto_type elf_d10v_howto_t
   HOWTO (R_D10V_10_PCREL_R, /* Type.  */
  2,                /* Rightshift.  */
  2,                /* Size (0 = byte, 1 = short, 2 = long).  */
- 7,                /* Bitsize.  */
+ 8,                /* Bitsize.  */
  TRUE,         /* PC_relative.  */
  0,                /* Bitpos.  */
- complain_overflow_bitfield, /* Complain_on_overflow.  */
+ complain_overflow_signed, /* Complain_on_overflow.  */
  bfd_elf_generic_reloc, /* Special_function.  */
  "R_D10V_10_PCREL_R", /* Name.  */
  FALSE,         /* Partial_inplace.  */
@@ -65,10 +65,10 @@ static reloc_howto_type elf_d10v_howto_t
   HOWTO (R_D10V_10_PCREL_L, /* Type.  */
  2,                /* Rightshift.  */
  2,                /* Size (0 = byte, 1 = short, 2 = long).  */
- 7,                /* Bitsize.  */
+ 8,                /* Bitsize.  */
  TRUE,         /* PC_relative.  */
  15,                /* Bitpos.  */
- complain_overflow_bitfield, /* Complain_on_overflow.  */
+ complain_overflow_signed, /* Complain_on_overflow.  */
  bfd_elf_generic_reloc, /* Special_function.  */
  "R_D10V_10_PCREL_L", /* Name.  */
  FALSE,         /* Partial_inplace.  */
@@ -110,10 +110,10 @@ static reloc_howto_type elf_d10v_howto_t
   HOWTO (R_D10V_18_PCREL, /* Type.  */
  2, /* Rightshift.  */
  2, /* Size (0 = byte, 1 = short, 2 = long).  */
- 15, /* Bitsize.  */
+ 16, /* Bitsize.  */
  TRUE, /* PC_relative.  */
  0, /* Bitpos.  */
- complain_overflow_bitfield, /* Complain_on_overflow.  */
+ complain_overflow_signed, /* Complain_on_overflow.  */
  bfd_elf_generic_reloc, /* Special_function.  */
  "R_D10V_18_PCREL", /* Name.  */
  FALSE, /* Partial_inplace.  */

--
Alan Modra
IBM OzLabs - Linux Technology Centre