(PR14907) Fix HI16S/LO16 relocations when buf[-1] is evaluated.

This commit is contained in:
Nick Clifton
1998-02-10 03:55:57 +00:00
parent dc4e95adcc
commit 6aa32b907b
2 changed files with 133 additions and 77 deletions

View File

@ -1,3 +1,8 @@
Mon Feb 9 19:40:59 1998 Nick Clifton <nickc@cygnus.com>
* elf32-v850.c (v850_elf_store_addend_in_insn): Fix another
LO16/HI16S bug and improve comments about what is going on.
Sat Feb 7 15:27:03 1998 Ian Lance Taylor <ian@cygnus.com> Sat Feb 7 15:27:03 1998 Ian Lance Taylor <ian@cygnus.com>
* configure, aclocal.m4: Rebuild with new libtool. * configure, aclocal.m4: Rebuild with new libtool.

View File

@ -713,107 +713,158 @@ v850_elf_store_addend_in_insn (abfd, r_type, addend, address, replace)
case R_V850_LO16: case R_V850_LO16:
/* Calculate the sum of the value stored in the instruction and the /* Calculate the sum of the value stored in the instruction and the
addend and check for overflow from the low 16 bits into the high addend and check for overflow from the low 16 bits into the high
16 bits. This can occur if the computation sets the 16th bit when 16 bits. The assembler has already done some of this: If the
before it was clear, since the 16th bit will be sign extended into value stored in the instruction has its 15th bit set, then the
the high part, thus reducing its value by one, but since the 16th bit assembler will have added 1 to the value stored in the associated
was originally clear, the previous R_V850_HI16_S relocation will not HI16S reloc. So for example, these relocations:
have added in an additional 1 to the high part to compensate for this
effect. Overflow can also occur if the compuation carries into the
17th bit and it also results in the 16th bit having the same value as
the 16th bit of the original value. What happens is that the
R_V850_HI16_S relocation will have already examined the 16th bit of
the original value and added 1 to the high part if the bit is set.
This compensates for the sign extension of 16th bit of the result of
the computation. But now there is a carry into the 17th bit, and this
has not been allowed for. Note - there is no need to change anything
if a carry occurs, and the 16th bit changes its value, (this can only
happen if the bit was set in the original value, but is clear in the
result of the computation), as the R_V850_HI16_S relocation will have
already added in 1 to the high part for us. Here are some examples:
original value = 0x12345 movhi hi( fred ), r0, r1
addend = 0x01234 movea lo( fred ), r1, r1
-------
0x13579 => R_V850_HI16_S stores 0x0001
R_V850_LO16 stores 0x3579
This is OK.
original value = 0x12345 will store 0 in the value fields for the MOVHI and MOVEA instructions
addend = 0x07000 and addend will be the address of fred, but for these instructions:
-------
0x19345 => R_V850_HI16_S stores 0x0001
R_V850_LO16 stores 0x9345
but the 0x9345 value gets sign movhi hi( fred + 0x123456), r0, r1
extended, so the sum becomes: movea lo( fred + 0x123456), r1, r1
0x00010000 the value stored in the MOVHI instruction will be 0x12 and the value
0xffff9345 stored in the MOVEA instruction will be 0x3456. If however the
---------- instructions were:
0x00009345 which is wrong.
This is the first example of overflow. movhi hi( fred + 0x10ffff), r0, r1
movea lo( fred + 0x10ffff), r1, r1
original value = 0x18888 then the value stored in the MOVHI instruction would be 0x11 (not
addend = 0x08888 0x10) and the value stored in the MOVEA instruction would be 0xffff.
------- Thus (assuming for the moment that the addend is 0), at run time the
0x21110 => R_V850_HI16_S stores 0x0002 (because 16th bit of the original value is set) MOVHI instruction loads 0x110000 into r1, then the MOVEA instruction
R_V850_LO16 stores 0x1110 adds 0xffffffff (sign extension!) producing 0x10ffff. Similarly if
the instructions were:
and the sum is now: movhi hi( fred - 1), r0, r1
movea lo( fred - 1), r1, r1
0x00020000 then 0 is stored in the MOVHI instruction and -1 is stored in the
0x00001110 MOVEA instruction.
----------
0x00021110 which is OK.
original value = 0x1ffff Overflow can occur if the addition of the value stored in the
addend = 0x08888 instruction plus the addend sets the 15th bit (counting from 0) when
------- before it was clear. This is because the 15th bit will be sign
0x28887 => R_V850_HI16_S stores 0x0002 extended into the high part, thus reducing its value by one, but
R_V850_LO16 stores 0x8887 since the 15th bit was originally clear, the previous HI16S
relocation will not have added in an additional 1 to the high part
to compensate for this effect. For example:
and the sum is now: movhi hi( fred + 0x123456), r0, r1
movea lo( fred + 0x123456), r1, r1
0x00020000 The value stored in HI16S reloc is 0x12, the value stored in the LO16
0xffff8887 reloc is 0x3456. If we assume that the address of fred is 0x00007000
---------- then the relocations become:
0x00018887 which is wrong.
This is the second example of overflow. HI16S: 0x0012 + (0x00007000 >> 16) = 0x12
(The 16th bit remains set). LO16: 0x3456 + (0x00007000 & 0xffff) = 0xa456
original value = 0x15555 but when the instructions are executed, the MOVEA instruction's value
addend = 0x0ffff is signed extended, so the sum becomes:
-------
0x25554 => R_V850_HI16_S stores 0x0001
R_V850_LO16 stores 0x5554
the sum is now: 0x00120000
+ 0xffffa456
------------
0x0011a456 but 'fred + 0x123456' = 0x0012a456
0x00010000 Note that if the 15th bit was set in the value stored in the LO16
0x00005554 reloc, then we do not have to do anything:
----------
0x00015554 which is wrong.
This is the other form of the second movhi hi( fred + 0x10ffff), r0, r1
example of overflow. (The 16th bit movea lo( fred + 0x10ffff), r1, r1
remains clear)
HI16S: 0x0011 + (0x00007000 >> 16) = 0x11
LO16: 0xffff + (0x00007000 & 0xffff) = 0x6fff
0x00110000
+ 0x00006fff
------------
0x00116fff = fred + 0x10ffff = 0x7000 + 0x10ffff
Overflow can also occur if the computation carries into the 16th bit
and it also results in the 15th bit having the same value as the 15th
bit of the original value. What happens is that the HI16S reloc
will have already examined the 15th bit of the original value and
added 1 to the high part if the bit is set. This compensates for the
sign extension of 15th bit of the result of the computation. But now
there is a carry into the 16th bit, and this has not been allowed for.
So, for example if fred is at address 0xf000:
movhi hi( fred + 0xffff), r0, r1 [bit 15 of the offset is 1]
movea lo( fred + 0xffff), r1, r1
HI16S: 0x0001 + (0x0000f000 >> 16) = 0x0001
LO16: 0xffff + (0x0000f000 & 0xffff) = 0xefff (carry into bit 16)
0x00010000
+ 0xffffefff
------------
0x0000efff but 'fred + 0xffff' = 0x0001efff
Similarly, if the 15th bit remains clear, but overflow occurs into
the 16th bit then (assuming the address of fred is 0xf000):
movhi hi( fred + 0x7000), r0, r1 [bit 15 of the offset is 0]
movea lo( fred + 0x7000), r1, r1
HI16S: 0x0000 + (0x0000f000 >> 16) = 0x0000
LO16: 0x7000 + (0x0000f000 & 0xffff) = 0x6fff (carry into bit 16)
0x00000000
+ 0x00006fff
------------
0x00006fff but 'fred + 0x7000' = 0x00016fff
Note - there is no need to change anything if a carry occurs, and the
15th bit changes its value from being set to being clear, as the HI16S
reloc will have already added in 1 to the high part for us:
movhi hi( fred + 0xffff), r0, r1 [bit 15 of the offset is 1]
movea lo( fred + 0xffff), r1, r1
HI16S: 0x0001 + (0x00007000 >> 16)
LO16: 0xffff + (0x00007000 & 0xffff) = 0x6fff (carry into bit 16)
0x00010000
+ 0x00006fff (bit 15 not set, so the top half is zero)
------------
0x00016fff which is right (assuming that fred is at 0x7000)
but if the 15th bit goes from being clear to being set, then we must
once again handle overflow:
movhi hi( fred + 0x7000), r0, r1 [bit 15 of the offset is 0]
movea lo( fred + 0x7000), r1, r1
HI16S: 0x0000 + (0x0000ffff >> 16)
LO16: 0x7000 + (0x0000ffff & 0xffff) = 0x6fff (carry into bit 16)
0x00000000
+ 0x00006fff (bit 15 not set, so the top half is zero)
------------
0x00006fff which is wrong (assuming that fred is at 0xffff)
*/ */
{ {
long result; long result;
insn = bfd_get_16 (abfd, address); insn = bfd_get_16 (abfd, address);
result = insn + addend; result = insn + addend;
#define BIT16_SET(x) ((x) & 0x8000) #define BIT15_SET(x) ((x) & 0x8000)
#define OVERFLOWS(a,i) (((a) & 0xffff) + (i) > 0xffff) #define OVERFLOWS(a,i) ((((a) & 0xffff) + (i)) > 0xffff)
if ((BIT16_SET (result) && ! BIT16_SET (addend)) if ((BIT15_SET (result) && ! BIT15_SET (addend))
|| (OVERFLOWS (addend, insn) || (OVERFLOWS (addend, insn)
&& (BIT16_SET (result) == BIT16_SET (addend)))) && ((! BIT15_SET (insn)) || (BIT15_SET (addend)))))
{ {
/* Amend the preceding HI16_S relocation, allowing for /* Amend the preceding HI16_S relocation, allowing for
an intervening instruction, which does occasionally happen. */ an intervening instruction, which does occasionally happen. */