Index: lld/ELF/Arch/ARM.cpp =================================================================== --- lld/ELF/Arch/ARM.cpp +++ lld/ELF/Arch/ARM.cpp @@ -377,6 +377,54 @@ return distance <= range; } +// LLD in 11.0 has changed it's handling of interworking for symbols that +// do not have type STT_FUNC. We have moved to be more compliant with the +// ELF for the ARM architecture specification and the ld.bfd implementation. +// This change may affect programs that have not been linked with ld.bfd and +// may depend on the old implementation. We warn when the change will result +// in the binary. +// A summary of the difference between 10.0 and 11.0 is that for symbols +// that do not have a type of STT_FUNC LLD will not change a BL to a BLX or +// vice versa. The table below enumerates the changes +// | relocation | STT_FUNC | bit(0) | in | 10.0- out | 11.0+ out | +// | R_ARM_CALL | no | 1 | BL | BLX | BL | +// | R_ARM_CALL | no | 0 | BLX | BL | BLX | +// | R_ARM_THM_CALL | no | 1 | BLX | BL | BLX | +// | R_ARM_THM_CALL | no | 0 | BL | BLX | BL | +// +// FIXME: We could do better for most objects by loading the mapping symbols, +// and using these to determine the state. Mapping symbols are the $a, $t and +// $d that the disassembler uses to switch between ARM, Thumb and Literal +// data. This is quite an expensive operation as it needs at least one scan +// of the local symbol table to find the closest mapping symbol predecessor to +// s. +static void warnOnStateMismatch(uint8_t *loc, RelType relt, const Symbol &s, + bool isBlx) { + if (s.type == STT_FUNC) + return; + + bool bit0 = s.getVA() & 0x1; + if ((relt == R_ARM_CALL && (bit0 ? !isBlx : isBlx)) || + (relt == R_ARM_THM_CALL && (bit0 ? isBlx : !isBlx))) { + if (s.isSection()) { + // Section symbols must be defined and in a section. Users cannot change + // the type. Use the section name as getName() returns an empty string. + const auto *d = cast(&s); + warn(getErrorLocation(loc) + "branch and link relocation: " + + toString(relt) + " to STT_SECTION symbol " + d->section->name + + " ; interworking not performed"); + } else { + // Warn with hint on how to alter the symbol type. + warn(getErrorLocation(loc) + "branch and link relocation: " + + toString(relt) + " to non STT_FUNC symbol: " + s.getName() + + " interworking not performed; consider using directive '.type " + + s.getName() + + ", %function' to give symbol type STT_FUNC if" + " interworking between ARM and Thumb is required"); + } + } +} + void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { switch (rel.type) { case R_ARM_ABS32: @@ -402,11 +450,17 @@ 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.expr != R_PLT_PC; + if (!interwork && rel.sym) + warnOnStateMismatch(loc, rel.type, *rel.sym, isBlx); + 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 +468,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 +497,25 @@ ((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.expr == R_PLT_PC; + if (!interwork && rel.sym) + warnOnStateMismatch(loc, rel.type, *rel.sym, isBlx); + 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 +529,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: Index: lld/test/ELF/Inputs/arm-thumb-abs.s =================================================================== --- /dev/null +++ lld/test/ELF/Inputs/arm-thumb-abs.s @@ -0,0 +1,4 @@ +/// absolute symbol with bit-0 set. If used as a B, BL or BLX target no +/// interworking will be done as sym has type STT_NOTYPE +.global sym +.equ sym, 0x13001 Index: lld/test/ELF/arm-thumb-interwork-abs.s =================================================================== --- /dev/null +++ lld/test/ELF/arm-thumb-interwork-abs.s @@ -0,0 +1,39 @@ +// REQUIRES: arm +// RUN: llvm-mc --triple=armv7a-linux-gnueabihf -arm-add-build-attributes -filetype=obj -o %t.o %s +// RUN: llvm-mc --triple=armv7a-linux-gnueabihf -arm-add-build-attributes -filetype=obj -o %t2.o %S/Inputs/arm-thumb-abs.s +// RUN: ld.lld %t.o %t2.o -o %t --no-threads 2>&1 | FileCheck %s --check-prefix=WARN +// RUN: llvm-objdump --no-show-raw-insn -d %t | FileCheck %s + +/// A similar test to arm-thumb-interwork-notfunc.s this time exercising the +/// case where a symbol does not have type STT_FUNC but it does have the bottom +/// bit set. We use absolute symbols to represent assembler labels as the +/// minimum alignment of a label in code is 2. +.syntax unified +.global sym +.global _start +.type _start, %function +.text +.balign 0x1000 +_start: +arm_caller: +.arm + b sym + bl sym +// WARN: branch and link relocation: R_ARM_CALL to non STT_FUNC symbol: sym interworking not performed; consider using directive '.type sym, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + blx sym +.thumb +thumb_caller: + b sym + bl sym + blx sym +// WARN: branch and link relocation: R_ARM_THM_CALL to non STT_FUNC symbol: sym interworking not performed; consider using directive '.type sym, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + +// CHECK: 00012000 arm_caller: +// CHECK-NEXT: 12000: b #4088 +// CHECK-NEXT: 12004: bl #4084 +// CHECK-NEXT: 12008: blx #4080 + +// CHECK: 0001200c thumb_caller: +// CHECK-NEXT: 1200c: b.w #4080 +// CHECK-NEXT: 12010: bl #4076 +// CHECK-NEXT: 12014: blx #4076 Index: lld/test/ELF/arm-thumb-interwork-notfunc.s =================================================================== --- lld/test/ELF/arm-thumb-interwork-notfunc.s +++ lld/test/ELF/arm-thumb-interwork-notfunc.s @@ -1,6 +1,6 @@ // REQUIRES: arm // RUN: llvm-mc --triple=armv7a-linux-gnueabihf -arm-add-build-attributes -filetype=obj -o %t.o %s -// RUN: ld.lld %t.o -o %t +// RUN: ld.lld %t.o -o %t --no-threads 2>&1 | FileCheck %s --check-prefix=WARN // RUN: llvm-objdump --no-show-raw-insn -d %t | FileCheck %s .syntax unified @@ -23,7 +23,11 @@ /// 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. +/// LLD will warn for the BL and BLX cases where the behavior has changed +/// from LLD 10.0 .section .arm_caller, "ax", %progbits .balign 4 .arm @@ -35,10 +39,30 @@ 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 +// WARN: branch and link relocation: R_ARM_CALL to STT_SECTION symbol .arm_target ; interworking not performed + blx arm_func_with_notype +// WARN: branch and link relocation: R_ARM_CALL to non STT_FUNC symbol: arm_func_with_notype interworking not performed; consider using directive '.type arm_func_with_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + blx arm_func_with_explicit_notype +// WARN: branch and link relocation: R_ARM_CALL to non STT_FUNC symbol: arm_func_with_explicit_notype interworking not performed; consider using directive '.type arm_func_with_explicit_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + blx .thumb_target +// WARN: branch and link relocation: R_ARM_CALL to STT_SECTION symbol .thumb_target ; interworking not performed + blx thumb_func_with_notype +// WARN: branch and link relocation: R_ARM_CALL to non STT_FUNC symbol: thumb_func_with_notype interworking not performed; consider using directive '.type thumb_func_with_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + blx thumb_func_with_explicit_notype +// WARN: branch and link relocation: R_ARM_CALL to non STT_FUNC symbol: thumb_func_with_explicit_notype interworking not performed; consider using directive '.type thumb_func_with_explicit_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required .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 +75,24 @@ beq.w .thumb_target beq.w thumb_func_with_notype beq.w thumb_func_with_explicit_notype + bl .arm_target +// WARN: branch and link relocation: R_ARM_THM_CALL to STT_SECTION symbol .arm_target ; interworking not performed + bl arm_func_with_notype +// WARN: branch and link relocation: R_ARM_THM_CALL to non STT_FUNC symbol: arm_func_with_notype interworking not performed; consider using directive '.type arm_func_with_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + bl arm_func_with_explicit_notype + // WARN: branch and link relocation: R_ARM_THM_CALL to non STT_FUNC symbol: arm_func_with_explicit_notype interworking not performed; consider using directive '.type arm_func_with_explicit_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + bl .thumb_target +// WARN: branch and link relocation: R_ARM_THM_CALL to STT_SECTION symbol .thumb_target ; interworking not performed + bl thumb_func_with_notype +// WARN: branch and link relocation: R_ARM_THM_CALL to non STT_FUNC symbol: thumb_func_with_notype interworking not performed; consider using directive '.type thumb_func_with_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + bl thumb_func_with_explicit_notype +// branch and link relocation: R_ARM_THM_CALL to non STT_FUNC symbol: thumb_func_with_explicit_notype interworking not performed; consider using directive '.type thumb_func_with_explicit_notype, %function' to give symbol type STT_FUNC if interworking between ARM and Thumb is required + 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 +101,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 Index: lld/test/ELF/arm-thumb-undefined-weak.s =================================================================== --- lld/test/ELF/arm-thumb-undefined-weak.s +++ lld/test/ELF/arm-thumb-undefined-weak.s @@ -10,6 +10,7 @@ .syntax unified .weak target + .type target, %function .text .global _start Index: lld/test/ELF/arm-undefined-weak.s =================================================================== --- lld/test/ELF/arm-undefined-weak.s +++ lld/test/ELF/arm-undefined-weak.s @@ -12,6 +12,7 @@ .syntax unified .weak target + .type target, %function .text .global _start