[lld] 5c42ba9 - [ARM] armv6m eXecute Only (XO) long branch Thunk
Keith Walker via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 30 04:00:33 PDT 2023
Author: Keith Walker
Date: 2023-06-30T11:56:41+01:00
New Revision: 5c42ba98372ba33988e67a3a88f1b08fba37f6e3
URL: https://github.com/llvm/llvm-project/commit/5c42ba98372ba33988e67a3a88f1b08fba37f6e3
DIFF: https://github.com/llvm/llvm-project/commit/5c42ba98372ba33988e67a3a88f1b08fba37f6e3.diff
LOG: [ARM] armv6m eXecute Only (XO) long branch Thunk
This patch adds a thunk for Thumb long branch on V6-M for eXecute Only.
Note that there is currently no support for a position independant and
eXecute Only V6-M long branch thunk
Differential Revision: https://reviews.llvm.org/D153772
Added:
lld/test/ELF/arm-thumb-thunk-v6m-xo.s
Modified:
lld/ELF/Thunks.cpp
Removed:
################################################################################
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 81140686cef2cf..30559dbe826312 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -196,6 +196,16 @@ class ThumbV6MABSLongThunk final : public ThumbThunk {
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 @@ void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
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 @@ static Thunk *addThunkArmv5v6(RelType reloc, Symbol &s, int64_t a) {
// - 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<ThumbV6MPILongThunk>(s, a);
+ if (config->isPic) {
+ if (!isPureCode)
+ return make<ThumbV6MPILongThunk>(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<ThumbV6MABSXOLongThunk>(s, a);
return make<ThumbV6MABSLongThunk>(s, a);
}
fatal("relocation " + toString(reloc) + " to " + toString(s) +
@@ -1302,7 +1350,8 @@ static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) {
}
// 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 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) {
// - 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 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) {
// 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 @@ Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
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:
diff --git a/lld/test/ELF/arm-thumb-thunk-v6m-xo.s b/lld/test/ELF/arm-thumb-thunk-v6m-xo.s
new file mode 100644
index 00000000000000..10f1e73f0b60a1
--- /dev/null
+++ b/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: <far>:
+// 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
More information about the llvm-commits
mailing list