[clang] [llvm] [Draft] Basic JITLink AArch32 support for clang-repl (PR #77313)

Stefan Gränitz via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 20 10:34:47 PST 2024


https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/77313

>From ab1fa3773766072882666a16682e985bbd9cd72d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 18 Jan 2024 17:26:33 +0100
Subject: [PATCH 1/9] [JITLink][AArch32] Add GOT builder and implement
 R_ARM_GOT_PREL relocations for ELF

---
 .../llvm/ExecutionEngine/JITLink/aarch32.h    | 17 ++++-
 .../ExecutionEngine/JITLink/ELF_aarch32.cpp   |  7 ++
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp  | 65 +++++++++++++++----
 .../JITLink/AArch32/ELF_relocations_data.s    | 50 +++++++++++---
 4 files changed, 118 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 7765208b5e3dfec..0968a093279bfbf 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -41,7 +41,10 @@ enum EdgeKind_aarch32 : Edge::Kind {
   /// Absolute 32-bit value relocation
   Data_Pointer32,
 
-  LastDataRelocation = Data_Pointer32,
+  /// Create GOT entry and store offset
+  Data_RequestGOTAndTransformToDelta32,
+
+  LastDataRelocation = Data_RequestGOTAndTransformToDelta32,
 
   ///
   /// Relocations of class Arm (covers fixed-width 4-byte instruction subset)
@@ -318,6 +321,18 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
   llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
 }
 
+/// Populate a Global Offset Table from edges that request it.
+class GOTBuilder : public TableManager<GOTBuilder> {
+public:
+  static StringRef getSectionName() { return "$__GOT"; }
+
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E);
+  Symbol &createEntry(LinkGraph &G, Symbol &Target);
+
+private:
+  Section *GOTSection = nullptr;
+};
+
 /// Stubs builder for v7 emits non-position-independent Thumb stubs.
 ///
 /// Right now we only have one default stub kind, but we want to extend this
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index b862a7ba2acc922..2553cd70a576912 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -35,6 +35,8 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
   switch (ELFType) {
   case ELF::R_ARM_ABS32:
     return aarch32::Data_Pointer32;
+  case ELF::R_ARM_GOT_PREL:
+    return aarch32::Data_RequestGOTAndTransformToDelta32;
   case ELF::R_ARM_REL32:
     return aarch32::Data_Delta32;
   case ELF::R_ARM_CALL:
@@ -71,6 +73,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
     return ELF::R_ARM_REL32;
   case aarch32::Data_Pointer32:
     return ELF::R_ARM_ABS32;
+  case aarch32::Data_RequestGOTAndTransformToDelta32:
+    return ELF::R_ARM_GOT_PREL;
   case aarch32::Arm_Call:
     return ELF::R_ARM_CALL;
   case aarch32::Arm_Jump24:
@@ -222,6 +226,9 @@ Error buildTables_ELF_aarch32(LinkGraph &G) {
 
   StubsManagerType StubsManager;
   visitExistingEdges(G, StubsManager);
+  aarch32::GOTBuilder GOT;
+  visitExistingEdges(G, GOT);
+
   return Error::success();
 }
 
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 8153c97deff6289..eb51a4896ddcc66 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -395,6 +395,7 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset,
   switch (Kind) {
   case Data_Delta32:
   case Data_Pointer32:
+  case Data_RequestGOTAndTransformToDelta32:
     return SignExtend64<32>(support::endian::read32(FixupPtr, Endian));
   default:
     return make_error<JITLinkError>(
@@ -464,15 +465,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
   char *BlockWorkingMem = B.getAlreadyMutableContent().data();
   char *FixupPtr = BlockWorkingMem + E.getOffset();
 
-  auto Write32 = [FixupPtr, Endian = G.getEndianness()](int64_t Value) {
-    assert(isInt<32>(Value) && "Must be in signed 32-bit range");
-    uint32_t Imm = static_cast<int32_t>(Value);
-    if (LLVM_LIKELY(Endian == endianness::little))
-      endian::write32<endianness::little>(FixupPtr, Imm);
-    else
-      endian::write32<endianness::big>(FixupPtr, Imm);
-  };
-
   Edge::Kind Kind = E.getKind();
   uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
   int64_t Addend = E.getAddend();
@@ -487,16 +479,24 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
     int64_t Value = TargetAddress - FixupAddress + Addend;
     if (!isInt<32>(Value))
       return makeTargetOutOfRangeError(G, B, E);
-    Write32(Value);
+    if (LLVM_LIKELY(G.getEndianness() == endianness::little))
+      endian::write32le(FixupPtr, Value);
+    else
+      endian::write32be(FixupPtr, Value);
     return Error::success();
   }
   case Data_Pointer32: {
     int64_t Value = TargetAddress + Addend;
-    if (!isInt<32>(Value))
+    if (!isUInt<32>(Value))
       return makeTargetOutOfRangeError(G, B, E);
-    Write32(Value);
+    if (LLVM_LIKELY(G.getEndianness() == endianness::little))
+      endian::write32le(FixupPtr, Value);
+    else
+      endian::write32be(FixupPtr, Value);
     return Error::success();
   }
+  case Data_RequestGOTAndTransformToDelta32:
+    llvm_unreachable("Should be transformed");
   default:
     return make_error<JITLinkError>(
         "In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -678,6 +678,46 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
   }
 }
 
+const uint8_t GOTEntryInit[] = {
+    0x00, 0x00, 0x00, 0x00,
+};
+
+/// Create a new node in the link-graph for the given pointer value.
+template <size_t Size>
+static Block &allocPointer(LinkGraph &G, Section &S, const uint8_t (&Content)[Size]) {
+  static_assert(Size == 4, "Pointers are 32-bit");
+  constexpr uint64_t Alignment = 4;
+  ArrayRef<char> Init(reinterpret_cast<const char *>(Content), Size);
+  return G.createContentBlock(S, Init, orc::ExecutorAddr(), Alignment, 0);
+}
+
+Symbol &GOTBuilder::createEntry(LinkGraph &G, Symbol &Target) {
+  if (!GOTSection)
+    GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read);
+  Block &B = allocPointer(G, *GOTSection, GOTEntryInit);
+  constexpr int64_t GOTEntryAddend = 0;
+  B.addEdge(Data_Pointer32, 0, Target, GOTEntryAddend);
+  return G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
+}
+
+bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) {
+  Edge::Kind KindToSet = Edge::Invalid;
+  switch (E.getKind()) {
+  case aarch32::Data_RequestGOTAndTransformToDelta32: {
+    KindToSet = aarch32::Data_Delta32;
+    break;
+  }
+  default:
+    return false;
+  }
+  LLVM_DEBUG(dbgs() << "  Transforming " << G.getEdgeKindName(E.getKind()) << " edge at "
+            << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
+            << formatv("{0:x}", E.getOffset()) << ") into " << G.getEdgeKindName(KindToSet) << "\n");
+  E.setKind(KindToSet);
+  E.setTarget(getEntryForTarget(G, E.getTarget()));
+  return true;
+}
+
 const uint8_t Thumbv7ABS[] = {
     0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000    ; lower 16-bit
     0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000    ; upper 16-bit
@@ -709,6 +749,7 @@ const char *getEdgeKindName(Edge::Kind K) {
   switch (K) {
     KIND_NAME_CASE(Data_Delta32)
     KIND_NAME_CASE(Data_Pointer32)
+    KIND_NAME_CASE(Data_RequestGOTAndTransformToDelta32)
     KIND_NAME_CASE(Arm_Call)
     KIND_NAME_CASE(Arm_Jump24)
     KIND_NAME_CASE(Arm_MovwAbsNC)
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
index 379d35fe4902cb0..f91a4733c40eee0 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
@@ -1,12 +1,13 @@
-# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_armv7.o %s
-# RUN: llvm-objdump -r %t_armv7.o | FileCheck --check-prefix=CHECK-TYPE %s
+# RUN: rm -rf %t && mkdir -p %t/armv7 && mkdir -p %t/thumbv7
+# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/armv7/out.o %s
+# RUN: llvm-objdump -r %t/armv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s
 # RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \
-# RUN:              -abs target=0x76bbe88f -check %s %t_armv7.o
+# RUN:              -abs target=0x76bbe88f -check %s %t/armv7/out.o
 
-# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_thumbv7.o %s
-# RUN: llvm-objdump -r %t_thumbv7.o | FileCheck --check-prefix=CHECK-TYPE %s
+# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/thumbv7/out.o %s
+# RUN: llvm-objdump -r %t/thumbv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s
 # RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \
-# RUN:              -abs target=0x76bbe88f -check %s %t_thumbv7.o
+# RUN:              -abs target=0x76bbe88f -check %s %t/thumbv7/out.o
 
 	.data
 	.global target
@@ -28,10 +29,43 @@ rel32:
 	.word target - .
 	.size rel32, .-rel32
 
-# Empty main function for jitlink to be happy
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_GOT_PREL target
+#
+# The GOT entry contains the absolute address of the external:
+# jitlink-check: *{4}(got_addr(out.o, target)) = target
+#
+# The embedded offset value contains the offset to the GOT entry relative to pc.
+# The +12 accounts for the ARM branch offset (8) and the .LPC offset (4), which
+# is stored as initial addend inline.
+# FIXME: We shouldn't need to substract the 64-bit sign-extension manually.
+# jitlink-check: *{4}got_prel_offset = got_addr(out.o, target) - (got_prel + 12) - 0xffffffff00000000
+	.globl	got_prel
+	.type	got_prel,%function
+	.p2align	2
+	.code	32
+got_prel:
+	ldr	r0, .LCPI
+.LPC:
+	ldr	r0, [pc, r0]
+	ldr	r0, [r0]
+	bx	lr
+# Actual relocation site is on the embedded offset value:
+	.globl	got_prel_offset
+got_prel_offset:
+.LCPI:
+	.long	target(GOT_PREL)-((.LPC+8)-.LCPI)
+	.size	got_prel_offset, .-got_prel_offset
+	.size	got_prel, .-got_prel
+
+# This test is executable with any 4-byte external target:
+#  > echo "unsigned target = 42;" | clang -target armv7-linux-gnueabihf -o target.o -c -xc -
+#  > llvm-jitlink target.o armv7/out.o
+#
 	.globl  main
 	.type main, %function
 	.p2align  2
 main:
-	bx lr
+	push	{lr}
+	bl	got_prel
+	pop	{pc}
 	.size   main, .-main

>From 8b607588a28b54c70609be0d7bc160f732797db8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Fri, 19 Jan 2024 19:17:36 +0100
Subject: [PATCH 2/9] fixup! [JITLink][AArch32] Add GOT builder and implement
 R_ARM_GOT_PREL relocations for ELF

---
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index eb51a4896ddcc66..111527a39e06ecd 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -679,12 +679,16 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
 }
 
 const uint8_t GOTEntryInit[] = {
-    0x00, 0x00, 0x00, 0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
 };
 
 /// Create a new node in the link-graph for the given pointer value.
 template <size_t Size>
-static Block &allocPointer(LinkGraph &G, Section &S, const uint8_t (&Content)[Size]) {
+static Block &allocPointer(LinkGraph &G, Section &S,
+                           const uint8_t (&Content)[Size]) {
   static_assert(Size == 4, "Pointers are 32-bit");
   constexpr uint64_t Alignment = 4;
   ArrayRef<char> Init(reinterpret_cast<const char *>(Content), Size);
@@ -710,9 +714,11 @@ bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) {
   default:
     return false;
   }
-  LLVM_DEBUG(dbgs() << "  Transforming " << G.getEdgeKindName(E.getKind()) << " edge at "
-            << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
-            << formatv("{0:x}", E.getOffset()) << ") into " << G.getEdgeKindName(KindToSet) << "\n");
+  LLVM_DEBUG(dbgs() << "  Transforming " << G.getEdgeKindName(E.getKind())
+                    << " edge at " << B->getFixupAddress(E) << " ("
+                    << B->getAddress() << " + "
+                    << formatv("{0:x}", E.getOffset()) << ") into "
+                    << G.getEdgeKindName(KindToSet) << "\n");
   E.setKind(KindToSet);
   E.setTarget(getEntryForTarget(G, E.getTarget()));
   return true;

>From e19bf04dd167179833093878d66779df683ed994 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:35:54 +0100
Subject: [PATCH 3/9] [llvm-jitlink] Allow optional stub-kind filter in
 stub_addr() expressions

---
 .../llvm/ExecutionEngine/RuntimeDyldChecker.h |  2 +-
 .../RuntimeDyld/RuntimeDyldChecker.cpp        | 27 ++++--
 .../RuntimeDyld/RuntimeDyldCheckerImpl.h      |  3 +-
 llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp  |  2 +-
 llvm/tools/llvm-jitlink/llvm-jitlink.cpp      | 86 +++++++++++++++++--
 llvm/tools/llvm-jitlink/llvm-jitlink.h        |  7 +-
 llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp        | 11 ++-
 7 files changed, 114 insertions(+), 24 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
index 80e4bbf494339cf..034c134a13731d3 100644
--- a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
+++ b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
@@ -154,7 +154,7 @@ class RuntimeDyldChecker {
   using GetSectionInfoFunction = std::function<Expected<MemoryRegionInfo>(
       StringRef FileName, StringRef SectionName)>;
   using GetStubInfoFunction = std::function<Expected<MemoryRegionInfo>(
-      StringRef StubContainer, StringRef TargetName)>;
+      StringRef StubContainer, StringRef TargetName, StringRef StubKindFilter)>;
   using GetGOTInfoFunction = std::function<Expected<MemoryRegionInfo>(
       StringRef GOTContainer, StringRef TargetName)>;
 
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
index 7fadbdd6a1fff2b..11fb21a9c1c0a70 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
@@ -400,6 +400,15 @@ class RuntimeDyldCheckerExprEval {
     StringRef Symbol;
     std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr);
 
+    // Parse optional parameter to filter by stub kind
+    StringRef KindNameFilter;
+    if (RemainingExpr.starts_with(",")) {
+      RemainingExpr = RemainingExpr.substr(1).ltrim();
+      size_t ClosingBracket = RemainingExpr.find(")");
+      KindNameFilter = RemainingExpr.substr(0, ClosingBracket);
+      RemainingExpr = RemainingExpr.substr(ClosingBracket);
+    }
+
     if (!RemainingExpr.starts_with(")"))
       return std::make_pair(
           unexpectedToken(RemainingExpr, Expr, "expected ')'"), "");
@@ -407,8 +416,9 @@ class RuntimeDyldCheckerExprEval {
 
     uint64_t StubAddr;
     std::string ErrorMsg;
-    std::tie(StubAddr, ErrorMsg) = Checker.getStubOrGOTAddrFor(
-        StubContainerName, Symbol, PCtx.IsInsideLoad, IsStubAddr);
+    std::tie(StubAddr, ErrorMsg) =
+        Checker.getStubOrGOTAddrFor(StubContainerName, Symbol, KindNameFilter,
+                                    PCtx.IsInsideLoad, IsStubAddr);
 
     if (ErrorMsg != "")
       return std::make_pair(EvalResult(ErrorMsg), "");
@@ -985,11 +995,14 @@ std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getSectionAddr(
 }
 
 std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getStubOrGOTAddrFor(
-    StringRef StubContainerName, StringRef SymbolName, bool IsInsideLoad,
-    bool IsStubAddr) const {
-
-  auto StubInfo = IsStubAddr ? GetStubInfo(StubContainerName, SymbolName)
-                             : GetGOTInfo(StubContainerName, SymbolName);
+    StringRef StubContainerName, StringRef SymbolName, StringRef StubKindFilter,
+    bool IsInsideLoad, bool IsStubAddr) const {
+
+  assert((StubKindFilter.empty() || IsStubAddr) &&
+         "Kind name filter only supported for stubs");
+  auto StubInfo =
+      IsStubAddr ? GetStubInfo(StubContainerName, SymbolName, StubKindFilter)
+                 : GetGOTInfo(StubContainerName, SymbolName);
 
   if (!StubInfo) {
     std::string ErrMsg;
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
index 9f44a9389f47344..bda554e9e5b673c 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h
@@ -64,7 +64,8 @@ class RuntimeDyldCheckerImpl {
 
   std::pair<uint64_t, std::string>
   getStubOrGOTAddrFor(StringRef StubContainerName, StringRef Symbol,
-                      bool IsInsideLoad, bool IsStubAddr) const;
+                      StringRef StubKindFilter, bool IsInsideLoad,
+                      bool IsStubAddr) const;
 
   std::optional<uint64_t> getSectionLoadAddress(void *LocalAddr) const;
 
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
index c6b4218aad7af86..1f0fca2202a0ef7 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
@@ -91,7 +91,7 @@ static Error registerSymbol(LinkGraph &G, Symbol &Sym, Session::FileInfo &FI,
   case Stubs:
     return FI.registerStubEntry(G, Sym, getELFStubTarget);
   case AArch32Stubs:
-    return FI.registerStubEntry(G, Sym, getELFAArch32StubTarget);
+    return FI.registerMultiStubEntry(G, Sym, getELFAArch32StubTarget);
   case Other:
     return Error::success();
   }
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index 8c18610313ce8f4..d233ebdb5a3a8dc 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -331,8 +331,12 @@ operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
     OS << "  Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
   for (auto &GOTKV : FI.GOTEntryInfos)
     OS << "  GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
-  for (auto &StubKV : FI.StubInfos)
-    OS << "  Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
+  for (auto &StubKVs : FI.StubInfos) {
+    OS << "  Stubs \"" << StubKVs.first() << "\":";
+    for (auto MemRegion : StubKVs.second)
+      OS << " " << MemRegion;
+    OS << "\n";
+  }
   return OS;
 }
 
@@ -1207,9 +1211,35 @@ Error Session::FileInfo::registerStubEntry(
   auto TS = GetSymbolTarget(G, Sym.getBlock());
   if (!TS)
     return TS.takeError();
-  StubInfos[TS->getName()] = {Sym.getSymbolContent(),
-                              Sym.getAddress().getValue(),
-                              Sym.getTargetFlags()};
+
+  SmallVector<MemoryRegionInfo> &Entry = StubInfos[TS->getName()];
+  Entry.insert(Entry.begin(),
+               {Sym.getSymbolContent(), Sym.getAddress().getValue(),
+                Sym.getTargetFlags()});
+  return Error::success();
+}
+
+Error Session::FileInfo::registerMultiStubEntry(
+    LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget) {
+  if (Sym.isSymbolZeroFill())
+    return make_error<StringError>("Unexpected zero-fill symbol in section " +
+                                       Sym.getBlock().getSection().getName(),
+                                   inconvertibleErrorCode());
+
+  auto Target = GetSymbolTarget(G, Sym.getBlock());
+  if (!Target)
+    return Target.takeError();
+
+  SmallVector<MemoryRegionInfo> &Entry = StubInfos[Target->getName()];
+  Entry.emplace_back(Sym.getSymbolContent(), Sym.getAddress().getValue(),
+                     Sym.getTargetFlags());
+
+  // Let's keep stubs ordered by ascending address.
+  std::sort(Entry.begin(), Entry.end(),
+            [](const MemoryRegionInfo &L, const MemoryRegionInfo &R) {
+              return L.getTargetAddress() < R.getTargetAddress();
+            });
+
   return Error::success();
 }
 
@@ -1235,8 +1265,14 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
   return SecInfoItr->second;
 }
 
+static StringRef detectStubKind(const Session::MemoryRegionInfo &Stub) {
+  // Implement acutal stub kind detection
+  return "";
+}
+
 Expected<Session::MemoryRegionInfo &>
-Session::findStubInfo(StringRef FileName, StringRef TargetName) {
+Session::findStubInfo(StringRef FileName, StringRef TargetName,
+                      StringRef KindNameFilter) {
   auto FI = findFileInfo(FileName);
   if (!FI)
     return FI.takeError();
@@ -1246,7 +1282,38 @@ Session::findStubInfo(StringRef FileName, StringRef TargetName) {
                                        "\" registered for file \"" + FileName +
                                        "\"",
                                    inconvertibleErrorCode());
-  return StubInfoItr->second;
+  auto &StubsForTarget = StubInfoItr->second;
+  assert(!StubsForTarget.empty() && "At least 1 stub in each entry");
+  if (KindNameFilter.empty() && StubsForTarget.size() == 1)
+    return StubsForTarget[0]; // Regular single-stub match
+
+  std::string KindsStr;
+  SmallVector<MemoryRegionInfo *, 1> Matches;
+  Regex KindNameMatcher(KindNameFilter.empty() ? ".*" : KindNameFilter);
+  for (MemoryRegionInfo &Stub : StubsForTarget) {
+    StringRef Kind = detectStubKind(Stub);
+    if (KindNameMatcher.match(Kind))
+      Matches.push_back(&Stub);
+    KindsStr += "\"" + (Kind.empty() ? "<unknown>" : Kind.str()) + "\", ";
+  }
+  if (Matches.empty())
+    return make_error<StringError>(
+        "\"" + TargetName + "\" has " + Twine(StubsForTarget.size()) +
+            " stubs in file \"" + FileName +
+            "\", but none of them matches the stub-kind filter \"" +
+            KindNameFilter + "\" (all encountered kinds are " +
+            StringRef(KindsStr.data(), KindsStr.size() - 2) + ").",
+        inconvertibleErrorCode());
+  if (Matches.size() > 1)
+    return make_error<StringError>(
+        "\"" + TargetName + "\" has " + Twine(Matches.size()) +
+            " candidate stubs in file \"" + FileName +
+            "\". Please refine stub-kind filter \"" + KindNameFilter +
+            "\" for disambiguation (encountered kinds are " +
+            StringRef(KindsStr.data(), KindsStr.size() - 2) + ").",
+        inconvertibleErrorCode());
+
+  return *Matches[0];
 }
 
 Expected<Session::MemoryRegionInfo &>
@@ -2015,8 +2082,9 @@ static Error runChecks(Session &S, Triple TT, SubtargetFeatures Features) {
     return S.findSectionInfo(FileName, SectionName);
   };
 
-  auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
-    return S.findStubInfo(FileName, SectionName);
+  auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName,
+                          StringRef KindNameFilter) {
+    return S.findStubInfo(FileName, SectionName, KindNameFilter);
   };
 
   auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h
index 93a00266b150436..e09c15adace20ec 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.h
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -49,7 +49,7 @@ struct Session {
 
   struct FileInfo {
     StringMap<MemoryRegionInfo> SectionInfos;
-    StringMap<MemoryRegionInfo> StubInfos;
+    StringMap<SmallVector<MemoryRegionInfo, 1>> StubInfos;
     StringMap<MemoryRegionInfo> GOTEntryInfos;
 
     using Symbol = jitlink::Symbol;
@@ -61,6 +61,8 @@ struct Session {
                            GetSymbolTargetFunction GetSymbolTarget);
     Error registerStubEntry(LinkGraph &G, Symbol &Sym,
                             GetSymbolTargetFunction GetSymbolTarget);
+    Error registerMultiStubEntry(LinkGraph &G, Symbol &Sym,
+                                 GetSymbolTargetFunction GetSymbolTarget);
   };
 
   using DynLibJDMap = std::map<std::string, orc::JITDylib *>;
@@ -74,7 +76,8 @@ struct Session {
   Expected<MemoryRegionInfo &> findSectionInfo(StringRef FileName,
                                                StringRef SectionName);
   Expected<MemoryRegionInfo &> findStubInfo(StringRef FileName,
-                                            StringRef TargetName);
+                                            StringRef TargetName,
+                                            StringRef KindNameFilter);
   Expected<MemoryRegionInfo &> findGOTEntryInfo(StringRef FileName,
                                                 StringRef TargetName);
 
diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 107b555a99faa40..4cb76f4347422b6 100644
--- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -926,7 +926,8 @@ static int linkAndVerify() {
   };
 
   auto GetStubInfo = [&Dyld, &StubMap](StringRef StubContainer,
-                                       StringRef SymbolName)
+                                       StringRef SymbolName,
+                                       StringRef KindNameFilter)
       -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
     if (!StubMap.count(StubContainer))
       return make_error<StringError>("Stub container not found: " +
@@ -947,6 +948,11 @@ static int linkAndVerify() {
     return StubMemInfo;
   };
 
+  auto GetGOTInfo = [&GetStubInfo](StringRef StubContainer,
+                                   StringRef SymbolName) {
+    return GetStubInfo(StubContainer, SymbolName, "");
+  };
+
   // We will initialize this below once we have the first object file and can
   // know the endianness.
   std::unique_ptr<RuntimeDyldChecker> Checker;
@@ -977,8 +983,7 @@ static int linkAndVerify() {
 
     if (!Checker)
       Checker = std::make_unique<RuntimeDyldChecker>(
-          IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo,
-          GetStubInfo,
+          IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
           Obj.isLittleEndian() ? llvm::endianness::little
                                : llvm::endianness::big,
           TheTriple, MCPU, SubtargetFeatures(), dbgs());

>From 559cf1218c87e304dcb474b7f1de5bc13d537247 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:58:36 +0100
Subject: [PATCH 4/9] [JITLink][AArch32] Multi-stub support for armv7/thumbv7

---
 .../llvm/ExecutionEngine/JITLink/aarch32.h    |  59 ++-------
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp  | 125 ++++++++++++++++--
 .../JITLink/AArch32/ELF_stubs_arm.s           |  53 ++++++++
 .../JITLink/AArch32/ELF_stubs_multi.s         |  50 +++++++
 llvm/tools/llvm-jitlink/llvm-jitlink.cpp      |  46 ++++++-
 5 files changed, 274 insertions(+), 59 deletions(-)
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 0968a093279bfbf..725f9a7eeb88896 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -333,64 +333,31 @@ class GOTBuilder : public TableManager<GOTBuilder> {
   Section *GOTSection = nullptr;
 };
 
-/// Stubs builder for v7 emits non-position-independent Thumb stubs.
-///
-/// Right now we only have one default stub kind, but we want to extend this
-/// and allow creation of specific kinds in the future (e.g. branch range
-/// extension or interworking).
-///
-/// Let's keep it simple for the moment and not wire this through a GOT.
-///
-class StubsManager_v7 : public TableManager<StubsManager_v7> {
+/// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs.
+class StubsManager_v7 {
 public:
   StubsManager_v7() = default;
 
   /// Name of the object file section that will contain all our stubs.
   static StringRef getSectionName() {
-    return "__llvm_jitlink_aarch32_STUBS_Thumbv7";
+    return "__llvm_jitlink_aarch32_STUBS_v7";
   }
 
   /// Implements link-graph traversal via visitExistingEdges().
-  bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
-    if (E.getTarget().isDefined())
-      return false;
-
-    switch (E.getKind()) {
-    case Thumb_Call:
-    case Thumb_Jump24: {
-      DEBUG_WITH_TYPE("jitlink", {
-        dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
-               << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
-               << formatv("{0:x}", E.getOffset()) << ")\n";
-      });
-      E.setTarget(this->getEntryForTarget(G, E.getTarget()));
-      return true;
-    }
-    }
-    return false;
-  }
-
-  /// Create a branch range extension stub with Thumb encoding for v7 CPUs.
-  Symbol &createEntry(LinkGraph &G, Symbol &Target);
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E);
 
 private:
-  /// Create a new node in the link-graph for the given stub template.
-  template <size_t Size>
-  Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size],
-                 uint64_t Alignment) {
-    ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
-    return G.createContentBlock(getStubsSection(G), Template,
-                                orc::ExecutorAddr(), Alignment, 0);
-  }
-
-  /// Get or create the object file section that will contain all our stubs.
-  Section &getStubsSection(LinkGraph &G) {
-    if (!StubsSection)
-      StubsSection = &G.createSection(getSectionName(),
-                                      orc::MemProt::Read | orc::MemProt::Exec);
-    return *StubsSection;
+  // Two slots per external: Arm and Thumb
+  using StubMapEntry = std::tuple<Symbol *, Symbol *>;
+
+  Symbol *&getStubSymbolSlot(StringRef Name, bool Thumb) {
+    StubMapEntry &Stubs = StubMap.try_emplace(Name).first->second;
+    if (Thumb)
+      return std::get<1>(Stubs);
+    return std::get<0>(Stubs);
   }
 
+  DenseMap<StringRef, StubMapEntry> StubMap;
   Section *StubsSection = nullptr;
 };
 
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 111527a39e06ecd..7f7b460b4b36968 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/ManagedStatic.h"
@@ -724,27 +725,127 @@ bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) {
   return true;
 }
 
+const uint8_t Armv7ABS[] = {
+    0x00, 0xc0, 0x00, 0xe3, // movw r12, #0x0000     ; lower 16-bit
+    0x00, 0xc0, 0x40, 0xe3, // movt r12, #0x0000     ; upper 16-bit
+    0x1c, 0xff, 0x2f, 0xe1  // bx   r12
+};
+
 const uint8_t Thumbv7ABS[] = {
     0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000    ; lower 16-bit
     0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000    ; upper 16-bit
     0x60, 0x47              // bx   r12
 };
 
-Symbol &StubsManager_v7::createEntry(LinkGraph &G, Symbol &Target) {
+/// Create a new node in the link-graph for the given stub template.
+template <size_t Size>
+static Block &allocStub(LinkGraph &G, Section &S, const uint8_t (&Code)[Size]) {
   constexpr uint64_t Alignment = 4;
-  Block &B = addStub(G, Thumbv7ABS, Alignment);
-  LLVM_DEBUG({
-    const char *StubPtr = B.getContent().data();
-    HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
-    assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
-           checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
-           "Linker generated stubs may only corrupt register r12 (IP)");
-  });
+  ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
+  return G.createContentBlock(S, Template, orc::ExecutorAddr(), Alignment, 0);
+}
+
+static Block &createStubThumbv7(LinkGraph &G, Section &S, Symbol &Target) {
+  Block &B = allocStub(G, S, Thumbv7ABS);
   B.addEdge(Thumb_MovwAbsNC, 0, Target, 0);
   B.addEdge(Thumb_MovtAbs, 4, Target, 0);
-  Symbol &Stub = G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
-  Stub.setTargetFlags(ThumbSymbol);
-  return Stub;
+
+  [[maybe_unused]] const char *StubPtr = B.getContent().data();
+  [[maybe_unused]] HalfWords Reg12 = encodeRegMovtT1MovwT3(12);
+  assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) &&
+         checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) &&
+         "Linker generated stubs may only corrupt register r12 (IP)");
+  return B;
+}
+
+static Block &createStubArmv7(LinkGraph &G, Section &S, Symbol &Target) {
+  Block &B = allocStub(G, S, Armv7ABS);
+  B.addEdge(Arm_MovwAbsNC, 0, Target, 0);
+  B.addEdge(Arm_MovtAbs, 4, Target, 0);
+
+  [[maybe_unused]] const char *StubPtr = B.getContent().data();
+  [[maybe_unused]] uint32_t Reg12 = encodeRegMovtA1MovwA2(12);
+  assert(checkRegister<Arm_MovwAbsNC>(StubPtr, Reg12) &&
+         checkRegister<Arm_MovtAbs>(StubPtr + 4, Reg12) &&
+         "Linker generated stubs may only corrupt register r12 (IP)");
+  return B;
+}
+
+static bool needsStub(const Edge &E) {
+  Symbol &Target = E.getTarget();
+
+  // Create stubs for external branch targets.
+  if (!Target.isDefined()) {
+    switch (E.getKind()) {
+    case Arm_Call:
+    case Arm_Jump24:
+    case Thumb_Call:
+    case Thumb_Jump24:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  // For local targets, create interworking stubs if we switch Arm/Thumb with an
+  // instruction that cannot switch the instruction set state natively.
+  bool TargetIsThumb = Target.getTargetFlags() & ThumbSymbol;
+  switch (E.getKind()) {
+  case Arm_Jump24:
+    return TargetIsThumb; // Branch to Thumb needs interworking stub
+  case Thumb_Jump24:
+    return !TargetIsThumb; // Branch to Arm needs interworking stub
+  default:
+    break;
+  }
+
+  return false;
+}
+
+bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) {
+  if (!needsStub(E))
+    return false;
+
+  // Stub Arm/Thumb follows instruction set state at relocation site.
+  // TODO: We may reduce them at relaxation time and reuse freed slots.
+  bool MakeThumb = (E.getKind() > LastArmRelocation);
+  LLVM_DEBUG(dbgs() << "  Preparing " << (MakeThumb ? "Thumb" : "Arm")
+                    << " stub for " << G.getEdgeKindName(E.getKind())
+                    << " edge at " << B->getFixupAddress(E) << " ("
+                    << B->getAddress() << " + "
+                    << formatv("{0:x}", E.getOffset()) << ")\n");
+
+  Symbol &Target = E.getTarget();
+  assert(Target.hasName() && "Edge cannot point to anonymous target");
+  Symbol *&StubSymbol = getStubSymbolSlot(Target.getName(), MakeThumb);
+
+  if (!StubSymbol) {
+    if (!StubsSection)
+      StubsSection = &G.createSection(getSectionName(),
+                                      orc::MemProt::Read | orc::MemProt::Exec);
+    Block &B = MakeThumb ? createStubThumbv7(G, *StubsSection, Target)
+                         : createStubArmv7(G, *StubsSection, Target);
+    StubSymbol = &G.addAnonymousSymbol(B, 0, B.getSize(), true, false);
+    if (MakeThumb)
+      StubSymbol->setTargetFlags(ThumbSymbol);
+
+    LLVM_DEBUG({
+      dbgs() << "    Created " << (MakeThumb ? "Thumb" : "Arm") << " entry for "
+             << Target.getName() << " in " << StubsSection->getName() << ": "
+             << *StubSymbol << "\n";
+    });
+  }
+
+  assert(MakeThumb == (StubSymbol->getTargetFlags() & ThumbSymbol) &&
+         "Instruction set states of stub and relocation site should be equal");
+  LLVM_DEBUG({
+    dbgs() << "    Using " << (MakeThumb ? "Thumb" : "Arm") << " entry "
+           << *StubSymbol << " in "
+           << StubSymbol->getBlock().getSection().getName() << "\n";
+  });
+
+  E.setTarget(*StubSymbol);
+  return true;
 }
 
 const char *getEdgeKindName(Edge::Kind K) {
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
new file mode 100644
index 000000000000000..fb2e0eb2c0bf249
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
@@ -0,0 +1,53 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \
+# RUN:         -filetype=obj -o %t/out.o %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
+# RUN:              -slab-allocate 10Kb -slab-page-size 4096 \
+# RUN:              -abs ext=0x76bbe880 \
+# RUN:              -check %s %t/out.o
+
+	.text
+	.syntax unified
+
+# Check that calls/jumps to external functions trigger the generation of
+# branch-range extension stubs. These stubs don't follow the default PLT model
+# where the branch-target address is loaded from a GOT entry. Instead, they
+# hard-code it in the immediate field.
+
+# The external function ext will return to the caller directly.
+# jitlink-check: decode_operand(test_arm_jump, 0) = stub_addr(out.o, ext) - (test_arm_jump + 8)
+	.globl	test_arm_jump
+	.type	test_arm_jump,%function
+	.p2align	2
+test_arm_jump:
+	b	ext
+	.size	test_arm_jump, .-test_arm_jump
+
+# The branch-with-link sets the LR register so that the external function ext
+# returns to us. We have to save the register (push) and return to main manually
+# (pop). This adds the +4 offset for the bl instruction we decode:
+# jitlink-check: decode_operand(test_arm_call + 4, 0) = stub_addr(out.o, ext) - (test_arm_call + 8) - 4
+	.globl  test_arm_call
+	.type	test_arm_call,%function
+	.p2align	2
+test_arm_call:
+	push	{lr}
+	bl	ext
+	pop	{pc}
+	.size	test_arm_call, .-test_arm_call
+
+# This test is executable with both, Arm and Thumb `ext` functions. It only has
+# to return with `bx lr`. For example:
+#   > echo "void ext() {}" | clang -target armv7-linux-gnueabihf -o ext-arm.o -c -xc -
+#   > llvm-jitlink ext-arm.o out.o
+#
+	.globl	main
+	.type	main,%function
+	.p2align	2
+main:
+	push	{lr}
+	bl	test_arm_call
+	bl	test_arm_jump
+	movw	r0, #0
+	pop	{pc}
+	.size	main, .-main
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s
new file mode 100644
index 000000000000000..d575f114dcba1e7
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s
@@ -0,0 +1,50 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \
+# RUN:         -filetype=obj -o %t/out.o %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
+# RUN:              -slab-allocate=10Kb -slab-page-size=4096 \
+# RUN:              -abs ext=0x76bbe880 -check %s %t/out.o
+
+	.text
+	.syntax unified
+
+# Check that a single external symbol can have multiple stubs. We access them
+# with the extra stub-index argument to stub_addr(). Stubs are sorted by
+# ascending size (because the default memory manager lays out blocks by size).
+
+# Thumb relocation site emits thumb stub
+# jitlink-check: decode_operand(test_stub_thumb, 0) = stub_addr(out.o, ext, thumb) - (test_stub_thumb + 4)
+	.globl  test_stub_thumb
+	.type	test_stub_thumb,%function
+	.p2align	1
+	.code	16
+	.thumb_func
+test_stub_thumb:
+	b	ext
+	.size	test_stub_thumb, .-test_stub_thumb
+
+# Arm relocation site emits arm stub
+# jitlink-check: decode_operand(test_stub_arm, 0) = stub_addr(out.o, ext, arm) - (test_stub_arm + 8)
+	.globl  test_stub_arm
+	.type	test_stub_arm,%function
+	.p2align	2
+	.code	32
+test_stub_arm:
+	b	ext
+	.size	test_stub_arm, .-test_stub_arm
+
+# This test is executable with both, Arm and Thumb `ext` functions. It only has
+# to return (directly to main) with `bx lr`. For example:
+#   > echo "void ext() {}" | clang -target armv7-linux-gnueabihf -o ext-arm.o -c -xc -
+#   > llvm-jitlink ext-arm.o out.o
+#
+	.globl	main
+	.type	main,%function
+	.p2align	2
+main:
+	push	{lr}
+	bl	test_stub_arm
+	bl	test_stub_thumb
+	movw	r0, #0
+	pop	{pc}
+	.size	main, .-main
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index d233ebdb5a3a8dc..c0554108818f68b 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -1265,8 +1265,52 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
   return SecInfoItr->second;
 }
 
+class MemoryMatcher {
+public:
+  MemoryMatcher(ArrayRef<char> Content)
+      : Pos(Content.data()), End(Pos + Content.size()) {}
+
+  template <typename MaskType> bool matchMask(MaskType Mask) {
+    if (Mask == (Mask & *reinterpret_cast<const MaskType *>(Pos))) {
+      Pos += sizeof(MaskType);
+      return true;
+    }
+    return false;
+  }
+
+  template <typename ValueType> bool matchEqual(ValueType Value) {
+    if (Value == *reinterpret_cast<const ValueType *>(Pos)) {
+      Pos += sizeof(ValueType);
+      return true;
+    }
+    return false;
+  }
+
+  bool done() const { return Pos == End; }
+
+private:
+  const char *Pos;
+  const char *End;
+};
+
 static StringRef detectStubKind(const Session::MemoryRegionInfo &Stub) {
-  // Implement acutal stub kind detection
+  constexpr uint32_t Armv7MovWTle = 0xe300c000;
+  constexpr uint32_t Armv7BxR12le = 0xe12fff1c;
+  constexpr uint32_t Thumbv7MovWTle = 0x0c00f240;
+  constexpr uint16_t Thumbv7BxR12le = 0x4760;
+
+  MemoryMatcher M(Stub.getContent());
+  if (M.matchMask(Thumbv7MovWTle)) {
+    if (M.matchMask(Thumbv7MovWTle))
+      if (M.matchEqual(Thumbv7BxR12le))
+        if (M.done())
+          return "thumbv7_abs_le";
+  } else if (M.matchMask(Armv7MovWTle)) {
+    if (M.matchMask(Armv7MovWTle))
+      if (M.matchEqual(Armv7BxR12le))
+        if (M.done())
+          return "armv7_abs_le";
+  }
   return "";
 }
 

>From 7215c81f693ec1caaac5006e1d269623a2068bc6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 20 Jan 2024 11:13:45 +0100
Subject: [PATCH 5/9] [clang-repl] Fix linker error on ARM

Error in gold linker is: PLT offset too large, try linking with --long-plt
---
 clang/tools/clang-repl/CMakeLists.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index 2ccbe292fd49e09..8589e37418354e7 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -22,3 +22,7 @@ clang_target_link_libraries(clang-repl PRIVATE
 if(CLANG_PLUGIN_SUPPORT)
   export_executable_symbols_for_plugins(clang-repl)
 endif()
+
+if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM")
+  target_link_options(clang-repl PRIVATE LINKER:--long-plt)
+endif()

>From e1ce5b6c12f8c85c641d99ecc94a7ae02e4ce4f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 20 Jan 2024 16:12:46 +0100
Subject: [PATCH 6/9] [JITLink][AArch32] Implement R_ARM_TARGET1

Prepare a configuration switch anddefault to R_ARM_ABS32
---
 llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h     | 2 ++
 llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp        | 8 ++++++--
 llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp | 7 ++++---
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 725f9a7eeb88896..98525b9f69b97e2 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -135,6 +135,8 @@ enum class StubsFlavor {
 struct ArmConfig {
   bool J1J2BranchEncoding = false;
   StubsFlavor Stubs = StubsFlavor::Unsupported;
+  // We might want a --target1-rel linker switch in the long term.
+  bool Target1Rel = false;
 };
 
 /// Obtain the sub-arch configuration for a given Arm CPU model.
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index 2553cd70a576912..ed7d58da2dc111e 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -31,7 +31,8 @@ namespace llvm {
 namespace jitlink {
 
 /// Translate from ELF relocation type to JITLink-internal edge kind.
-Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
+Expected<aarch32::EdgeKind_aarch32>
+getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) {
   switch (ELFType) {
   case ELF::R_ARM_ABS32:
     return aarch32::Data_Pointer32;
@@ -47,6 +48,9 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
     return aarch32::Arm_MovwAbsNC;
   case ELF::R_ARM_MOVT_ABS:
     return aarch32::Arm_MovtAbs;
+  case ELF::R_ARM_TARGET1:
+    return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32
+                               : aarch32::Data_Pointer32;
   case ELF::R_ARM_THM_CALL:
     return aarch32::Thumb_Call;
   case ELF::R_ARM_THM_JUMP24:
@@ -171,7 +175,7 @@ class ELFLinkGraphBuilder_aarch32
           inconvertibleErrorCode());
 
     uint32_t Type = Rel.getType(false);
-    Expected<aarch32::EdgeKind_aarch32> Kind = getJITLinkEdgeKind(Type);
+    Expected<aarch32::EdgeKind_aarch32> Kind = getJITLinkEdgeKind(Type, ArmCfg);
     if (!Kind)
       return Kind.takeError();
 
diff --git a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
index 26c773b8dc3a715..46792e9e397b6ae 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
@@ -39,7 +39,7 @@ struct MutableWord {
 namespace llvm {
 namespace jitlink {
 
-Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType);
+Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &Cfg);
 Expected<uint32_t> getELFRelocationType(Edge::Kind Kind);
 
 } // namespace jitlink
@@ -47,7 +47,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind);
 
 TEST(AArch32_ELF, EdgeKinds) {
   // Fails: Invalid ELF type -> JITLink kind
-  Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_NONE);
+  aarch32::ArmConfig Cfg;
+  Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_NONE, Cfg);
   EXPECT_TRUE(errorToBool(ErrKind.takeError()));
 
   // Fails: Invalid JITLink kind -> ELF type
@@ -59,7 +60,7 @@ TEST(AArch32_ELF, EdgeKinds) {
     EXPECT_FALSE(errorToBool(ELFType.takeError()))
         << "Failed to translate JITLink kind -> ELF type";
 
-    Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType);
+    Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType, Cfg);
     EXPECT_FALSE(errorToBool(JITLinkKind.takeError()))
         << "Failed to translate ELF type -> JITLink kind";
 

>From 14ec373340abce8655f59e85cc1dfe3c37b13a61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 20 Jan 2024 18:06:14 +0100
Subject: [PATCH 7/9] [JITLink][AArch32] Implement R_ARM_NONE

---
 llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h | 12 +++++++++---
 llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp    |  4 ++++
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp        |  1 +
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 98525b9f69b97e2..7be0380d4a7c3e7 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -101,7 +101,11 @@ enum EdgeKind_aarch32 : Edge::Kind {
   Thumb_MovtPrel,
 
   LastThumbRelocation = Thumb_MovtPrel,
-  LastRelocation = LastThumbRelocation,
+
+  /// No-op relocation
+  None,
+
+  LastRelocation = None,
 };
 
 /// Flags enum for AArch32-specific symbol properties
@@ -293,7 +297,8 @@ inline Expected<int64_t> readAddend(LinkGraph &G, Block &B,
   if (Kind <= LastThumbRelocation)
     return readAddendThumb(G, B, Offset, Kind, ArmCfg);
 
-  llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
+  assert(Kind == None && "Not associated with a relocation class");
+  return 0;
 }
 
 /// Helper function to apply the fixup for Data-class relocations.
@@ -320,7 +325,8 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
   if (Kind <= LastThumbRelocation)
     return applyFixupThumb(G, B, E, ArmCfg);
 
-  llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
+  assert(Kind == None && "Not associated with a relocation class");
+  return Error::success();
 }
 
 /// Populate a Global Offset Table from edges that request it.
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index ed7d58da2dc111e..15c209e1ebe5bf6 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -48,6 +48,8 @@ getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) {
     return aarch32::Arm_MovwAbsNC;
   case ELF::R_ARM_MOVT_ABS:
     return aarch32::Arm_MovtAbs;
+  case ELF::R_ARM_NONE:
+    return aarch32::None;
   case ELF::R_ARM_TARGET1:
     return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32
                                : aarch32::Data_Pointer32;
@@ -99,6 +101,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
     return ELF::R_ARM_THM_MOVW_PREL_NC;
   case aarch32::Thumb_MovtPrel:
     return ELF::R_ARM_THM_MOVT_PREL;
+  case aarch32::None:
+    return ELF::R_ARM_NONE;
   }
 
   return make_error<JITLinkError>(formatv("Invalid aarch32 edge {0:d}: ",
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 7f7b460b4b36968..9508cde07b42a65 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -867,6 +867,7 @@ const char *getEdgeKindName(Edge::Kind K) {
     KIND_NAME_CASE(Thumb_MovtAbs)
     KIND_NAME_CASE(Thumb_MovwPrelNC)
     KIND_NAME_CASE(Thumb_MovtPrel)
+    KIND_NAME_CASE(None)
   default:
     return getGenericEdgeKindName(K);
   }

>From 1fc8d948dd7728810d1ff579a4e0ef087a1b3519 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 20 Jan 2024 16:00:51 +0100
Subject: [PATCH 8/9] [JITLink][AArch32] Implement R_ARM_PREL31 and stop
 skipping .ARM.exidx sections (wip)

---
 .../llvm/ExecutionEngine/JITLink/aarch32.h    |  2 ++
 .../ExecutionEngine/JITLink/ELF_aarch32.cpp   | 13 ++++++++
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp  | 21 +++++++++++--
 .../JITLink/AArch32/ELF_relocations_data.s    | 30 +++++++++++++++++++
 4 files changed, 63 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 7be0380d4a7c3e7..dc75394c7283672 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -41,6 +41,8 @@ enum EdgeKind_aarch32 : Edge::Kind {
   /// Absolute 32-bit value relocation
   Data_Pointer32,
 
+  Data_PRel31,
+
   /// Create GOT entry and store offset
   Data_RequestGOTAndTransformToDelta32,
 
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index 15c209e1ebe5bf6..563dc498d7737eb 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -50,6 +50,8 @@ getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) {
     return aarch32::Arm_MovtAbs;
   case ELF::R_ARM_NONE:
     return aarch32::None;
+  case ELF::R_ARM_PREL31:
+    return aarch32::Data_PRel31;
   case ELF::R_ARM_TARGET1:
     return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32
                                : aarch32::Data_Pointer32;
@@ -79,6 +81,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
     return ELF::R_ARM_REL32;
   case aarch32::Data_Pointer32:
     return ELF::R_ARM_ABS32;
+  case aarch32::Data_PRel31:
+    return ELF::R_ARM_PREL31;
   case aarch32::Data_RequestGOTAndTransformToDelta32:
     return ELF::R_ARM_GOT_PREL;
   case aarch32::Arm_Call:
@@ -147,6 +151,15 @@ class ELFLinkGraphBuilder_aarch32
     // Handling ABI for the Arm® Architecture -> Index table entries
     if (Sect.sh_type == ELF::SHT_ARM_EXIDX)
       return true;
+    // Skip .ARM.extab sections. They are only ever referenced from .ARM.exidx,
+    // i.e. when unwind instructions don't fit into 4 bytes.
+    if (Sect.sh_type == ELF::SHT_PROGBITS && (Sect.sh_flags & ELF::SHF_ALLOC)) {
+      if (Sect.sh_name && Sect.sh_name < Base::SectionStringTab.size()) {
+        StringRef SectionName = Base::SectionStringTab.data() + Sect.sh_name;
+        if (SectionName.starts_with(".ARM.extab"))
+          return true;
+      }
+    }
     return false;
   }
 
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 9508cde07b42a65..96dff7656f17d63 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -398,6 +398,8 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset,
   case Data_Pointer32:
   case Data_RequestGOTAndTransformToDelta32:
     return SignExtend64<32>(support::endian::read32(FixupPtr, Endian));
+  case Data_PRel31:
+    return SignExtend64<31>(support::endian::read32(FixupPtr, Endian));
   default:
     return make_error<JITLinkError>(
         "In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -472,9 +474,8 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
   Symbol &TargetSymbol = E.getTarget();
   uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
 
-  // Regular data relocations have size 4, alignment 1 and write the full 32-bit
-  // result to the place; no need for overflow checking. There are three
-  // exceptions: R_ARM_ABS8, R_ARM_ABS16, R_ARM_PREL31
+  // Data relocations have alignment 1, size 4 (except R_ARM_ABS8 and
+  // R_ARM_ABS16) and write the full 32-bit result (except R_ARM_PREL31).
   switch (Kind) {
   case Data_Delta32: {
     int64_t Value = TargetAddress - FixupAddress + Addend;
@@ -496,6 +497,19 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
       endian::write32be(FixupPtr, Value);
     return Error::success();
   }
+  case Data_PRel31: {
+    int64_t Value = TargetAddress - FixupAddress + Addend;
+    if (!isInt<31>(Value))
+      return makeTargetOutOfRangeError(G, B, E);
+    if (LLVM_LIKELY(G.getEndianness() == endianness::little)) {
+      uint32_t MSB = endian::read32le(FixupPtr) & 0x80000000;
+      endian::write32le(FixupPtr, MSB | (Value & ~0x80000000));
+    } else {
+      uint32_t MSB = endian::read32be(FixupPtr) & 0x80000000;
+      endian::write32be(FixupPtr, MSB | (Value & ~0x80000000));
+    }
+    return Error::success();
+  }
   case Data_RequestGOTAndTransformToDelta32:
     llvm_unreachable("Should be transformed");
   default:
@@ -856,6 +870,7 @@ const char *getEdgeKindName(Edge::Kind K) {
   switch (K) {
     KIND_NAME_CASE(Data_Delta32)
     KIND_NAME_CASE(Data_Pointer32)
+    KIND_NAME_CASE(Data_PRel31)
     KIND_NAME_CASE(Data_RequestGOTAndTransformToDelta32)
     KIND_NAME_CASE(Arm_Call)
     KIND_NAME_CASE(Arm_Jump24)
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
index f91a4733c40eee0..82092965d5bb307 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
@@ -57,6 +57,35 @@ got_prel_offset:
 	.size	got_prel_offset, .-got_prel_offset
 	.size	got_prel, .-got_prel
 
+# Dummy personality routine. It will be referenced from .ARM.exidx
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_NONE               __aeabi_unwind_cpp_pr0
+	.globl __aeabi_unwind_cpp_pr0
+	.type __aeabi_unwind_cpp_pr0,%function
+	.align 2
+__aeabi_unwind_cpp_pr0:
+	bx lr
+
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_PREL31 .text
+#
+# Used in the .ARM.exidx exception tables. The linker must preserve the most-
+# significant bit. It denotes whether the field is an inline entry (1) or a
+# relocation (0).
+#
+# jitlink-check: *{4}(section_addr(out.o, .ARM.exidx)) = (prel31 - (section_addr(out.o, .ARM.exidx))) & 0x7fffffff
+	.globl  prel31
+	.type	prel31,%function
+	.align	2
+prel31:
+	.fnstart
+	.save   {r11, lr}
+	push	{r11, lr}
+	.setfp  r11, sp
+	mov	r11, sp
+	pop	{r11, lr}
+	mov	pc, lr
+	.size	prel31,.-prel31
+	.fnend
+
 # This test is executable with any 4-byte external target:
 #  > echo "unsigned target = 42;" | clang -target armv7-linux-gnueabihf -o target.o -c -xc -
 #  > llvm-jitlink target.o armv7/out.o
@@ -67,5 +96,6 @@ got_prel_offset:
 main:
 	push	{lr}
 	bl	got_prel
+	bl	prel31
 	pop	{pc}
 	.size   main, .-main

>From b7431fc5f62147e48261e5d0fcfa7c390efb855c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Mon, 8 Jan 2024 15:06:33 +0100
Subject: [PATCH 9/9] [Orc] Make JITLink default in LLJIT for ELF-based ARM
 targets

---
 llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index e259c393d07e031..833dcb9d5bf2e72 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -734,6 +734,12 @@ Error LLJITBuilderState::prepareForConstruction() {
     case Triple::aarch64:
       UseJITLink = !TT.isOSBinFormatCOFF();
       break;
+    case Triple::arm:
+    case Triple::armeb:
+    case Triple::thumb:
+    case Triple::thumbeb:
+      UseJITLink = TT.isOSBinFormatELF();
+      break;
     case Triple::x86_64:
       UseJITLink = !TT.isOSBinFormatCOFF();
       break;



More information about the cfe-commits mailing list