This is the mail archive of the
binutils@sourceware.cygnus.com
mailing list for the binutils project.
Re: arm-elf-as truncates branch offsets w/o warning
- To: grante at visi dot com
- Subject: Re: arm-elf-as truncates branch offsets w/o warning
- From: Nick Clifton <nickc at cygnus dot com>
- Date: Thu, 16 Mar 2000 18:50:25 -0800
- CC: scottb at netwinder dot org, binutils at sourceware dot cygnus dot com
Hi Grant,
OK, here is another patch for you to try out. It is basically your
patch with a little extra code to handle the case where a
pc-relative unrelocated instruction can be generated. ie this code
will handle your first example:
.text
.org 0x05000000
b label1
nop
nop
nop
label1:
nop
[I do not run out of memeory linking this one. But then I have a big
machine!]
But it will not handle your second example, where the destination of
the branch is not known to be in the same section as the source of
the branch. (At least this is not known at assemble time). Instead
an error message will be genreated, ratehr than silently producing
bad code.
The patch will also generate an error for your third case where the
branch definitely is out of range, despite it being within the same
section.
Can you try the patch out and let me know if it works for you.
Cheers
Nick
Index: config/tc-arm.c
===================================================================
RCS file: /cvs/src//src/gas/config/tc-arm.c,v
retrieving revision 1.33
diff -p -r1.33 tc-arm.c
*** tc-arm.c 2000/02/24 19:46:27 1.33
--- tc-arm.c 2000/03/17 02:22:35
*************** md_apply_fix3 (fixP, val, seg)
*** 5560,5571 ****
case BFD_RELOC_ARM_PCREL_BRANCH:
newval = md_chars_to_number (buf, INSN_SIZE);
#ifdef OBJ_ELF
if (! target_oabi)
! value = fixP->fx_offset;
#endif
! value = (value >> 2) & 0x00ffffff;
! value = (value + (newval & 0x00ffffff)) & 0x00ffffff;
newval = value | (newval & 0xff000000);
md_number_to_chars (buf, newval, INSN_SIZE);
break;
--- 5560,5623 ----
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;
#endif
!
! /* 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)
! {
! /* Compute the pc relative valeu to go into the branch. */
! value = fixP->fx_offset - fixP->fx_frag->fr_address;
!
! /* Permit a negative branch provided that enough bits are set. */
! if ((value & 0xfe000000UL) == 0xfe000000UL)
! {
! value &= ~ 0xfc000000UL;
! fixP->fx_done = 1;
! }
! /* Also allow a positive branch, provided enough bits are clear. */
! else if ((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)
! as_bad_where (fixP->fx_file, fixP->fx_line,
! _("out of range branch"));
!
newval = value | (newval & 0xff000000);
md_number_to_chars (buf, newval, INSN_SIZE);
break;