From 4f3c3dbb370a8c0c8b66710c5d0c2854c1bab4f5 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 6 Mar 2001 22:33:47 +0000 Subject: [PATCH] Fix BLX(1) for Thumb --- bfd/ChangeLog | 8 ++++++++ bfd/coff-arm.c | 45 ++++++++++++++++++++++----------------------- bfd/elf32-arm.h | 11 +++++++++++ gas/ChangeLog | 5 +++++ gas/config/tc-arm.c | 9 +++++++++ opcodes/ChangeLog | 6 ++++++ opcodes/arm-dis.c | 25 +++++++++++++++++++++---- sim/arm/ChangeLog | 6 ++++++ sim/arm/thumbemu.c | 23 ++++++++++++++++++----- 9 files changed, 106 insertions(+), 32 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 1e037b06be4..32794be55a4 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2001-03-06 Nick Clifton + + * 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. + 2001-03-06 Nick Clifton * coff-arm.c (coff_arm_reloc_type_lookup): Add diff --git a/bfd/coff-arm.c b/bfd/coff-arm.c index 3431567e44a..ad4813f808b 100644 --- a/bfd/coff-arm.c +++ b/bfd/coff-arm.c @@ -1588,18 +1588,18 @@ coff_arm_relocate_section (output_bfd, info, input_bfd, input_section, BFD_ASSERT (size == 4); - /* howto->pc_relative should be TRUE for type 14 BRANCH23 */ + /* 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 */ + /* 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. */ + /* Drop unwanted bits from the value we are relocating to. */ check = relocation >> howto->rightshift; /* If this is a signed value, the rightshift just dropped @@ -1613,13 +1613,9 @@ coff_arm_relocate_section (output_bfd, info, input_bfd, input_section, /* Get the value from the object file. */ if (bfd_big_endian (input_bfd)) - { - add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1); - } + add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1); else - { - add = ((((x) & 0x7ff) << 12) | (((x) & 0x07ff0000) >> 15)); - } + 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 @@ -1629,18 +1625,16 @@ coff_arm_relocate_section (output_bfd, info, input_bfd, input_section, 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. */ - /* howto->bitpos == 0 */ - signed_check += signed_add; - relocation += signed_add; + relocation += signed_add; BFD_ASSERT (howto->complain_on_overflow == complain_overflow_signed); @@ -1649,21 +1643,26 @@ coff_arm_relocate_section (output_bfd, info, input_bfd, input_section, || signed_check < reloc_signed_min) overflow = true; - /* Put RELOCATION into the correct bits: */ - + /* 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)); - } + relocation = (((relocation & 0xffe) >> 1) | ((relocation << 4) & 0x07ff0000)); else - { - relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff)); - } + relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff)); - /* Add RELOCATION to the correct bits of X: */ + /* Add the relocation to the correct bits of X. */ x = ((x & ~howto->dst_mask) | relocation); - /* Put the relocated value back in the object file: */ + /* Put the relocated value back in the object file. */ bfd_put_32 (input_bfd, x, location); rstat = overflow ? bfd_reloc_overflow : bfd_reloc_ok; diff --git a/bfd/elf32-arm.h b/bfd/elf32-arm.h index 6b567972700..b64df9a9469 100644 --- a/bfd/elf32-arm.h +++ b/bfd/elf32-arm.h @@ -1434,6 +1434,17 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd, 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); diff --git a/gas/ChangeLog b/gas/ChangeLog index 503102a2e0b..9654744ae02 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,8 @@ +2001-03-06 Nick Clifton + + * config/tc-arm.c (md_apply_fix3): Clear bit zero of offset in + BLX(1) instruction. + 2001-03-06 Igor Shevlyakov * config/tc-m68k.c : Add 5407 to archs[] table. diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 514dba7598c..ebb7b97214a 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -7149,6 +7149,15 @@ md_apply_fix3 (fixP, val, seg) 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); } diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 5b21a5c8b42..3677f2e3e75 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,9 @@ +2001-03-06 Nick Clifton + + * 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. + 2001-03-06 Igor Shevlyakov * m68k-dis.c (print_insn_m68k): Recognize Coldfire CPUs diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c index 686b18a1a91..0681758de99 100644 --- a/opcodes/arm-dis.c +++ b/opcodes/arm-dis.c @@ -668,15 +668,32 @@ print_insn_thumb (pc, info, given) /* 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"); + { + 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 (BDISP23 (given) * 2 + pc + 4, info); + func (stream, "bl\t"); + + info->print_address_func (offset * 2 + pc + 4, info); return 4; } else diff --git a/sim/arm/ChangeLog b/sim/arm/ChangeLog index fd11ec17cf2..d3d5b1ce365 100644 --- a/sim/arm/ChangeLog +++ b/sim/arm/ChangeLog @@ -1,3 +1,9 @@ +2001-03-06 Nick Clifton + + * 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. + 2001-02-27 Nick Clifton * armvirt.c (GetWord): Add new parameter - check - to enable or diff --git a/sim/arm/thumbemu.c b/sim/arm/thumbemu.c index 3351c2f65da..4f007333363 100644 --- a/sim/arm/thumbemu.c +++ b/sim/arm/thumbemu.c @@ -481,7 +481,6 @@ tdstate ARMul_ThumbDecode (state, pc, tinstr, ainstr) } /* 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 @@ -514,17 +513,31 @@ tdstate ARMul_ThumbDecode (state, pc, tinstr, ainstr) |((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) { - pc += 2; - goto do_blx2; + 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; } - break; /* exit, since not correct instruction */ + else + /* Exit, since not correct instruction. */ + pc -= 2; + break; } /* 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 -- 2.30.2