This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: arm-elf-as truncates branch offsets w/o warning


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;
  

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]