Fix implementation of BLX(1) instruction for Thumb

Nick Clifton nickc@redhat.com
Tue Mar 6 14:32:00 GMT 2001


Hi Guys,

  I am applying the following patch to fix the implementation of the
  new v5 BLX(1) instruction for the Thumb.  The problem was that the
  code was assuming that bit 0 of the offset could be set, whereas in
  fact the spec says that it must always be zero.

Cheers
        Nick

gas/ChangeLog
2001-03-06  Nick Clifton  <nickc@redhat.com>

	* config/tc-arm.c (md_apply_fix3): Clear bit zero of offset in
	BLX(1) instruction.

bfd/ChangeLog
2001-03-06  Nick Clifton  <nickc@redhat.com>

	* elf32-arm.h (elf32_arm_final_link_relocate): Clear bit zero
	of offset in BLX(1) instruction.
	* coff-arm.c (coff_arm_relocate_section): Clear bit zero of
	offset in BLX(1) instruction.
        Fix formatting.

opcodes/ChangeLog
2001-03-06  Nick Clifton  <nickc@redhat.com>

	* arm-dis.c (print_insn_thumb): Compute destination address
        of BLX(1) instruction by taking bit 1 from PC and not from bit
        0 of the offset.

sim/arm/ChangeLog
2001-03-06  Nick Clifton  <nickc@redhat.com>

	* thumbemu.c (ARMul_ThumbDecode): Delete label bo_blx2.
        Compute destination address of BLX(1) instruction by
        taking bit 1 from PC and not from bit 0 of the offset.        


Index: gas/config/tc-arm.c
===================================================================
RCS file: /cvs/src//src/gas/config/tc-arm.c,v
retrieving revision 1.73
diff -p -r1.73 tc-arm.c
*** tc-arm.c	2001/02/12 13:32:25	1.73
--- tc-arm.c	2001/03/06 22:23:40
*************** md_apply_fix3 (fixP, val, seg)
*** 7149,7154 ****
--- 7149,7163 ----
  
  	newval  = (newval  & 0xf800) | ((value & 0x7fffff) >> 12);
  	newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
+ 	if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ 	  /* Remove bit zero of the adjusted offset.  Bit zero can only be
+ 	     set if the upper insn is at a half-word boundary, since the
+ 	     destination address, an ARM instruction, must always be on a
+ 	     word boundary.  The semantics of the BLX (1) instruction, however,
+ 	     are that bit zero in the offset must always be zero, and the
+ 	     corresponding bit one in the target address will be set from bit
+ 	     one of the source address.  */
+ 	  newval2 &= ~1;
  	md_number_to_chars (buf, newval, THUMB_SIZE);
  	md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
        }

Index: bfd/elf32-arm.h
===================================================================
RCS file: /cvs/src//src/bfd/elf32-arm.h,v
retrieving revision 1.44
diff -p -r1.44 elf32-arm.h
*** elf32-arm.h	2001/02/28 10:13:20	1.44
--- elf32-arm.h	2001/03/06 22:23:40
*************** elf32_arm_final_link_relocate (howto, in
*** 1434,1439 ****
--- 1434,1450 ----
  	upper_insn = (upper_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 12) & 0x7ff);
  	lower_insn = (lower_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 1) & 0x7ff);
  
+ 	if (r_type == R_ARM_THM_XPC22
+ 	    && ((lower_insn & 0x1800) == 0x0800))
+ 	  /* Remove bit zero of the adjusted offset.  Bit zero can only be
+ 	     set if the upper insn is at a half-word boundary, since the
+ 	     destination address, an ARM instruction, must always be on a
+ 	     word boundary.  The semantics of the BLX (1) instruction, however,
+ 	     are that bit zero in the offset must always be zero, and the
+ 	     corresponding bit one in the target address will be set from bit
+ 	     one of the source address.  */
+ 	  lower_insn &= ~1;
+ 	
  	/* Put the relocated value back in the object file:  */
  	bfd_put_16 (input_bfd, upper_insn, hit_data);
  	bfd_put_16 (input_bfd, lower_insn, hit_data + 2);

