Extend the allowed bitfield range (ie. that for which

complain_overflow_bitfield doesn't complain) from -2**(n-1)..2**n-1 to
-2**n..2**n.  This might mean that some reloc overflows are no longer
caught, but it solves the address wrap problem for 16-bit relocs
nicely.  In any case, ports that rely on complain_overflow_bitfield
for reloc overflow checking were not getting a very good check
previously.  A bitfield range in a machine instruction is typically
either the signed or unsigned n bit numbers, not the overlap of these
two ranges.
This commit is contained in:
Alan Modra 2000-03-17 02:02:38 +00:00
parent 17f9defb0b
commit d5afc56eb1
2 changed files with 41 additions and 65 deletions

View File

@ -1,3 +1,11 @@
2000-03-17 Alan Modra <alan@linuxcare.com.au>
* reloc.c (bfd_check_overflow): In case complain_overflow_bitfield,
flag an overflow if the bitfield is outside -2**n to 2**n-1. The
allowable range used to be -2**(n-1) to 2**n-1.
* reloc.c (_bfd_relocate_contents): Same here. Also replace
"boolean overflow" with "bfd_reloc_status_type flag".
2000-03-14 Doug Evans <dje@casey.transmeta.com>
* elf32-m32r.c (m32r_elf_lo16_reloc): Rewrite.

View File

@ -540,17 +540,14 @@ bfd_check_overflow (how, bitsize, rightshift, addrsize, relocation)
case complain_overflow_bitfield:
/* Bitfields are sometimes signed, sometimes unsigned. We
overflow if the value has some, but not all, bits set outside
the field, or if it has any bits set outside the field but
the sign bit is not set. */
explicitly allow an address wrap too, which means a bitfield
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;
if ((a & ~ fieldmask) != 0)
{
signmask = (fieldmask >> 1) + 1;
ss = (signmask << rightshift) - 1;
if ((ss | relocation) != ~ (bfd_vma) 0)
flag = bfd_reloc_overflow;
}
ss = a & ~ fieldmask;
if (ss != 0 && ss != (((bfd_vma) -1 >> rightshift) & ~ fieldmask))
flag = bfd_reloc_overflow;
break;
default:
@ -1428,7 +1425,7 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
{
int size;
bfd_vma x = 0;
boolean overflow;
bfd_reloc_status_type flag;
unsigned int rightshift = howto->rightshift;
unsigned int bitpos = howto->bitpos;
@ -1466,7 +1463,7 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
which we don't check for. We must either check at every single
operation, which would be tedious, or we must do the computations
in a type larger than bfd_vma, which would be inefficient. */
overflow = false;
flag = bfd_reloc_ok;
if (howto->complain_on_overflow != complain_overflow_dont)
{
bfd_vma addrmask, fieldmask, signmask, ss;
@ -1492,7 +1489,7 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
signmask = ~ (fieldmask >> 1);
ss = a & signmask;
if (ss != 0 && ss != ((addrmask >> rightshift) & signmask))
overflow = true;
flag = bfd_reloc_overflow;
/* We only need this next bit of code if the sign bit of B
is below the sign bit of A. This would only happen if
@ -1522,7 +1519,7 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
*/
signmask = (fieldmask >> 1) + 1;
if (((~ (a ^ b)) & (a ^ sum)) & signmask)
overflow = true;
flag = bfd_reloc_overflow;
break;
@ -1542,64 +1539,35 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
b = (b & addrmask) >> bitpos;
sum = (a + b) & addrmask;
if ((a | b | sum) & ~ fieldmask)
overflow = true;
flag = bfd_reloc_overflow;
break;
case complain_overflow_bitfield:
/* Much like unsigned, except no trimming with addrmask. In
addition, the sum overflows if there is a carry out of
the bfd_vma, i.e., the sum is less than either input
operand. */
/* Much like the signed check, but for a field one bit
wider, and no trimming 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))
flag = bfd_reloc_overflow;
signmask = ((~ howto->src_mask) >> 1) & howto->src_mask;
if ((b & signmask) != 0)
b -= signmask << 1;
b >>= bitpos;
/* Bitfields are sometimes used for signed numbers; for
example, a 13-bit field sometimes represents values in
0..8191 and sometimes represents values in -4096..4095.
If the field is signed and a is -4095 (0x1001) and b is
-1 (0x1fff), the sum is -4096 (0x1000), but (0x1001 +
0x1fff is 0x3000). It's not clear how to handle this
everywhere, since there is no way to know how many bits
are significant in the relocation, but the original code
assumed that it was fully sign extended, and we will keep
that assumption. */
signmask = (fieldmask >> 1) + 1;
if ((a & ~ fieldmask) != 0)
{
/* Some bits out of the field are set. This might not
be a problem: if this is a signed bitfield, it is OK
if all the high bits are set, including the sign
bit. We'll try setting all but the most significant
bit in the original relocation value: if this is all
ones, we are OK, assuming a signed bitfield. */
ss = (signmask << rightshift) - 1;
if ((ss | relocation) != ~ (bfd_vma) 0)
overflow = true;
a &= fieldmask;
}
/* We just assume (b & ~ fieldmask) == 0. */
/* We explicitly permit wrap around if this relocation
covers the high bit of an address. 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 (howto->bitsize + rightshift
== bfd_arch_bits_per_address (input_bfd))
break;
sum = a + b;
if (sum < a || (sum & ~ fieldmask) != 0)
{
/* There was a carry out, or the field overflowed. Test
for signed operands again. Here the overflow test is
as for complain_overflow_signed. */
if (((~ (a ^ b)) & (a ^ sum)) & signmask)
overflow = true;
}
signmask = fieldmask + 1;
if (((~ (a ^ b)) & (a ^ sum)) & signmask)
flag = bfd_reloc_overflow;
break;
@ -1640,7 +1608,7 @@ _bfd_relocate_contents (howto, input_bfd, relocation, location)
break;
}
return overflow ? bfd_reloc_overflow : bfd_reloc_ok;
return flag;
}
/*