RFC: Unify overflow checking
Daniel Jacobowitz
drow@false.org
Tue Apr 22 18:33:00 GMT 2008
More information about the Binutils mailing list
Tue Apr 22 18:33:00 GMT 2008
- Previous message (by thread): PATCH: Fix two more ARFMAG typos
- Next message (by thread): RFC: Unify overflow checking
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Does anyone remember why bfd_check_overflow and _bfd_relocate_contents have different overflow checks? It looks to me as if the ones in _bfd_relocate_contents are always appropriate, and bfd_check_overflow is simply stale. This (untested so far) patch unifies them. -- Daniel Jacobowitz CodeSourcery 2008-04-22 Daniel Jacobowitz <dan@codesourcery.com> * reloc.c (bfd_check_overflow): Update from _bfd_relocate_contents. (_bfd_relocate_contents): Use bfd_check_overflow. Index: reloc.c =================================================================== RCS file: /cvs/src/src/bfd/reloc.c,v retrieving revision 1.174 diff -u -p -r1.174 reloc.c --- reloc.c 16 Apr 2008 08:51:18 -0000 1.174 +++ reloc.c 22 Apr 2008 14:06:27 -0000 @@ -495,7 +495,7 @@ bfd_check_overflow (enum complain_overfl unsigned int addrsize, bfd_vma relocation) { - bfd_vma fieldmask, addrmask, signmask, ss, a; + bfd_vma fieldmask, addrmask, signmask, ss, a, b, sum; bfd_reloc_status_type flag = bfd_reloc_ok; /* Note: BITSIZE should always be <= ADDRSIZE, but in case it's not, @@ -506,6 +506,7 @@ bfd_check_overflow (enum complain_overfl signmask = ~fieldmask; addrmask = N_ONES (addrsize) | fieldmask; a = (relocation & addrmask) >> rightshift;; + b = (x & howto->src_mask & addrmask) >> bitpos; switch (how) { @@ -527,11 +528,53 @@ bfd_check_overflow (enum complain_overfl ss = a & signmask; if (ss != 0 && ss != ((addrmask >> rightshift) & signmask)) 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 + SRC_MASK had fewer bits than BITSIZE. Note that if + 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. */ + ss = ((~howto->src_mask) >> 1) & howto->src_mask; + ss >>= bitpos; + + /* Set all the bits above the sign bit. */ + b = (b ^ ss) - ss; + + /* Now we can do the addition. */ + sum = a + b; + + /* See if the result has the correct sign. Bits above the + sign bit are junk now; ignore them. If the sum is + positive, make sure we did not have all negative inputs; + if the sum is negative, make sure we did not have all + positive inputs. The test below looks only at the sign + bits, and it really just + SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM) + + 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: - /* We have an overflow if the address does not fit in the field. */ - if ((a & signmask) != 0) + /* Checking for an unsigned overflow is relatively easy: + trim the addresses and add, and trim the result as well. + Overflow is normally indicated when the result does not + fit in the field. However, we also need to consider the + case when, e.g., fieldmask is 0x7fffffff or smaller, an + input is 0x80000000, and bfd_vma is only 32 bits; then we + will get sum == 0, but there is an overflow, since the + inputs did not fit in the field. Instead of doing a + separate test, we can check for this by or-ing in the + operands when testing for the sum overflowing its final + field. */ + sum = (a + b) & addrmask; + if ((a | b | sum) & signmask) flag = bfd_reloc_overflow; break; @@ -1424,92 +1467,10 @@ _bfd_relocate_contents (reloc_howto_type in a type larger than bfd_vma, which would be inefficient. */ flag = bfd_reloc_ok; if (howto->complain_on_overflow != complain_overflow_dont) - { - bfd_vma addrmask, fieldmask, signmask, ss; - bfd_vma a, b, sum; - - /* Get the values to be added together. For signed and unsigned - relocations, we assume that all values should be truncated to - 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 & addrmask) >> rightshift; - b = (x & howto->src_mask & addrmask) >> bitpos; - - switch (howto->complain_on_overflow) - { - 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. */ - 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; - - /* 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 - SRC_MASK had fewer bits than BITSIZE. Note that if - 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. */ - ss = ((~howto->src_mask) >> 1) & howto->src_mask; - ss >>= bitpos; - - /* Set all the bits above the sign bit. */ - b = (b ^ ss) - ss; - - /* Now we can do the addition. */ - sum = a + b; - - /* See if the result has the correct sign. Bits above the - sign bit are junk now; ignore them. If the sum is - positive, make sure we did not have all negative inputs; - if the sum is negative, make sure we did not have all - positive inputs. The test below looks only at the sign - bits, and it really just - SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM) - - 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: - /* Checking for an unsigned overflow is relatively easy: - trim the addresses and add, and trim the result as well. - Overflow is normally indicated when the result does not - fit in the field. However, we also need to consider the - case when, e.g., fieldmask is 0x7fffffff or smaller, an - input is 0x80000000, and bfd_vma is only 32 bits; then we - will get sum == 0, but there is an overflow, since the - inputs did not fit in the field. Instead of doing a - separate test, we can check for this by or-ing in the - operands when testing for the sum overflowing its final - field. */ - sum = (a + b) & addrmask; - if ((a | b | sum) & signmask) - flag = bfd_reloc_overflow; - break; - - default: - abort (); - } - } + flag = bfd_check_overflow (howto->complain_on_overflow, howto->bitsize, + rightshift, + bfd_arch_bits_per_address (input_bfd), + relocation); /* Put RELOCATION in the right bits. */ relocation >>= (bfd_vma) rightshift;
- Previous message (by thread): PATCH: Fix two more ARFMAG typos
- Next message (by thread): RFC: Unify overflow checking
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list