[llvm] [JITLink] Support AArch64 big-endian (BE8) ELF objects (PR #225120)
dong jianqiang via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 08:49:47 PDT 2026
https://github.com/dongjianqiang2 created https://github.com/llvm/llvm-project/pull/225120
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.
>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] [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 12bcdd1a79d80a..4b737f0828e207 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 494466297aefe4..d568fbc71632b1 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 cd2eed748acdef..b4e2a6773dde55 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 00000000000000..bd3401cb0c45a3
--- /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
More information about the llvm-commits
mailing list