Index: lld/ELF/Thunks.cpp =================================================================== --- lld/ELF/Thunks.cpp +++ lld/ELF/Thunks.cpp @@ -196,6 +196,16 @@ void addSymbols(ThunkSection &isec) override; }; +class ThumbV6MABSXOLongThunk final : public ThumbThunk { +public: + ThumbV6MABSXOLongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} + + uint32_t sizeLong() override { return 20; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + class ThumbV6MPILongThunk final : public ThumbThunk { public: ThumbV6MPILongThunk(Symbol &dest, int64_t addend) @@ -748,6 +758,34 @@ addSymbol("$d", STT_NOTYPE, 8, isec); } +void ThumbV6MABSXOLongThunk::writeLong(uint8_t *buf) { + // Most Thumb instructions cannot access the high registers r8 - r15. As the + // only register we can corrupt is r12 we must instead spill a low register + // to the stack to use as a scratch register. We push r1 even though we + // don't need to get some space to use for the return address. + write16(buf + 0, 0xb403); // push {r0, r1} ; Obtain scratch registers + write16(buf + 2, 0x2000); // movs r0, :upper8_15:S + write16(buf + 4, 0x0200); // lsls r0, r0, #8 + write16(buf + 6, 0x3000); // adds r0, :upper0_7:S + write16(buf + 8, 0x0200); // lsls r0, r0, #8 + write16(buf + 10, 0x3000); // adds r0, :lower8_15:S + write16(buf + 12, 0x0200); // lsls r0, r0, #8 + write16(buf + 14, 0x3000); // adds r0, :lower0_7:S + write16(buf + 16, 0x9001); // str r0, [sp, #4] ; SP + 4 = S + write16(buf + 18, 0xbd01); // pop {r0, pc} ; restore r0 and branch to dest + uint64_t s = getARMThunkDestVA(destination); + target->relocateNoSym(buf + 2, R_ARM_THM_ALU_ABS_G3, s); + target->relocateNoSym(buf + 6, R_ARM_THM_ALU_ABS_G2_NC, s); + target->relocateNoSym(buf + 10, R_ARM_THM_ALU_ABS_G1_NC, s); + target->relocateNoSym(buf + 14, R_ARM_THM_ALU_ABS_G0_NC, s); +} + +void ThumbV6MABSXOLongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv6MABSXOLongThunk_" + destination.getName()), + STT_FUNC, 1, isec); + addSymbol("$t", STT_NOTYPE, 0, isec); +} + void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { // Most Thumb instructions cannot access the high registers r8 - r15. As the // only register we can corrupt is ip (r12) we must instead spill a low @@ -1288,13 +1326,23 @@ // - MOVT and MOVW instructions cannot be used. // - Only a limited number of instructions can access registers r8 and above // - No interworking support is needed (all Thumb). -static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) { +static Thunk *addThunkV6M(const InputSection &isec, RelType reloc, Symbol &s, + int64_t a) { + const bool isPureCode = isec.getParent()->flags & SHF_ARM_PURECODE; switch (reloc) { case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - if (config->isPic) - return make(s, a); + if (config->isPic) { + if (!isPureCode) + return make(s, a); + + fatal("relocation " + toString(reloc) + " to " + toString(s) + + " not supported for Armv6-M targets for position independant" + " and execute only code"); + } + if (isPureCode) + return make(s, a); return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + @@ -1302,7 +1350,8 @@ } // Creates a thunk for Thumb-ARM interworking or branch range extension. -static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) { +static Thunk *addThunkArm(const InputSection &isec, RelType reloc, Symbol &s, + int64_t a) { // Decide which Thunk is needed based on: // Available instruction set // - An Arm Thunk can only be used if Arm state is available. @@ -1314,6 +1363,7 @@ // - Branch and link relocations can change state, can select Thunks from // either Arm or Thumb. // Position independent Thunks if we require position independent code. + // Execute Only Thunks if the output section is execute only code. // Handle architectures that have restrictions on the instructions that they // can use in Thunks. The flags below are set by reading the BuildAttributes @@ -1321,7 +1371,7 @@ // architecture to flag. if (!config->armHasMovtMovw) { if (config->armJ1J2BranchEncoding) - return addThunkV6M(reloc, s, a); + return addThunkV6M(isec, reloc, s, a); if (config->armHasBlx) return addThunkArmv5v6(reloc, s, a); return addThunkArmv4(reloc, s, a); @@ -1411,7 +1461,7 @@ case EM_AARCH64: return addThunkAArch64(rel.type, s, a); case EM_ARM: - return addThunkArm(rel.type, s, a); + return addThunkArm(isec, rel.type, s, a); case EM_AVR: return addThunkAVR(rel.type, s, a); case EM_MIPS: Index: lld/test/ELF/arm-thumb-thunk-v6m-xo.s =================================================================== --- /dev/null +++ lld/test/ELF/arm-thumb-thunk-v6m-xo.s @@ -0,0 +1,57 @@ +// REQUIRES: arm +// RUN: rm -rf %t && split-file %s %t +// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv6m-none-eabi %t/a.s -o %t/a.o +// RUN: ld.lld --no-rosegment --script %t/a.t %t/a.o -o %t/a +// RUN: llvm-objdump --no-print-imm-hex --no-show-raw-insn -d %t/a --triple=armv6m-none-eabi | FileCheck %s +// RUN: not ld.lld --no-rosegment --script %t/a.t %t/a.o -o %t/a2 --pie 2>&1 | FileCheck --check-prefix=CHECK-PI %s +// RUN: rm -f %t/a %t/a2 + +// Range extension thunks for Arm Architecture v6m. Only Thumb instructions +// are permitted which limits the access to instructions that can access the +// high registers (r8 - r15), this means that the thunks have to spill +// low registers (r0 - r7) in order to perform the transfer of control. + +//--- a.t +SECTIONS { + .text_low 0x11345670 : { *(.text_low) } + .text_high 0x12345678 : { *(.text_high) } +} + +//--- a.s +// The 'y' on the .section directive means that this section is eXecute Only code + .syntax unified + .section .text_low, "axy", %progbits + .thumb + .type _start, %function + .balign 4 + .globl _start +_start: + bl far + + .section .text_high, "ax", %progbits + .globl far + .type far, %function +far: + bx lr + +// CHECK: Disassembly of section .text_low: +// CHECK-EMPTY: +// CHECK-NEXT: <_start>: +// CHECK-NEXT: 11345670: bl 0x11345674 <__Thumbv6MABSXOLongThunk_far> +// CHECK: <__Thumbv6MABSXOLongThunk_far>: +// CHECK-NEXT: push {r0, r1} +// CHECK-NEXT: movs r0, #18 +// CHECK-NEXT: lsls r0, r0, #8 +// CHECK-NEXT: adds r0, #52 +// CHECK-NEXT: lsls r0, r0, #8 +// CHECK-NEXT: adds r0, #86 +// CHECK-NEXT: lsls r0, r0, #8 +// CHECK-NEXT: adds r0, #121 +// CHECK-NEXT: str r0, [sp, #4] +// CHECK-NEXT: pop {r0, pc} +// CHECK: Disassembly of section .text_high: +// CHECK-EMPTY: +// CHECK-NEXT: : +// CHECK-NEXT: 12345678: bx lr + +// CHECK-PI: error: relocation R_ARM_THM_CALL to far not supported for Armv6-M targets for position independant and execute only code