diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -402,11 +402,15 @@ checkInt(loc, val, 31, rel); write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; - case R_ARM_CALL: - // R_ARM_CALL is used for BL and BLX instructions, depending on the - // value of bit 0 of Val, we must select a BL or BLX instruction - if (val & 1) { - // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. + case R_ARM_CALL: { + // R_ARM_CALL is used for BL and BLX instructions, for symbols of type + // STT_FUNC we choose whether to write a BL or BLX depending on the + // value of bit 0 of Val. With bit 0 == 1 denoting Thumb. If the symbol is + // not of type STT_FUNC then we must preserve the original instruction. + // PLT entries are always ARM state so we know we don't need to interwork. + bool isBlx = (read32le(loc) & 0xfe000000) == 0xfa000000; + bool interwork = rel.sym && rel.sym->isFunc() && rel.type != R_PLT_PC; + if (interwork ? val & 1 : isBlx) { // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' checkInt(loc, val, 26, rel); write32le(loc, 0xfa000000 | // opcode @@ -414,11 +418,11 @@ ((val >> 2) & 0x00ffffff)); // imm24 break; } - if ((read32le(loc) & 0xfe000000) == 0xfa000000) - // BLX (always unconditional) instruction to an ARM Target, select an - // unconditional BL. - write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); + // BLX (always unconditional) instruction to an ARM Target, select an + // unconditional BL. + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B + } LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: @@ -443,16 +447,23 @@ ((val >> 5) & 0x2000) | // J1 ((val >> 1) & 0x07ff)); // imm11 break; - case R_ARM_THM_CALL: - // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the - // value of bit 0 of Val, we must select a BL or BLX instruction - if ((val & 1) == 0) { - // Ensure BLX destination is 4-byte aligned. As BLX instruction may - // only be two byte aligned. This must be done before overflow check + case R_ARM_THM_CALL: { + // R_ARM_THM_CALL is used for BL and BLX instructions, for symbols of type + // STT_FUNC we choose whether to write a BL or BLX depending on the + // value of bit 0 of Val. With bit 0 == 0 denoting ARM, if the symbol is + // not of type STT_FUNC then we must preserve the original instruction. + // PLT entries are always ARM state so we know we need to interwork. + bool isBlx = (read16le(loc + 2) & 0x1000) == 0; + bool interwork = (rel.sym && rel.sym->isFunc()) || rel.type == R_PLT_PC; + if (interwork ? (val & 1) == 0 : isBlx) { + // We are writing a BLX. Ensure BLX destination is 4-byte aligned. As + // the BLX instruction may only be two byte aligned. This must be done + // before overflow check. val = alignTo(val, 4); + write16le(loc + 2, read16le(loc + 2) & ~0x1000); + } else { + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | 1 << 12); } - // Bit 12 is 0 for BLX, 1 for BL - write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. @@ -466,6 +477,7 @@ ((val >> 1) & 0x07ff)); // imm11 break; } + } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: diff --git a/lld/test/ELF/arm-thumb-interwork-notfunc.s b/lld/test/ELF/arm-thumb-interwork-notfunc.s --- a/lld/test/ELF/arm-thumb-interwork-notfunc.s +++ b/lld/test/ELF/arm-thumb-interwork-notfunc.s @@ -23,7 +23,9 @@ /// All the symbols that are targets of the branch relocations do not have /// type STT_FUNC. LLD should not insert interworking thunks as non STT_FUNC /// symbols have no state information, the ABI assumes the user has manually -/// done the interworking. +/// done the interworking. For the BL and BLX instructions LLD should +/// preserve the original instruction instead of writing out the correct one +/// for the assumed state at the target. .section .arm_caller, "ax", %progbits .balign 4 .arm @@ -35,10 +37,24 @@ b .thumb_target b thumb_func_with_notype b thumb_func_with_explicit_notype + bl .arm_target + bl arm_func_with_notype + bl arm_func_with_explicit_notype + bl .thumb_target + bl thumb_func_with_notype + bl thumb_func_with_explicit_notype + blx .arm_target + blx arm_func_with_notype + blx arm_func_with_explicit_notype + blx .thumb_target + blx thumb_func_with_notype + blx thumb_func_with_explicit_notype .section .thumb_caller, "ax", %progbits .thumb .balign 4 + .global thumb_caller +thumb_caller: b.w .arm_target b.w arm_func_with_notype b.w arm_func_with_explicit_notype @@ -51,6 +67,18 @@ beq.w .thumb_target beq.w thumb_func_with_notype beq.w thumb_func_with_explicit_notype + bl .arm_target + bl arm_func_with_notype + bl arm_func_with_explicit_notype + bl .thumb_target + bl thumb_func_with_notype + bl thumb_func_with_explicit_notype + blx .arm_target + blx arm_func_with_notype + blx arm_func_with_explicit_notype + blx .thumb_target + blx thumb_func_with_notype + blx thumb_func_with_explicit_notype // CHECK: 00012008 _start: // CHECK-NEXT: 12008: b #-16 @@ -59,15 +87,41 @@ // CHECK-NEXT: 12014: b #-24 // CHECK-NEXT: 12018: b #-28 // CHECK-NEXT: 1201c: b #-32 -// CHECK: 12020: b.w #-36 -// CHECK-NEXT: 12024: b.w #-40 -// CHECK-NEXT: 12028: b.w #-44 -// CHECK-NEXT: 1202c: b.w #-44 -// CHECK-NEXT: 12030: b.w #-48 -// CHECK-NEXT: 12034: b.w #-52 -// CHECK-NEXT: 12038: beq.w #-60 -// CHECK-NEXT: 1203c: beq.w #-64 -// CHECK-NEXT: 12040: beq.w #-68 -// CHECK-NEXT: 12044: beq.w #-68 -// CHECK-NEXT: 12048: beq.w #-72 -// CHECK-NEXT: 1204c: beq.w #-76 +// CHECK-NEXT: 12020: bl #-40 +// CHECK-NEXT: 12024: bl #-44 +// CHECK-NEXT: 12028: bl #-48 +// CHECK-NEXT: 1202c: bl #-48 +// CHECK-NEXT: 12030: bl #-52 +// CHECK-NEXT: 12034: bl #-56 +// CHECK-NEXT: 12038: blx #-64 +// CHECK-NEXT: 1203c: blx #-68 +// CHECK-NEXT: 12040: blx #-72 +// CHECK-NEXT: 12044: blx #-72 +// CHECK-NEXT: 12048: blx #-76 +// CHECK-NEXT: 1204c: blx #-80 + +// CHECK: 00012050 thumb_caller: +// CHECK-NEXT: 12050: b.w #-84 +// CHECK-NEXT: 12054: b.w #-88 +// CHECK-NEXT: 12058: b.w #-92 +// CHECK-NEXT: 1205c: b.w #-92 +// CHECK-NEXT: 12060: b.w #-96 +// CHECK-NEXT: 12064: b.w #-100 +// CHECK-NEXT: 12068: beq.w #-108 +// CHECK-NEXT: 1206c: beq.w #-112 +// CHECK-NEXT: 12070: beq.w #-116 +// CHECK-NEXT: 12074: beq.w #-116 +// CHECK-NEXT: 12078: beq.w #-120 +// CHECK-NEXT: 1207c: beq.w #-124 +// CHECK-NEXT: 12080: bl #-132 +// CHECK-NEXT: 12084: bl #-136 +// CHECK-NEXT: 12088: bl #-140 +// CHECK-NEXT: 1208c: bl #-140 +// CHECK-NEXT: 12090: bl #-144 +// CHECK-NEXT: 12094: bl #-148 +// CHECK-NEXT: 12098: blx #-156 +// CHECK-NEXT: 1209c: blx #-160 +// CHECK-NEXT: 120a0: blx #-164 +// CHECK-NEXT: 120a4: blx #-164 +// CHECK-NEXT: 120a8: blx #-168 +// CHECK-NEXT: 120ac: blx #-172 diff --git a/lld/test/ELF/arm-thumb-undefined-weak.s b/lld/test/ELF/arm-thumb-undefined-weak.s --- a/lld/test/ELF/arm-thumb-undefined-weak.s +++ b/lld/test/ELF/arm-thumb-undefined-weak.s @@ -10,6 +10,7 @@ .syntax unified .weak target + .type target, %function .text .global _start diff --git a/lld/test/ELF/arm-undefined-weak.s b/lld/test/ELF/arm-undefined-weak.s --- a/lld/test/ELF/arm-undefined-weak.s +++ b/lld/test/ELF/arm-undefined-weak.s @@ -12,6 +12,7 @@ .syntax unified .weak target + .type target, %function .text .global _start