diff options
author | Nick Clifton <nickc@redhat.com> | 2000-03-17 19:35:44 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2000-03-17 19:35:44 +0000 |
commit | 672314026cf2b2b904a1a4ff3b5a38f2c637acbf (patch) | |
tree | ccb7b00c2295999674da28d8e645751dfbcd5626 /gas/config/tc-arm.c | |
parent | ccdaf797f78b0ddf1f63fea1baef497066d1344f (diff) | |
download | gdb-672314026cf2b2b904a1a4ff3b5a38f2c637acbf.zip gdb-672314026cf2b2b904a1a4ff3b5a38f2c637acbf.tar.gz gdb-672314026cf2b2b904a1a4ff3b5a38f2c637acbf.tar.bz2 |
Handle same-section relocations where the destination is at an address >=
0x00400000.
Diffstat (limited to 'gas/config/tc-arm.c')
-rw-r--r-- | gas/config/tc-arm.c | 58 |
1 files changed, 54 insertions, 4 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index f8f43be..55cca9d 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -5560,13 +5560,63 @@ md_apply_fix3 (fixP, val, seg) case BFD_RELOC_ARM_PCREL_BRANCH: newval = md_chars_to_number (buf, INSN_SIZE); + /* Sign-extend a 24-bit number. */ +#define SEXT24(x) ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000) + #ifdef OBJ_ELF if (! target_oabi) - value = fixP->fx_offset; + value = fixP->fx_offset; #endif - value = (value >> 2) & 0x00ffffff; - value = (value + (newval & 0x00ffffff)) & 0x00ffffff; - newval = value | (newval & 0xff000000); + + /* We are going to store value (shifted right by two) in the + instruction, in a 24 bit, signed field. Thus we need to check + that none of the top 8 bits of the shifted value (top 7 bits of + the unshifted, unsigned value) are set, or that they are all set. */ + if ((value & 0xfe000000UL) != 0 + && ((value & 0xfe000000UL) != 0xfe000000UL)) + { +#ifdef OBJ_ELF + /* Normally we would be stuck at this point, since we cannot store + the absolute address that is the destination of the branch in the + 24 bits of the branch instruction. If however, we happen to know + that the destination of the branch is in the same section as the + branch instruciton itself, then we can compute the relocation for + ourselves and not have to bother the linker with it. + + FIXME: The tests for OBJ_ELF and ! target_oabi are only here + because I have not worked out how to do this for OBJ_COFF or + target_oabi. */ + if (! target_oabi + && fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && S_GET_SEGMENT (fixP->fx_addsy) == seg) + { + /* Get pc relative value to go into the branch. */ + value = * val; + + /* Permit a backward branch provided that enough bits are set. + Allow a forwards branch, provided that enough bits are clear. */ + if ((value & 0xfe000000UL) == 0xfe000000UL + || (value & 0xfe000000UL) == 0) + fixP->fx_done = 1; + } + + if (! fixP->fx_done) +#endif + as_bad_where (fixP->fx_file, fixP->fx_line, + _("gas can't handle same-section branch dest >= 0x04000000")); + } + + value >>= 2; + value += SEXT24 (newval); + + if ((value & 0xff000000UL) != 0 + && (fixP->fx_done == 0 + || ((value & 0xff000000UL) != 0xff000000UL))) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("out of range branch")); + + newval = (value & 0x00ffffff) | (newval & 0xff000000); md_number_to_chars (buf, newval, INSN_SIZE); break; |