[llvm] [JITLink] Support AArch64 big-endian (BE8) ELF objects (PR #225120)

dong jianqiang via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 07:40:26 PDT 2026


https://github.com/dongjianqiang2 updated https://github.com/llvm/llvm-project/pull/225120

>From 9d286fdebbd24ecb2dcccc12c911c034c98b685e Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Tue, 16 Jun 2026 15:36:03 +0800
Subject: [PATCH 1/3] [JITLink] Support AArch64 big-endian (BE8) ELF objects

The AArch64 ELF JITLink backend hard-coded ELF64LE, so aarch64_be
objects failed to load ("Only AArch64 (little endian) is supported
for now").

- Dispatch aarch64_be to link_ELF_aarch64 and replace the ELF64LE cast
  in createLinkGraphFromELFObject_aarch64 with dyn_cast-based
  endianness detection to handle both ELF64LE and ELF64BE.
- Apply target endianness to data fixups (Pointer64, Pointer32,
  Delta32/64, NegDelta32/64) in aarch64::applyFixup. Instructions are
  always little-endian encoded (the A64 ISA is word-invariant), so
  instruction fixups keep writing LE in BE8 mode.
---
 .../llvm/ExecutionEngine/JITLink/aarch64.h    | 37 +++++++++++++--
 llvm/lib/ExecutionEngine/JITLink/ELF.cpp      |  1 +
 .../ExecutionEngine/JITLink/ELF_aarch64.cpp   | 26 +++++++---
 .../JITLink/AArch64/ELF_aarch64_be.s          | 47 +++++++++++++++++++
 4 files changed, 100 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
index 12bcdd1a79d80..4b737f0828e20 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
@@ -501,17 +501,46 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
   char *FixupPtr = BlockWorkingMem + E.getOffset();
   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
 
+  // AArch64 BE8: data values follow target endianness, but instructions are
+  // always encoded in LE byte order (the A64 ISA is word-invariant).
+  bool IsBE = !G.getTargetTriple().isLittleEndian();
+
+  auto writeData64 = [IsBE](char *Ptr, uint64_t Val) {
+    if (IsBE)
+      *(ubig64_t *)Ptr = Val;
+    else
+      *(ulittle64_t *)Ptr = Val;
+  };
+  auto writeData32 = [IsBE](char *Ptr, uint32_t Val) {
+    if (IsBE)
+      *(ubig32_t *)Ptr = Val;
+    else
+      *(ulittle32_t *)Ptr = Val;
+  };
+  auto writeDataS32 = [IsBE](char *Ptr, int32_t Val) {
+    if (IsBE)
+      *(big32_t *)Ptr = Val;
+    else
+      *(little32_t *)Ptr = Val;
+  };
+  auto writeDataS64 = [IsBE](char *Ptr, int64_t Val) {
+    if (IsBE)
+      *(big64_t *)Ptr = Val;
+    else
+      *(little64_t *)Ptr = Val;
+  };
+
   switch (E.getKind()) {
   case Pointer64: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
-    *(ulittle64_t *)FixupPtr = Value;
+    writeData64(FixupPtr, Value);
     break;
   }
   case Pointer32: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
     if (Value > std::numeric_limits<uint32_t>::max())
       return makeTargetOutOfRangeError(G, B, E);
-    *(ulittle32_t *)FixupPtr = Value;
+    writeData32(FixupPtr, Value);
     break;
   }
   case Delta32:
@@ -528,9 +557,9 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
       if (Value < std::numeric_limits<int32_t>::min() ||
           Value > std::numeric_limits<int32_t>::max())
         return makeTargetOutOfRangeError(G, B, E);
-      *(little32_t *)FixupPtr = Value;
+      writeDataS32(FixupPtr, Value);
     } else