Index: bfd/coff-arm.c
===================================================================
RCS file: /cvs/src//src/bfd/coff-arm.c,v
retrieving revision 1.24
diff -p -r1.24 coff-arm.c
*** coff-arm.c	2001/03/06 20:27:50	1.24
--- coff-arm.c	2001/03/06 22:23:40
*************** coff_arm_relocate_section (output_bfd, i
*** 1588,1605 ****
  
  	      BFD_ASSERT (size == 4);
  
!               /* howto->pc_relative should be TRUE for type 14 BRANCH23 */
                relocation -= (input_section->output_section->vma
                               + input_section->output_offset);
  
!               /* howto->pcrel_offset should be TRUE for type 14 BRANCH23 */
                relocation -= address;
  
  	      /* No need to negate the relocation with BRANCH23.  */
  	      /* howto->complain_on_overflow == complain_overflow_signed for BRANCH23.  */
  	      /* howto->rightshift == 1 */
- 	      /* Drop unwanted bits from the value we are relocating to.  */
  
  	      check = relocation >> howto->rightshift;
  
  	      /* If this is a signed value, the rightshift just dropped
--- 1588,1605 ----
  
  	      BFD_ASSERT (size == 4);
  
!               /* howto->pc_relative should be TRUE for type 14 BRANCH23.  */
                relocation -= (input_section->output_section->vma
                               + input_section->output_offset);
  
!               /* howto->pcrel_offset should be TRUE for type 14 BRANCH23.  */
                relocation -= address;
  
  	      /* No need to negate the relocation with BRANCH23.  */
  	      /* howto->complain_on_overflow == complain_overflow_signed for BRANCH23.  */
  	      /* howto->rightshift == 1 */
  
+ 	      /* Drop unwanted bits from the value we are relocating to.  */
  	      check = relocation >> howto->rightshift;
  
  	      /* If this is a signed value, the rightshift just dropped
*************** coff_arm_relocate_section (output_bfd, i
*** 1613,1625 ****
  
  	      /* Get the value from the object file.  */
  	      if (bfd_big_endian (input_bfd))
! 		{
! 		  add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1);
! 		}
  	      else
! 		{
! 		  add = ((((x) & 0x7ff) << 12) | (((x) & 0x07ff0000) >> 15));
! 		}
  
  	      /* Get the value from the object file with an appropriate sign.
  		 The expression involving howto->src_mask isolates the upper
--- 1613,1621 ----
  
  	      /* Get the value from the object file.  */
  	      if (bfd_big_endian (input_bfd))
! 		add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1);
  	      else
! 		add = ((((x) & 0x7ff) << 12) | (((x) & 0x07ff0000) >> 15));
  
  	      /* Get the value from the object file with an appropriate sign.
  		 The expression involving howto->src_mask isolates the upper
*************** coff_arm_relocate_section (output_bfd, i
*** 1629,1646 ****
  		 can not get the upper bit, but that does not matter since
  		 signed_add needs no adjustment to become negative in that
  		 case.  */
- 
  	      signed_add = add;
  
  	      if ((add & (((~ src_mask) >> 1) & src_mask)) != 0)
  		signed_add -= (((~ src_mask) >> 1) & src_mask) << 1;
  
  	      /* Add the value from the object file, shifted so that it is a
  		 straight number.  */
- 	      /* howto->bitpos == 0 */
- 
  	      signed_check += signed_add;
! 	      relocation += signed_add;
  
  	      BFD_ASSERT (howto->complain_on_overflow == complain_overflow_signed);
  
--- 1625,1640 ----
  		 can not get the upper bit, but that does not matter since
  		 signed_add needs no adjustment to become negative in that
  		 case.  */
  	      signed_add = add;
  
  	      if ((add & (((~ src_mask) >> 1) & src_mask)) != 0)
  		signed_add -= (((~ src_mask) >> 1) & src_mask) << 1;
  
+ 	      /* howto->bitpos == 0 */
  	      /* Add the value from the object file, shifted so that it is a
  		 straight number.  */
  	      signed_check += signed_add;
! 	      relocation   += signed_add;
  
  	      BFD_ASSERT (howto->complain_on_overflow == complain_overflow_signed);
  
*************** coff_arm_relocate_section (output_bfd, i
*** 1649,1669 ****
  		  || signed_check < reloc_signed_min)
  		overflow = true;
  
! 	      /* Put RELOCATION into the correct bits:  */
  
  	      if (bfd_big_endian (input_bfd))
! 		{
! 		  relocation = (((relocation & 0xffe) >> 1)  | ((relocation << 4) & 0x07ff0000));
! 		}
  	      else
! 		{
! 		  relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff));
! 		}
  
! 	      /* Add RELOCATION to the correct bits of X:  */
  	      x = ((x & ~howto->dst_mask) | relocation);
  
! 	      /* Put the relocated value back in the object file:  */
  	      bfd_put_32 (input_bfd, x, location);
  
  	      rstat = overflow ? bfd_reloc_overflow : bfd_reloc_ok;
--- 1643,1668 ----
  		  || signed_check < reloc_signed_min)
  		overflow = true;
  
! 	      /* For the BLX(1) instruction remove bit 0 of the adjusted offset.
! 		 Bit 0 can only be set if the upper insn is at a half-word boundary,
! 		 since the destination address, an ARM instruction, must always be
! 		 on a word boundary.  The semantics of the BLX (1) instruction,
! 		 however, are that bit 0 in the offset must always be 0, and the
! 		 corresponding bit 1 in the target address will be set from bit
! 		 1 of the source address.  */
! 	      if ((x & 0x18000000) == 0x08000000)
! 		relocation &= ~0x2;
  
+ 	      /* Put the relocation into the correct bits.  */
  	      if (bfd_big_endian (input_bfd))
! 		relocation = (((relocation & 0xffe) >> 1)  | ((relocation << 4) & 0x07ff0000));
  	      else
! 		relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff));
  
! 	      /* Add the relocation to the correct bits of X.  */
  	      x = ((x & ~howto->dst_mask) | relocation);
  
