[lld] 8806df4 - [lld-macho] Preliminary support for ARM_RELOC_BR24

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Wed May 5 11:42:16 PDT 2021


Author: Jez Ng
Date: 2021-05-05T14:41:01-04:00
New Revision: 8806df4778349dac4e8744b9ae50f43b80eedda3

URL: https://github.com/llvm/llvm-project/commit/8806df4778349dac4e8744b9ae50f43b80eedda3
DIFF: https://github.com/llvm/llvm-project/commit/8806df4778349dac4e8744b9ae50f43b80eedda3.diff

LOG: [lld-macho] Preliminary support for ARM_RELOC_BR24

ARM_RELOC_BR24 is used for BL/BLX instructions from within ARM (i.e. not
Thumb) code. This diff just handles the basic case: branches from ARM to
ARM, or from ARM to Thumb where no shimming is required. (See comments
in ARM.cpp for why shims are required.)

Note: I will likely be deprioritizing ARM work for the near future to
focus on other parts of LLD. Apologies for the half-done state of this;
I'm just trying to wrap up what I've already worked on.

Reviewed By: #lld-macho, alexshap

Differential Revision: https://reviews.llvm.org/D101814

Added: 
    lld/test/MachO/arm-branch-relocs.s

Modified: 
    lld/MachO/Arch/ARM.cpp

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Arch/ARM.cpp b/lld/MachO/Arch/ARM.cpp
index fe7d69b587b41..f2d606f396e93 100644
--- a/lld/MachO/Arch/ARM.cpp
+++ b/lld/MachO/Arch/ARM.cpp
@@ -12,9 +12,11 @@
 #include "Target.h"
 
 #include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/Bitfields.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/Endian.h"
 
+using namespace llvm;
 using namespace llvm::MachO;
 using namespace llvm::support::endian;
 using namespace lld;
@@ -28,7 +30,7 @@ struct ARM : TargetInfo {
   int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
                             const relocation_info) const override;
   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
-                   uint64_t relocVA) const override;
+                   uint64_t pc) const override;
 
   void writeStub(uint8_t *buf, const Symbol &) const override;
   void writeStubHelperHeader(uint8_t *buf) const override;
@@ -50,8 +52,8 @@ const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const {
       {"SECTDIFF", /* FIXME populate this */ B(_0)},
       {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)},
       {"PB_LA_PTR", /* FIXME populate this */ B(_0)},
-      {"BR24", /* FIXME populate this */ B(_0)},
-      {"BR22", /* FIXME populate this */ B(_0)},
+      {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+      {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
       {"32BIT_BRANCH", /* FIXME populate this */ B(_0)},
       {"HALF", /* FIXME populate this */ B(_0)},
       {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)},
@@ -65,12 +67,77 @@ const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const {
 
 int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
                                relocation_info rel) const {
-  fatal("TODO: implement this");
+  // FIXME: implement this
+  return 0;
 }
 
+template <int N> using BitfieldFlag = Bitfield::Element<bool, N, 1>;
+
+// ARM BL encoding:
+//
+// 30       28        24                                              0
+// +---------+---------+----------------------------------------------+
+// |  cond   | 1 0 1 1 |                  imm24                       |
+// +---------+---------+----------------------------------------------+
+//
+// `cond` here varies depending on whether we have bleq, blne, etc.
+// `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM
+// functions are 4-byte-aligned.
+//
+// ARM BLX encoding:
+//
+// 30       28        24                                              0
+// +---------+---------+----------------------------------------------+
+// | 1 1 1 1 | 1 0 1 H |                  imm24                       |
+// +---------+---------+----------------------------------------------+
+//
+// Since Thumb functions are 2-byte-aligned, we need one extra bit to encode
+// the offset -- that is the H bit.
+//
+// BLX is always unconditional, so while we can convert directly from BLX to BL,
+// we need to insert a shim if a BL's target is a Thumb function.
+//
+// Helper aliases for decoding BL / BLX:
+using Cond = Bitfield::Element<uint32_t, 28, 4>;
+using Imm24 = Bitfield::Element<int32_t, 0, 24>;
+
 void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
-                      uint64_t relocVA) const {
-  fatal("TODO: implement this");
+                      uint64_t pc) const {
+  switch (r.type) {
+  case ARM_RELOC_BR24: {
+    uint32_t base = read32le(loc);
+    bool isBlx = Bitfield::get<Cond>(base) == 0xf;
+    const Symbol *sym = r.referent.get<const Symbol *>();
+    int32_t offset = value - (pc + 8);
+
+    if (auto *defined = dyn_cast<Defined>(sym)) {
+      if (!isBlx && defined->thumb) {
+        error("TODO: implement interworking shim");
+        return;
+      } else if (isBlx && !defined->thumb) {
+        Bitfield::set<Cond>(base, 0xe); // unconditional BL
+        Bitfield::set<BitfieldFlag<24>>(base, 1);
+        isBlx = false;
+      }
+    } else {
+      error("TODO: Implement ARM_RELOC_BR24 for dylib symbols");
+      return;
+    }
+
+    if (isBlx) {
+      assert((0x1 & value) == 0);
+      Bitfield::set<Imm24>(base, offset >> 2);
+      Bitfield::set<BitfieldFlag<24>>(base, (offset >> 1) & 1); // H bit
+    } else {
+      assert((0x3 & value) == 0);
+      Bitfield::set<Imm24>(base, offset >> 2);
+    }
+    write32le(loc, base);
+    break;
+  }
+  default:
+    fatal("unhandled relocation type");
+  }
 }
 
 void ARM::writeStub(uint8_t *buf, const Symbol &sym) const {

diff  --git a/lld/test/MachO/arm-branch-relocs.s b/lld/test/MachO/arm-branch-relocs.s
new file mode 100644
index 0000000000000..a82a159d8c1bc
--- /dev/null
+++ b/lld/test/MachO/arm-branch-relocs.s
@@ -0,0 +1,33 @@
+# REQUIRES: arm
+# RUN: llvm-mc -filetype=obj -triple=armv7-apple-watchos %s -o %t.o
+# RUN: %lld-watchos -dylib -arch armv7 -lSystem -o %t %t.o
+# RUN: llvm-objdump --macho -d %t | FileCheck %s
+
+# CHECK:      _arm:
+# CHECK-NEXT: blx	_thumb_1
+# CHECK-NEXT: blx	_thumb_2
+# CHECK-NEXT: bl	_arm
+# CHECK-NEXT: bl	_arm
+
+.globl _arm, _thumb_1, _thumb_2
+.syntax unified
+.thumb_func _thumb_1
+.thumb_func _thumb_2
+
+.p2align 2
+
+.code 16
+## These two thumb functions are exactly 2 bytes apart in order to test that we
+## set the H bit correctly in the BLX instruction.
+_thumb_1:
+  nop
+
+_thumb_2:
+  nop
+
+.code 32
+_arm:
+  blx _thumb_1
+  blx _thumb_2
+  bl _arm
+  blx _arm


        


More information about the llvm-commits mailing list