-      *(little64_t *)FixupPtr = Value;
+      writeDataS64(FixupPtr, Value);
     break;
   }
   case Branch26PCRel: {
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
index 494466297aefe..d568fbc71632b 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
@@ -119,6 +119,7 @@ void link_ELF(std::unique_ptr<LinkGraph> G,
               std::unique_ptr<JITLinkContext> Ctx) {
   switch (G->getTargetTriple().getArch()) {
   case Triple::aarch64:
+  case Triple::aarch64_be:
     link_ELF_aarch64(std::move(G), std::move(Ctx));
     return;
   case Triple::arm:
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
index cd2eed748acde..b4e2a6773dde5 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
@@ -237,6 +237,8 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
   Error addSingleRelocation(const typename ELFT::Rela &Rel,
                             const typename ELFT::Shdr &FixupSect,
                             Block &BlockToFix) {
+    // AArch64 BE8: instructions are always LE-encoded regardless of the ELF
+    // data endianness, so instruction words are read as ulittle32_t below.
     using support::ulittle32_t;
     using Base = ELFLinkGraphBuilder<ELFT>;
 
@@ -687,14 +689,24 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64(
   if (!Features)
     return Features.takeError();
 
-  assert((*ELFObj)->getArch() == Triple::aarch64 &&
-         "Only AArch64 (little endian) is supported for now");
+  assert(((*ELFObj)->getArch() == Triple::aarch64 ||
+          (*ELFObj)->getArch() == Triple::aarch64_be) &&
+         "Object is not an AArch64 ELF file");
 
-  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
-  return ELFLinkGraphBuilder_aarch64<object::ELF64LE>(
-             (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), std::move(SSP),
-             (*ELFObj)->makeTriple(), std::move(*Features))
-      .buildGraph();
+  if (auto *E = dyn_cast<object::ELFObjectFile<object::ELF64LE>>(&**ELFObj))
+    return ELFLinkGraphBuilder_aarch64<object::ELF64LE>(
+               (*ELFObj)->getFileName(), E->getELFFile(), std::move(SSP),
+               (*ELFObj)->makeTriple(), std::move(*Features))
+        .buildGraph();
+
+  if (auto *E = dyn_cast<object::ELFObjectFile<object::ELF64BE>>(&**ELFObj))
+    return ELFLinkGraphBuilder_aarch64<object::ELF64BE>(
+               (*ELFObj)->getFileName(), E->getELFFile(), std::move(SSP),
+               (*ELFObj)->makeTriple(), std::move(*Features))
+        .buildGraph();
+
+  return make_error<JITLinkError>("Unsupported AArch64 ELF format in " +
+                                  ObjectBuffer.getBufferIdentifier());
 }
 
 void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s
new file mode 100644
index 0000000000000..bd3401cb0c45a
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s
@@ -0,0 +1,47 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=aarch64_be-unknown-linux-gnu -filetype=obj \
+# RUN:   -o %t/elf_aarch64_be.o %s
+# RUN: llvm-jitlink -noexec -abs external_data=0xdeadbeef \
+# RUN:   -check %s %t/elf_aarch64_be.o
+
+# Test that AArch64 big-endian (BE8) objects link: data fixups are written
+# in target (big) endianness while instruction fixups stay little-endian
+# (the A64 ISA is word-invariant, so instructions are always LE-encoded).
+
+	.text
+	.globl	main
+	.p2align	2
+	.type	main, at function
+main:
+	ret
+	.size	main, .-main
+
+# Instruction fixup (R_AARCH64_CALL26): the BL immediate is patched into an
+# LE-encoded instruction word, even in a big-endian object.
+# jitlink-check: decode_operand(local_func_call26, 0)[25:0] = (local_func - local_func_call26)[27:2]
+	.globl	local_func
+	.p2align	2
+	.type	local_func, at function
+local_func:
+	ret
+	.size	local_func, .-local_func
+
+	.globl	local_func_call26
+	.p2align	2
+local_func_call26:
+	bl	local_func
+	.size	local_func_call26, .-local_func_call26
+
+# Data fixups: absolute addresses are stored big-endian.
+# jitlink-check: *{8}(local_func_addr_quad) = local_func
+# jitlink-check: *{4}(external_data_addr_word) = external_data
+	.data
+	.globl	local_func_addr_quad
+	.p2align	3
+local_func_addr_quad:
+	.quad	local_func
+
+	.globl	external_data_addr_word
+	.p2align	2
+external_data_addr_word:
+	.word	external_data

>From f70acb73e7a8cd1f8c9b7d8204a5604037c55770 Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Tue, 22 Sep 2026 05:28:00 +0000
Subject: [PATCH 2/3] [JITLink] Restructure AArch64 BE8 support to follow the
 PPC64 pattern

Address review feedback by mirroring the structure used by the PPC64
backend, so big-endian objects flow through the same ELFT-templated
graph-building path as little-endian ones:

- Dispatch EM_AARCH64 on the ELF data encoding in ELF.cpp (like
  EM_PPC64) and export createLinkGraphFromELFObject_aarch64_be. Both
  entry points are thin wrappers over a single
  template <llvm::endianness> implementation, replacing the previous
  dyn_cast-based endianness detection.
- Replace the hand-written per-width endianness lambdas in
  aarch64::applyFixup with support::endian::write32/write64 calls on
  LinkGraph::getEndianness(). Instructions remain little-endian
  encoded (the A64 ISA is word-invariant), so instruction fixups are
  unchanged.
- Replace the minimal big-endian test with ELF_relocations_be.s, a
  big-endian port of ELF_relocations.s plus R_AARCH64_ABS32 and
  positive/negative R_AARCH64_PREL32/PREL64 coverage, and add
  ELF_ehframe_be.s to cover eh-frame processing on big-endian data.
---
 .../ExecutionEngine/JITLink/ELF_aarch64.h     |  15 +-
 .../llvm/ExecutionEngine/JITLink/aarch64.h    |  36 +-
 llvm/lib/ExecutionEngine/JITLink/ELF.cpp      |   9 +-
 .../ExecutionEngine/JITLink/ELF_aarch64.cpp   |  33 +-
 .../JITLink/AArch64/ELF_aarch64_be.s          |  47 ---
 .../JITLink/AArch64/ELF_ehframe_be.s          |  78 ++++
 .../JITLink/AArch64/ELF_relocations_be.s      | 389 ++++++++++++++++++
 7 files changed, 509 insertions(+), 98 deletions(-)
 delete mode 100644 llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe_be.s
 create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations_be.s

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
index 8d92abacbc8a6..9921474daba24 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
@@ -20,7 +20,7 @@
 namespace llvm {
 namespace jitlink {
 
-/// Create a LinkGraph from an ELF/aarch64 relocatable object
+/// Create a LinkGraph from an ELF/aarch64 (little-endian) relocatable object
 ///
 /// Note: The graph does not take ownership of the underlying buffer, nor copy
 /// its contents. The caller is responsible for ensuring that the object buffer
@@ -29,8 +29,17 @@ LLVM_ABI Expected<std::unique_ptr<LinkGraph>>
 createLinkGraphFromELFObject_aarch64(
     MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
 
-/// jit-link the given object buffer, which must be a ELF aarch64 relocatable
-/// object file.
+/// Create a LinkGraph from an ELF/aarch64_be (BE8) relocatable object
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+LLVM_ABI Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromELFObject_aarch64_be(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
+
+/// jit-link the given object buffer, which must be an ELF aarch64 relocatable
+/// object file, in either little-endian or big-endian (BE8) encoding.
 LLVM_ABI void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
                                std::unique_ptr<JITLinkContext> Ctx);
 
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
index 4b737f0828e20..c3285a38e4159 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
@@ -17,6 +17,7 @@
 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
 #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/Endian.h"
 
 namespace llvm {
 namespace jitlink {
@@ -503,44 +504,19 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
 
   // AArch64 BE8: data values follow target endianness, but instructions are
   // always encoded in LE byte order (the A64 ISA is word-invariant).
-  bool IsBE = !G.getTargetTriple().isLittleEndian();
-
-  auto writeData64 = [IsBE](char *Ptr, uint64_t Val) {
-    if (IsBE)
-      *(ubig64_t *)Ptr = Val;
-    else
-      *(ulittle64_t *)Ptr = Val;
-  };
-  auto writeData32 = [IsBE](char *Ptr, uint32_t Val) {
-    if (IsBE)
-      *(ubig32_t *)Ptr = Val;
-    else
-      *(ulittle32_t *)Ptr = Val;
-  };
-  auto writeDataS32 = [IsBE](char *Ptr, int32_t Val) {
-    if (IsBE)
-      *(big32_t *)Ptr = Val;
-    else
-      *(little32_t *)Ptr = Val;
-  };
-  auto writeDataS64 = [IsBE](char *Ptr, int64_t Val) {
-    if (IsBE)
-      *(big64_t *)Ptr = Val;
-    else
-      *(little64_t *)Ptr = Val;
-  };
+  llvm::endianness DataEndian = G.getEndianness();
 
   switch (E.getKind()) {
   case Pointer64: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
-    writeData64(FixupPtr, Value);
+    endian::write64(FixupPtr, Value, DataEndian);
     break;
   }
   case Pointer32: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
     if (Value > std::numeric_limits<uint32_t>::max())
       return makeTargetOutOfRangeError(G, B, E);
-    writeData32(FixupPtr, Value);
+    endian::write32(FixupPtr, Value, DataEndian);
     break;
   }
   case Delta32:
@@ -557,9 +533,9 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
       if (Value < std::numeric_limits<int32_t>::min() ||
           Value > std::numeric_limits<int32_t>::max())
         return makeTargetOutOfRangeError(G, B, E);
-      writeDataS32(FixupPtr, Value);
+      endian::write32(FixupPtr, static_cast<uint32_t>(Value), DataEndian);
     } else
-      writeDataS64(FixupPtr, Value);
+      endian::write64(FixupPtr, static_cast<uint64_t>(Value), DataEndian);
     break;
   }
   case Branch26PCRel: {
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
index d568fbc71632b..b6dea1fb1fc18 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
@@ -86,8 +86,13 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer,
     return TargetMachineArch.takeError();
 
   switch (*TargetMachineArch) {
-  case ELF::EM_AARCH64:
-    return createLinkGraphFromELFObject_aarch64(ObjectBuffer, std::move(SSP));
+  case ELF::EM_AARCH64: {
+    if (DataEncoding == ELF::ELFDATA2LSB)
+      return createLinkGraphFromELFObject_aarch64(ObjectBuffer, std::move(SSP));
+    else
+      return createLinkGraphFromELFObject_aarch64_be(ObjectBuffer,
+                                                     std::move(SSP));
+  }
   case ELF::EM_ARM:
     return createLinkGraphFromELFObject_aarch32(ObjectBuffer, std::move(SSP));
   case ELF::EM_HEXAGON:
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
index b4e2a6773dde5..0ed1c6fb0fc16 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
@@ -674,6 +674,7 @@ Error buildTables_ELF_aarch64(LinkGraph &G) {
 namespace llvm {
 namespace jitlink {
 
+template <llvm::endianness Endianness>
 Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64(
     MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
   LLVM_DEBUG({
@@ -689,24 +690,24 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64(
   if (!Features)
     return Features.takeError();
 
-  assert(((*ELFObj)->getArch() == Triple::aarch64 ||
-          (*ELFObj)->getArch() == Triple::aarch64_be) &&
-         "Object is not an AArch64 ELF file");
-
-  if (auto *E = dyn_cast<object::ELFObjectFile<object::ELF64LE>>(&**ELFObj))
-    return ELFLinkGraphBuilder_aarch64<object::ELF64LE>(
-               (*ELFObj)->getFileName(), E->getELFFile(), std::move(SSP),
-               (*ELFObj)->makeTriple(), std::move(*Features))
-        .buildGraph();
+  using ELFT = object::ELFType<Endianness, true>;
+  auto &ELFObjFile = cast<object::ELFObjectFile<ELFT>>(**ELFObj);
+  return ELFLinkGraphBuilder_aarch64<ELFT>(
+             (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), std::move(SSP),
+             (*ELFObj)->makeTriple(), std::move(*Features))
+      .buildGraph();
+}
 
-  if (auto *E = dyn_cast<object::ELFObjectFile<object::ELF64BE>>(&**ELFObj))
-    return ELFLinkGraphBuilder_aarch64<object::ELF64BE>(
-               (*ELFObj)->getFileName(), E->getELFFile(), std::move(SSP),
-               (*ELFObj)->makeTriple(), std::move(*Features))
-        .buildGraph();
+Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
+  return createLinkGraphFromELFObject_aarch64<llvm::endianness::little>(
+      std::move(ObjectBuffer), std::move(SSP));
+}
 
-  return make_error<JITLinkError>("Unsupported AArch64 ELF format in " +
-                                  ObjectBuffer.getBufferIdentifier());
+Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64_be(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
+  return createLinkGraphFromELFObject_aarch64<llvm::endianness::big>(
+      std::move(ObjectBuffer), std::move(SSP));
 }
 
 void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s
deleted file mode 100644
index bd3401cb0c45a..0000000000000
--- a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_be.s
+++ /dev/null
@@ -1,47 +0,0 @@
-# RUN: rm -rf %t && mkdir -p %t
-# RUN: llvm-mc -triple=aarch64_be-unknown-linux-gnu -filetype=obj \
-# RUN:   -o %t/elf_aarch64_be.o %s
-# RUN: llvm-jitlink -noexec -abs external_data=0xdeadbeef \
-# RUN:   -check %s %t/elf_aarch64_be.o
-
-# Test that AArch64 big-endian (BE8) objects link: data fixups are written
-# in target (big) endianness while instruction fixups stay little-endian
-# (the A64 ISA is word-invariant, so instructions are always LE-encoded).
-
-	.text
-	.globl	main
-	.p2align	2
-	.type	main, at function
-main:
-	ret
-	.size	main, .-main
-
-# Instruction fixup (R_AARCH64_CALL26): the BL immediate is patched into an
-# LE-encoded instruction word, even in a big-endian object.
-# jitlink-check: decode_operand(local_func_call26, 0)[25:0] = (local_func - local_func_call26)[27:2]
-	.globl	local_func
-	.p2align	2
-	.type	local_func, at function
-local_func:
-	ret
-	.size	local_func, .-local_func
-
-	.globl	local_func_call26
-	.p2align	2
-local_func_call26:
-	bl	local_func
-	.size	local_func_call26, .-local_func_call26
-
-# Data fixups: absolute addresses are stored big-endian.
-# jitlink-check: *{8}(local_func_addr_quad) = local_func
-# jitlink-check: *{4}(external_data_addr_word) = external_data
-	.data
-	.globl	local_func_addr_quad
-	.p2align	3
-local_func_addr_quad:
-	.quad	local_func
-
-	.globl	external_data_addr_word
-	.p2align	2
-external_data_addr_word:
-	.word	external_data
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe_be.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe_be.s
new file mode 100644
index 0000000000000..89f8aa3ea4682
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe_be.s
@@ -0,0 +1,78 @@
+# REQUIRES: asserts
+# RUN: llvm-mc -triple=aarch64_be-linux-gnu -filetype=obj -o %t %s
+# RUN: llvm-jitlink -num-threads=0 -debug-only=jitlink -noexec \
+# RUN:              -phony-externals %t 2>&1 | FileCheck %s
+#
+# Check that splitting of eh-frame sections works in a big-endian (BE8)
+# object: .eh_frame records are data (big-endian here), so this exercises
+# the endianness-aware reads of DWARFRecordSectionSplitter and
+# EHFrameEdgeFixer.
+#
+# CHECK: DWARFRecordSectionSplitter: Processing .eh_frame...
+# CHECK:  Processing block at
+# CHECK:    Processing CFI record at
+# CHECK:    Processing CFI record at
+# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
+# CHECK:   Processing block at
+# CHECK:     Record is CIE
+# CHECK:   Processing block at
+# CHECK:     Record is FDE
+# CHECK:       Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:       Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:       Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK:   Processing block at
+# CHECK:     Record is FDE
+# CHECK:       Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:       Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:       Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+
+	.text
+	.globl	main
+	.p2align	2
+	.type	main, at function
+main: 
+	.cfi_startproc
+	sub	sp, sp, #32
+	.cfi_def_cfa_offset 32
+	stp	x29, x30, [sp, #16]
+	add	x29, sp, #16
+	.cfi_def_cfa w29, 16
+	.cfi_offset w30, -8
+	.cfi_offset w29, -16
+	stur	wzr, [x29, #-4]
+	mov	x0, #4
+	bl	__cxa_allocate_exception
+	mov	w8, #1
+	str	w8, [x0]
+	adrp	x1, :got:_ZTIi
+	ldr	x1, [x1, :got_lo12:_ZTIi]
+	mov	x2, xzr
+	bl	__cxa_throw
+.main_end:
+	.size	main, .main_end-main
+	.cfi_endproc
+
+	.globl	dup
+	.p2align	2
+	.type	dup, at function
+dup: 
+	.cfi_startproc
+	sub	sp, sp, #32
+	.cfi_def_cfa_offset 32
+	stp	x29, x30, [sp, #16]
+	add	x29, sp, #16
+	.cfi_def_cfa w29, 16
+	.cfi_offset w30, -8
+	.cfi_offset w29, -16
+	stur	wzr, [x29, #-4]
+	mov	x0, #4
+	bl	__cxa_allocate_exception
+	mov	w8, #1
+	str	w8, [x0]
+	adrp	x1, :got:_ZTIi
+	ldr	x1, [x1, :got_lo12:_ZTIi]
+	mov	x2, xzr
+	bl	__cxa_throw
+.dup_end:
+	.size	dup, .dup_end-dup
+	.cfi_endproc
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations_be.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations_be.s
new file mode 100644
index 0000000000000..bafef7c334a57
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations_be.s
@@ -0,0 +1,389 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=aarch64_be-unknown-linux-gnu -position-independent \
+# RUN:   -filetype=obj -o %t/elf_reloc.o %s
+# RUN: llvm-jitlink -noexec \
+# RUN:              -abs external_data=0xdeadbeef \
+# RUN:              -abs external_func=0xcafef00d \
+# RUN:              -check %s %t/elf_reloc.o
+
+# Big-endian (BE8) counterpart of ELF_relocations.s: same relocation coverage,
+# plus checks that data fixups are written in target (big) endianness.
+# Instructions are always little-endian encoded (the A64 ISA is
+# word-invariant), so instruction fixups still patch LE-encoded words here.
+
+        .text
+
+        .globl  main
+        .p2align  2
+        .type main, at function
+main:
+        ret
+
+        .size   main, .-main
+
+# Check R_AARCH64_CALL26 / R_AARCH64_JUMP26 relocation of a local function call
+#
+# jitlink-check: decode_operand(local_func_call26, 0)[25:0] = (local_func - local_func_call26)[27:2]
+# jitlink-check: decode_operand(local_func_jump26, 0)[25:0] = (local_func - local_func_jump26)[27:2]
+        .globl  local_func
+        .p2align  2
+        .type	local_func, at function
+local_func:
+        ret
+        .size   local_func, .-local_func
+
+        .globl  local_func_call26
+        .p2align  2
+local_func_call26:
+        bl	local_func
+        .size   local_func_call26, .-local_func_call26
+
+        .globl  local_func_jump26
+        .p2align  2
+local_func_jump26:
+        b	local_func
+        .size   local_func_jump26, .-local_func_jump26
+
+# Check R_AARCH64_ADR_PREL_LO21 relocation of a local symbol
+#
+# jitlink-check: decode_operand(test_adr_prel_lo21, 1) = (adr_data - test_adr_prel_lo21)[20:0]
+        .globl  test_adr_prel_lo21, adr_data
+        .p2align  2
+test_adr_prel_lo21:
+        adr	x0, adr_data
+        .size test_adr_prel_lo21, .-test_adr_prel_lo21
+## ADR encoding is a bit tricky so use an offset with an irregular bit pattern
+## to test this bit better
+adr_data = test_adr_prel_lo21 + 0xe46f2
+
+# Check R_AARCH64_LD_PREL_LO19 relocation of a local symbol
+#
+# jitlink-check: decode_operand(test_ldr_prel_lo19 + 0, 1)[19:0] = \
+# jitlink-check:     (ldr_data - test_ldr_prel_lo19 + 0x4)[21:2]
+        .globl  test_ldr_prel_lo19, ldr_data
+        .p2align  2
+test_ldr_prel_lo19:
+        ldr	x0, ldr_data + 0x4
+        .size test_ldr_prel_lo19, .-test_ldr_prel_lo19
+ldr_data = test_ldr_prel_lo19 + 4
+
+# Check R_AARCH64_ADR_PREL_PG_HI21 / R_AARCH64_ADD_ABS_LO12_NC relocation of a local symbol
+#
+# For the ADR_PREL_PG_HI21/ADRP instruction we have the 21-bit delta to the 4k page
+# containing the global.
+#
+# jitlink-check: decode_operand(test_adr_prel, 1) = (named_data - test_adr_prel)[32:12]
+# jitlink-check: decode_operand(test_add_abs_lo12, 2) = (named_data + 0)[11:0]
+        .globl  test_adr_prel
+        .p2align  2
+test_adr_prel:
+        adrp	x0, named_data
+        .size test_adr_prel, .-test_adr_prel
+
+        .globl  test_add_abs_lo12
+        .p2align  2
+test_add_abs_lo12:
+        add	x0, x0, :lo12:named_data
+        .size test_add_abs_lo12, .-test_add_abs_lo12
+
+# Check that calls/jumps to external functions trigger the generation of stubs and GOT
+# entries.
+#
+# jitlink-check: decode_operand(test_external_call, 0) = (stub_addr(elf_reloc.o, external_func) - test_external_call)[27:2]
+# jitlink-check: decode_operand(test_external_jump, 0) = (stub_addr(elf_reloc.o, external_func) - test_external_jump)[27:2]
+# jitlink-check: *{8}(got_addr(elf_reloc.o, external_func)) = external_func
+        .globl  test_external_call
+        .p2align  2
+test_external_call:
+        bl   external_func
+        .size test_external_call, .-test_external_call
+
+       .globl  test_external_jump
+        .p2align  2
+test_external_jump:
+        b   external_func
+        .size test_external_jump, .-test_external_jump
+
+# Check R_AARCH64_LDST*_ABS_LO12NC relocation of a local symbol
+#
+# The immediate value should be the symbol address right shifted according to its instruction bitwidth.
+#
+# jitlink-check: decode_operand(test_ldrb, 2) = named_data[11:0]
+# jitlink-check: decode_operand(test_ldrsb, 2) = (named_data + 0)[11:0]
+# jitlink-check: decode_operand(test_ldrh, 2) = (named_data + 0)[11:1]
+# jitlink-check: decode_operand(test_ldrsh, 2) = (named_data + 0)[11:1]
+# jitlink-check: decode_operand(test_ldr_32bit, 2) = (named_data + 0)[11:2]
+# jitlink-check: decode_operand(test_ldr_64bit, 2) = (named_data + 0)[11:3]
+# jitlink-check: decode_operand(test_strb, 2) = named_data[11:0]
+# jitlink-check: decode_operand(test_strh, 2) = (named_data + 0)[11:1]
+# jitlink-check: decode_operand(test_str_32bit, 2) = (named_data + 0)[11:2]
+# jitlink-check: decode_operand(test_str_64bit, 2) = (named_data + 0)[11:3]
+
+        .globl  test_ldrb
+test_ldrb:
+        ldrb	w0, [x1, :lo12:named_data]
+        .size test_ldrb, .-test_ldrb
+
+        .globl  test_ldrsb
+test_ldrsb:
+        ldrsb	w0, [x1, :lo12:named_data]
+        .size test_ldrsb, .-test_ldrsb
+
+        .globl  test_ldrh
+test_ldrh:
+        ldrh	w0, [x1, :lo12:named_data]
+        .size test_ldrh, .-test_ldrh
+
+        .globl  test_ldrsh
+test_ldrsh:
+        ldrsh	w0, [x1, :lo12:named_data]
+        .size test_ldrsh, .-test_ldrsh
+
+        .globl  test_ldr_32bit
+test_ldr_32bit:
+        ldr	w0, [x1, :lo12:named_data]
+        .size test_ldr_32bit, .-test_ldr_32bit
+
+        .globl  test_ldr_64bit
+test_ldr_64bit:
+        ldr	x0, [x1, :lo12:named_data]
+        .size test_ldr_64bit, .-test_ldr_64bit
+
+        .globl  test_strb
+test_strb:
+        strb	w0, [x1, :lo12:named_data]
+        .size test_strb, .-test_strb
+
+        .globl  test_strh
+test_strh:
+        strh	w0, [x1, :lo12:named_data]
+        .size test_strh, .-test_strh
+
+        .globl  test_str_32bit
+test_str_32bit:
+        str	w0, [x1, :lo12:named_data]
+        .size test_str_32bit, .-test_str_32bit
+
+        .globl  test_str_64bit
+test_str_64bit:
+        str	x0, [x1, :lo12:named_data]
+        .size test_str_64bit, .-test_str_64bit
+
+
+# Check R_AARCH64_MOVW_UABS_G*_NC relocation of a local symbol
+#
+# The immediate value should be the symbol address right shifted according to LSL value
+#
+# jitlink-check: decode_operand(test_movz_g0_nc, 1) = named_data[15:0]
+# jitlink-check: decode_operand(test_movk_g0_nc, 2) = named_data[15:0]
+# jitlink-check: decode_operand(test_movz_g1_nc, 1) = named_data[31:16]
+# jitlink-check: decode_operand(test_movk_g1_nc, 2) = named_data[31:16]
+# jitlink-check: decode_operand(test_movz_g2_nc, 1) = named_data[47:32]
+# jitlink-check: decode_operand(test_movk_g2_nc, 2) = named_data[47:32]
+# jitlink-check: decode_operand(test_movz_g3, 1) = named_data[63:48]
+# jitlink-check: decode_operand(test_movk_g3, 2) = named_data[63:48]
+
+        .globl  test_movz_g0_nc
+test_movz_g0_nc:
+        movz x0, #:abs_g0_nc:named_data
+        .size test_movz_g0_nc, .-test_movz_g0_nc
+
+        .globl  test_movk_g0_nc
+test_movk_g0_nc:
+        movk x0, #:abs_g0_nc:named_data
+        .size test_movk_g0_nc, .-test_movk_g0_nc
+
+        .globl  test_movz_g1_nc
+test_movz_g1_nc:
+        movz x0, #:abs_g1_nc:named_data
+        .size test_movz_g1_nc, .-test_movz_g1_nc
+
+        .globl  test_movk_g1_nc
+test_movk_g1_nc:
+        movk x0, #:abs_g1_nc:named_data
+        .size test_movk_g1_nc, .-test_movk_g1_nc
+
+        .globl  test_movz_g2_nc
+test_movz_g2_nc:
+        movz x0, #:abs_g2_nc:named_data
+        .size test_movz_g2_nc, .-test_movz_g2_nc
+
+        .globl  test_movk_g2_nc
+test_movk_g2_nc:
+        movk x0, #:abs_g2_nc:named_data
+        .size test_movk_g2_nc, .-test_movk_g2_nc
+
+        .globl  test_movk_g3
+test_movk_g3:
+        movk x0, #:abs_g3:named_data
+        .size test_movk_g3, .-test_movk_g3
+
+        .globl  test_movz_g3
+test_movz_g3:
+        movz x0, #:abs_g3:named_data
+        .size test_movz_g3, .-test_movz_g3
+
+# Check R_AARCH64_ABS64 relocation of a function pointer to local symbol.
+# In a big-endian object the 64-bit value must be stored big-endian.
+#
+# jitlink-check: *{8}local_func_addr_quad = named_func
+        .globl  local_func_addr_quad
+        .p2align  3
+local_func_addr_quad:
+        .xword	named_func
+        .size	local_func_addr_quad, 8
+
+# Check R_AARCH64_ABS64 relocation of a function pointer to external symbol
+#
+# jitlink-check: *{8}external_func_addr_quad = external_func
+        .globl  external_func_addr_quad
+        .p2align  3
+external_func_addr_quad:
+        .xword	external_func
+        .size	external_func_addr_quad, 8
+
+# Check R_AARCH64_ABS32 relocation of an external symbol
+#
+# jitlink-check: *{4}external_data_addr_word = external_data
+        .globl  external_data_addr_word
+        .p2align  2
+external_data_addr_word:
+        .word	external_data
+        .size	external_data_addr_word, 4
+
+# Check R_AARCH64_PREL32 / R_AARCH64_PREL64 relocations of local symbols.
+# Delta fixups must be written in target (big) endianness, for both forward
+# (positive) and backward (negative) deltas.
+#
+# jitlink-check: *{4}prel32_pos = (named_func - prel32_pos)[31:0]
+# jitlink-check: *{8}prel64_pos = named_func - prel64_pos
+# jitlink-check: *{4}prel32_neg = (main - prel32_neg)[31:0]
+# jitlink-check: *{8}prel64_neg = main - prel64_neg
+        .globl  prel32_pos
+        .p2align  2
+prel32_pos:
+        .word	named_func - .
+        .size	prel32_pos, 4
+
+        .globl  prel64_pos
+        .p2align  3
+prel64_pos:
+        .xword	named_func - .
+        .size	prel64_pos, 8
+
+        .globl  prel32_neg
+        .p2align  2
+prel32_neg:
+        .word	main - .
+        .size	prel32_neg, 4
+
+        .globl  prel64_neg
+        .p2align  3
+prel64_neg:
+        .xword	main - .
+        .size	prel64_neg, 8
+
+# Check R_AARCH64_ADR_GOT_PAGE / R_AARCH64_LD64_GOT_LO12_NC handling with a
+# reference to an external symbol. Validate both the reference to the GOT entry,
+# and also the content of the GOT entry.
+#
+# For the ADRP :got: instruction we have the 21-bit delta to the 4k page
+# containing the GOT entry for external_data.
+#
+# For the LDR :got_lo12: instruction we have the 12-bit offset of the entry
+# within the page.
+#
+# jitlink-check: *{8}(got_addr(elf_reloc.o, external_data)) = external_data
+# jitlink-check: decode_operand(test_adr_gotpage_external, 1) = \
+# jitlink-check:     (got_addr(elf_reloc.o, external_data)[32:12] - \
+# jitlink-check:        test_adr_gotpage_external[32:12])
+# jitlink-check: decode_operand(test_ld64_gotlo12_external, 2) = \
+# jitlink-check:     got_addr(elf_reloc.o, external_data)[11:3]
+        .globl  test_adr_gotpage_external
+        .p2align  2
+test_adr_gotpage_external:
+        adrp  x0, :got:external_data
+        .size test_adr_gotpage_external, .-test_adr_gotpage_external
+
+        .globl  test_ld64_gotlo12_external
+        .p2align  2
+test_ld64_gotlo12_external:
+        ldr   x0, [x0, :got_lo12:external_data]
+        .size test_ld64_gotlo12_external, .-test_ld64_gotlo12_external
+
+# Check R_AARCH64_LD64_GOTPAGE_LO15 handling with a reference to an external
+# symbol. Validate the reference to the GOT entry.
+# For the LDR :gotpage_lo15: instruction we have the 15-bit offset of the GOT
+# entry from the page containing the GOT.
+# jitlink-check: decode_operand(test_ld64_gotpagelo15_external, 2) = \
+# jitlink-check:     (got_addr(elf_reloc.o, external_data) - \
+# jitlink-check:     (section_addr(elf_reloc.o, $__GOT) & 0xfffffffffffff000)) \
+# jitlink-check:     [15:3]
+        .globl  test_ld64_gotpagelo15_external
+        .p2align  2
+test_ld64_gotpagelo15_external:
+        ldr   x0, [x0, :gotpage_lo15:external_data]
+        .size test_ld64_gotpagelo15_external, .-test_ld64_gotpagelo15_external
+
+# Check R_AARCH64_TSTBR14 for tbz
+#
+# jitlink-check: decode_operand(test_tstbr14_tbz, 2) = \
+# jitlink-check:     (test_tstbr14_tbz_target - test_tstbr14_tbz)[16:2]
+        .globl test_tstbr14_tbz, test_tstbr14_tbz_target
+        .p2align 2
+test_tstbr14_tbz:
+        tbz x0, 0, test_tstbr14_tbz_target
+        .skip (1 << 14)
+test_tstbr14_tbz_target:
+        .size test_tstbr14_tbz, .-test_tstbr14_tbz
+
+# Check R_AARCH64_TSTBR14 for tbnz
+#
+# jitlink-check: decode_operand(test_tstbr14_tbnz, 2) = \
+# jitlink-check:     (test_tstbr14_tbnz_target - test_tstbr14_tbnz)[16:2]
+        .globl test_tstbr14_tbnz, test_tstbr14_tbnz_target
+        .p2align 2
+test_tstbr14_tbnz:
+        tbnz x0, 0, test_tstbr14_tbnz_target
+        .skip (1 << 14)
+test_tstbr14_tbnz_target:
+        .size test_tstbr14_tbnz, .-test_tstbr14_tbnz
+
+# Check R_AARCH64_CONDBR19 for compare and branch instructions
+#
+# jitlink-check: decode_operand(test_condbr19_cbz, 1) = \
+# jitlink-check:     (test_condbr19_cbz_target - test_condbr19_cbz)[21:2]
+        .globl test_condbr19_cbz, test_condbr19_cbz_target
+        .p2align 2
+test_condbr19_cbz:
+        cbz x0, test_condbr19_cbz_target
+        .skip (1 << 19)
+test_condbr19_cbz_target:
+        .size test_condbr19_cbz, .-test_condbr19_cbz
+
+# Check R_AARCH64_CONDBR19 for conditional branch instructions
+#
+# jitlink-check: decode_operand(test_condbr19_bc, 1) = \
+# jitlink-check:     (test_condbr19_bc_target - test_condbr19_bc)[21:2]
+        .globl test_condbr19_bc, test_condbr19_bc_target
+        .p2align 2
+test_condbr19_bc:
+        b.eq test_condbr19_bc_target
+        .skip (1 << 19)
+test_condbr19_bc_target:
+        .size test_condbr19_bc, .-test_condbr19_bc
+
+        .globl  named_data
+        .p2align  4
+        .type   named_data, at object
+named_data:
+        .quad   0x2222222222222222
+        .quad   0x3333333333333333
+        .size   named_data, .-named_data
+
+        .globl  named_func
+        .p2align  2
+        .type	named_func, at function
+named_func:
+        ret
+        .size   named_func, .-named_func

>From 3d883a27be4c619810d9b70397c36b26a89a2246 Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Tue, 22 Sep 2026 14:50:58 +0000
Subject: [PATCH 3/3] [JITLink] Template the AArch64 link side on endianness

Address review feedback on the link side: ELFJITLinker_aarch64 and
aarch64::applyFixup are now templated on llvm::endianness, mirroring
the PPC64 backend:

- aarch64::applyFixup<Endianness> writes data fixups (Pointer64/32,
  Delta32/64, NegDelta32/64) with compile-time target endianness,
  replacing the runtime LinkGraph::getEndianness() lookup. Instruction
  fixups remain little-endian (the A64 ISA is word-invariant, so BE8
  keeps instructions LE).
- ELFJITLinker_aarch64<Endianness> forwards the endianness to
  applyFixup; the CRTP base is accessed through a JITLinkerBase alias
  with this-> qualification for dependent-name lookup.
- link_ELF_aarch64 is templated, with link_ELF_aarch64 (little-endian)
  and link_ELF_aarch64_be (BE8) entry points; link_ELF dispatches
  Triple::aarch64 vs Triple::aarch64_be, matching the PPC64 dispatch.
- MachO_arm64 passes llvm::endianness::little explicitly to
  aarch64::applyFixup.
- lowerPointer64AuthEdgesToSigningFunction now serializes its signing
  instructions little-endian explicitly (A64 instructions are always
  LE, BE8 included) rather than via the graph endianness.
---
 .../ExecutionEngine/JITLink/ELF_aarch64.h     |  9 ++++--
 .../llvm/ExecutionEngine/JITLink/aarch64.h    | 17 ++++++-----
 llvm/lib/ExecutionEngine/JITLink/ELF.cpp      |  4 ++-
 .../ExecutionEngine/JITLink/ELF_aarch64.cpp   | 29 ++++++++++++++-----
 .../ExecutionEngine/JITLink/MachO_arm64.cpp   |  2 +-
 llvm/lib/ExecutionEngine/JITLink/aarch64.cpp  |  5 ++--
 6 files changed, 45 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
index 9921474daba24..0118bb85e3242 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h
@@ -38,11 +38,16 @@ LLVM_ABI Expected<std::unique_ptr<LinkGraph>>
 createLinkGraphFromELFObject_aarch64_be(
     MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
 
-/// jit-link the given object buffer, which must be an ELF aarch64 relocatable
-/// object file, in either little-endian or big-endian (BE8) encoding.
+/// jit-link the given object buffer, which must be an ELF aarch64
+/// (little-endian) relocatable object file.
 LLVM_ABI void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
                                std::unique_ptr<JITLinkContext> Ctx);
 
+/// jit-link the given object buffer, which must be an ELF aarch64_be (BE8)
+/// relocatable object file.
+LLVM_ABI void link_ELF_aarch64_be(std::unique_ptr<LinkGraph> G,
+                                  std::unique_ptr<JITLinkContext> Ctx);
+
 } // end namespace jitlink
 } // end namespace llvm
 
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
index c3285a38e4159..ee69746bf0217 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
@@ -494,6 +494,11 @@ inline unsigned getMoveWide16Shift(uint32_t Instr) {
 }
 
 /// Apply fixup expression for edge to block content.
+///
+/// \tparam Endianness the target endianness applied to data fixups. In BE8
+///        mode instructions remain little-endian (the A64 ISA is
+///        word-invariant), so only data fixups use this value.
+template <llvm::endianness Endianness>
 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
                         const Symbol *GOTSymbol) {
   using namespace support;
@@ -502,21 +507,17 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
   char *FixupPtr = BlockWorkingMem + E.getOffset();
   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
 
-  // AArch64 BE8: data values follow target endianness, but instructions are
-  // always encoded in LE byte order (the A64 ISA is word-invariant).
-  llvm::endianness DataEndian = G.getEndianness();
-
   switch (E.getKind()) {
   case Pointer64: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
-    endian::write64(FixupPtr, Value, DataEndian);
+    endian::write64<Endianness>(FixupPtr, Value);
     break;
   }
   case Pointer32: {
     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
     if (Value > std::numeric_limits<uint32_t>::max())
       return makeTargetOutOfRangeError(G, B, E);
-    endian::write32(FixupPtr, Value, DataEndian);
+    endian::write32<Endianness>(FixupPtr, Value);
     break;
   }
   case Delta32:
@@ -533,9 +534,9 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
       if (Value < std::numeric_limits<int32_t>::min() ||
           Value > std::numeric_limits<int32_t>::max())
         return makeTargetOutOfRangeError(G, B, E);
-      endian::write32(FixupPtr, static_cast<uint32_t>(Value), DataEndian);
+      endian::write32<Endianness>(FixupPtr, static_cast<uint32_t>(Value));
     } else
-      endian::write64(FixupPtr, static_cast<uint64_t>(Value), DataEndian);
+      endian::write64<Endianness>(FixupPtr, static_cast<uint64_t>(Value));
     break;
   }
   case Branch26PCRel: {
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
index b6dea1fb1fc18..c1b5b00bdac3b 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
@@ -124,9 +124,11 @@ void link_ELF(std::unique_ptr<LinkGraph> G,
               std::unique_ptr<JITLinkContext> Ctx) {
   switch (G->getTargetTriple().getArch()) {
   case Triple::aarch64:
-  case Triple::aarch64_be:
     link_ELF_aarch64(std::move(G), std::move(Ctx));
     return;
+  case Triple::aarch64_be:
+    link_ELF_aarch64_be(std::move(G), std::move(Ctx));
+    return;
   case Triple::arm:
   case Triple::armeb:
   case Triple::thumb:
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
index 0ed1c6fb0fc16..394c6d683e769 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
@@ -31,16 +31,19 @@ namespace {
 
 constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
 
-class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
-  friend class JITLinker<ELFJITLinker_aarch64>;
+template <llvm::endianness Endianness>
+class ELFJITLinker_aarch64
+    : public JITLinker<ELFJITLinker_aarch64<Endianness>> {
+  using JITLinkerBase = JITLinker<ELFJITLinker_aarch64<Endianness>>;
+  friend JITLinkerBase;
 
 public:
   ELFJITLinker_aarch64(std::unique_ptr<JITLinkContext> Ctx,
                        std::unique_ptr<LinkGraph> G,
                        PassConfiguration PassConfig)
-      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {
-    if (shouldAddDefaultTargetPasses(getGraph().getTargetTriple()))
-      getPassConfig().PostAllocationPasses.push_back(
+      : JITLinkerBase(std::move(Ctx), std::move(G), std::move(PassConfig)) {
+    if (this->shouldAddDefaultTargetPasses(this->getGraph().getTargetTriple()))
+      this->getPassConfig().PostAllocationPasses.push_back(
           [this](LinkGraph &G) { return getOrCreateGOTSymbol(G); });
   }
 
@@ -48,7 +51,7 @@ class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
   Symbol *GOTSymbol = nullptr;
 
   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
-    return aarch64::applyFixup(G, B, E, GOTSymbol);
+    return aarch64::applyFixup<Endianness>(G, B, E, GOTSymbol);
   }
 
   Error getOrCreateGOTSymbol(LinkGraph &G) {
@@ -710,6 +713,7 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_aarch64_be(
       std::move(ObjectBuffer), std::move(SSP));
 }
 
+template <llvm::endianness Endianness>
 void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
                       std::unique_ptr<JITLinkContext> Ctx) {
   PassConfiguration Config;
@@ -740,7 +744,18 @@ void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
   if (auto Err = Ctx->modifyPassConfig(*G, Config))
     return Ctx->notifyFailed(std::move(Err));
 
-  ELFJITLinker_aarch64::link(std::move(Ctx), std::move(G), std::move(Config));
+  ELFJITLinker_aarch64<Endianness>::link(std::move(Ctx), std::move(G),
+                                         std::move(Config));
+}
+
+void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
+                      std::unique_ptr<JITLinkContext> Ctx) {
+  link_ELF_aarch64<llvm::endianness::little>(std::move(G), std::move(Ctx));
+}
+
+void link_ELF_aarch64_be(std::unique_ptr<LinkGraph> G,
+                         std::unique_ptr<JITLinkContext> Ctx) {
+  link_ELF_aarch64<llvm::endianness::big>(std::move(G), std::move(Ctx));
 }
 
 } // namespace jitlink
diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
index baeab60c9ccc1..9550f7911e9c2 100644
--- a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
@@ -577,7 +577,7 @@ class MachOJITLinker_arm64 : public JITLinker<MachOJITLinker_arm64> {
 
 private:
   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
-    return aarch64::applyFixup(G, B, E, nullptr);
+    return aarch64::applyFixup<llvm::endianness::little>(G, B, E, nullptr);
   }
 
   uint64_t NullValue = 0;
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp
index b19b839aab7fb..0935e6021c1f1 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp
@@ -316,11 +316,12 @@ Error lowerPointer64AuthEdgesToSigningFunction(LinkGraph &G) {
   auto &SigningFunctionBlock = SigningFunctionSym.getBlock();
   auto SigningFunctionBuf = SigningFunctionBlock.getAlreadyMutableContent();
 
-  // Write the instructions to the block content.
+  // Write the instructions to the block content. A64 instructions are always
+  // little-endian encoded regardless of the graph's data endianness (BE8).
   BinaryStreamWriter InstrWriter(
       {reinterpret_cast<uint8_t *>(SigningFunctionBuf.data()),
        SigningFunctionBuf.size()},
-      G.getEndianness());
+      llvm::endianness::little);
 
   auto AppendInstr = [&](uint32_t Instr) {
     return InstrWriter.writeInteger(Instr);



More information about the llvm-commits mailing list