! 	      /* Put the relocated value back in the object file.  */
  	      bfd_put_32 (input_bfd, x, location);
  
  	      rstat = overflow ? bfd_reloc_overflow : bfd_reloc_ok;

Index: opcodes/arm-dis.c
===================================================================
RCS file: /cvs/src//src/opcodes/arm-dis.c,v
retrieving revision 1.20
diff -p -r1.20 arm-dis.c
*** arm-dis.c	2001/01/09 20:29:48	1.20
--- arm-dis.c	2001/03/06 22:23:40
*************** print_insn_thumb (pc, info, given)
*** 668,682 ****
            /* Special processing for Thumb 2 instruction BL sequence:  */
            if (!*c) /* Check for empty (not NULL) assembler string.  */
              {
  	      info->bytes_per_chunk = 4;
  	      info->bytes_per_line  = 4;
  	      
  	      if ((given & 0x10000000) == 0)
!                  func (stream, "blx\t");
  	      else
!                 func (stream, "bl\t");
! 		
!               info->print_address_func (BDISP23 (given) * 2 + pc + 4, info);
                return 4;
              }
            else
--- 668,699 ----
            /* Special processing for Thumb 2 instruction BL sequence:  */
            if (!*c) /* Check for empty (not NULL) assembler string.  */
              {
+ 	      long offset;
+ 	      
  	      info->bytes_per_chunk = 4;
  	      info->bytes_per_line  = 4;
+ 
+ 	      offset = BDISP23 (given);
  	      
  	      if ((given & 0x10000000) == 0)
! 		{
! 		  func (stream, "blx\t");
! 
! 		  /* The spec says that bit 1 of the branch's destination
! 		     address comes from bit 1 of the instruction's
! 		     address and not from the offset in the instruction.  */
! 		  if (offset & 0x1)
! 		    {
! 		      /* func (stream, "*malformed!* "); */
! 		      offset &= ~ 0x1;
! 		    }
! 
! 		  offset |= ((pc & 0x2) >> 1);
! 		}
  	      else
! 		func (stream, "bl\t");
! 
! 	      info->print_address_func (offset * 2 + pc + 4, info);
                return 4;
              }
            else

Index: sim/arm/thumbemu.c
===================================================================
RCS file: /cvs/src//src/sim/arm/thumbemu.c,v
retrieving revision 1.2
diff -p -r1.2 thumbemu.c
*** thumbemu.c	2000/11/30 01:55:12	1.2
--- thumbemu.c	2001/03/06 22:23:40
*************** tdstate ARMul_ThumbDecode (state, pc, ti
*** 481,487 ****
  	    }
  	  /* Drop through.  */
  	  
- 	do_blx2:			/* BLX instruction 2 */
  	  /* Format 19 */
  	  /* There is no single ARM instruction equivalent for this
  	     instruction. Also, it should only ever be matched with the
--- 481,486 ----
*************** tdstate ARMul_ThumbDecode (state, pc, ti
*** 514,530 ****
  	  |((tinstr & (1 << 10)) ? 0xFF800000 : 0));
        valid = t_branch;		/* in-case we don't have the 2nd half */
        tinstr = next_instr;	/* move the instruction down */
        if (((tinstr & 0xF800) >> 11) != 31)
  	{
  	  if (((tinstr & 0xF800) >> 11) == 29)
  	    {
! 	      pc += 2;
! 	      goto do_blx2;
  	    }
! 	  break;		/* exit, since not correct instruction */
  	}
        /* else we fall through to process the second half of the BL */
-       pc += 2;			/* point the pc at the 2nd half */
      case 31:			/* BL instruction 2 */
        /* Format 19 */
        /* There is no single ARM instruction equivalent for this
--- 513,543 ----
  	  |((tinstr & (1 << 10)) ? 0xFF800000 : 0));
        valid = t_branch;		/* in-case we don't have the 2nd half */
        tinstr = next_instr;	/* move the instruction down */
+       pc += 2;			/* point the pc at the 2nd half */
        if (((tinstr & 0xF800) >> 11) != 31)
  	{
  	  if (((tinstr & 0xF800) >> 11) == 29)
  	    {
! 	      ARMword tmp = (pc + 2);
! 
! 	      /* Bit one of the destination address comes from bit one of the
! 		 address of the first (H == 10) half of the instruction, not
! 		 from the offset in the instruction.  */
! 	      state->Reg[15] = ((state->Reg[14]
! 				 + ((tinstr & 0x07FE) << 1)
! 				 + ((pc - 2) & 2))
! 				& 0xFFFFFFFC);
! 	      CLEART;
! 	      state->Reg[14] = (tmp | 1);
! 	      valid = t_branch;
! 	      FLUSHPIPE;
  	    }
! 	  else
! 	    /* Exit, since not correct instruction. */
! 	    pc -= 2;
! 	  break;
  	}
        /* else we fall through to process the second half of the BL */
      case 31:			/* BL instruction 2 */
        /* Format 19 */
        /* There is no single ARM instruction equivalent for this



More information about the Binutils mailing list