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 19:01:58 -0800
- CC: scottb at netwinder dot org, binutils at sourceware dot cygnus dot com
Hi Grant,
Stop press - I have just found a bug in my last patch. It would not
compute the pc-relative relocation correctly if the branch was not
immediately following a label. This version will. I can now
assemble and link this source file and get a working binary:
.text
bar:
b label2
nop
nop
label2:
nop
.org 0x03000000
baz:
b label3
b label3
nop
label3:
nop
.org 0x05000000
foo:
b label3
b label3
b label1
nop
label1:
nop
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 03:00:24
*************** md_apply_fix3 (fixP, val, seg)
*** 5560,5572 ****
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,5622 ----
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 = * 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;