[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