[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