[llvm] [JITLink][AArch64] Add LD64_GOTPAGE_LO15 rel support (PR #100854)
Vladislav Khmelevsky via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 26 22:50:35 PDT 2024
https://github.com/yota9 created https://github.com/llvm/llvm-project/pull/100854
This relocation is used in order to address GOT entries using 15 bit
offset in ldr instruction. The offset is calculated relative to GOT
section page address.
>From 40f1708ed7f587bbb246aa00647ef5cc3b50e7e7 Mon Sep 17 00:00:00 2001
From: Vladislav Khmelevsky <och95 at yandex.ru>
Date: Sat, 27 Jul 2024 08:13:09 +0400
Subject: [PATCH] [JITLink][AArch64] Add LD64_GOTPAGE_LO15 rel support
This relocation is used in order to address GOT entries using 15 bit
offset in ldr instruction. The offset is calculated relative to GOT
section page address.
---
.../llvm/ExecutionEngine/JITLink/JITLink.h | 6 +
.../llvm/ExecutionEngine/JITLink/aarch64.h | 307 +++++++++++-------
.../ExecutionEngine/JITLink/ELF_aarch64.cpp | 9 +
.../JITLink/AArch64/ELF_relocations.s | 14 +
4 files changed, 213 insertions(+), 123 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
index 8fe53760a852d..c84b960f3b13f 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
@@ -758,6 +758,12 @@ class Section {
return make_range(Symbols.begin(), Symbols.end());
}
+ /// Returns address of the section
+ uint64_t getAddress() const {
+ assert(Symbols.size() && "Section has no symbols");
+ return (*Symbols.begin())->getAddress().getValue();
+ }
+
/// Return the number of symbols in this section.
SymbolSet::size_type symbols_size() const { return Symbols.size(); }
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
index c09398f984477..893c03ca1714b 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
@@ -233,6 +233,21 @@ enum EdgeKind_aarch64 : Edge::Kind {
/// out-of-range error will be returned.
PageOffset12,
+ /// The 15-bit offset of the GOT entry from the GOT table.
+ ///
+ /// Used for load/store instructions addressing a GOT entry.
+ ///
+ /// Fixup expression:
+ ///
+ /// Fixup <- ((Target + Addend - Page(GOT))) & 0x7fff) >> 3 : uint12
+ ///
+ /// Errors:
+ /// - The result of the unshifted part of the fixup expression must be
+ /// aligned otherwise an alignment error will be returned.
+ /// - The result of the fixup expression must fit into a uint12 otherwise an
+ /// out-of-range error will be returned.
+ GotPageOffset15,
+
/// A GOT entry getter/constructor, transformed to Page21 pointing at the GOT
/// entry for the original target.
///
@@ -273,6 +288,23 @@ enum EdgeKind_aarch64 : Edge::Kind {
///
RequestGOTAndTransformToPageOffset12,
+ /// A GOT entry getter/constructor, transformed to Pageoffset15 pointing at
+ /// the GOT entry for the original target.
+ ///
+ /// Indicates that this edge should be transformed into a GotPageOffset15
+ /// targeting the GOT entry for the edge's current target, maintaining the
+ /// same addend. A GOT entry for the target should be created if one does not
+ /// already exist.
+ ///
+ /// Fixup expression:
+ /// NONE
+ ///
+ /// Errors:
+ /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+ /// phase will result in an assert/unreachable during the fixup phase.
+ ///
+ RequestGOTAndTransformToPageOffset15,
+
/// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT
/// entry for the original target.
///
@@ -429,6 +461,138 @@ inline unsigned getMoveWide16Shift(uint32_t Instr) {
return 0;
}
+/// aarch64 pointer size.
+constexpr uint64_t PointerSize = 8;
+
+/// AArch64 null pointer content.
+extern const char NullPointerContent[PointerSize];
+
+/// AArch64 pointer jump stub content.
+///
+/// Contains the instruction sequence for an indirect jump via an in-memory
+/// pointer:
+/// ADRP x16, ptr at page21
+/// LDR x16, [x16, ptr at pageoff12]
+/// BR x16
+extern const char PointerJumpStubContent[12];
+
+/// Creates a new pointer block in the given section and returns an
+/// Anonymous symbol pointing to it.
+///
+/// If InitialTarget is given then an Pointer64 relocation will be added to the
+/// block pointing at InitialTarget.
+///
+/// The pointer block will have the following default values:
+/// alignment: 64-bit
+/// alignment-offset: 0
+/// address: highest allowable (~7U)
+inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
+ Symbol *InitialTarget = nullptr,
+ uint64_t InitialAddend = 0) {
+ auto &B = G.createContentBlock(PointerSection, NullPointerContent,
+ orc::ExecutorAddr(~uint64_t(7)), 8, 0);
+ if (InitialTarget)
+ B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
+ return G.addAnonymousSymbol(B, 0, 8, false, false);
+}
+
+/// Create a jump stub block that jumps via the pointer at the given symbol.
+///
+/// The stub block will have the following default values:
+/// alignment: 32-bit
+/// alignment-offset: 0
+/// address: highest allowable: (~11U)
+inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
+ Symbol &PointerSymbol) {
+ auto &B = G.createContentBlock(StubSection, PointerJumpStubContent,
+ orc::ExecutorAddr(~uint64_t(11)), 4, 0);
+ B.addEdge(Page21, 0, PointerSymbol, 0);
+ B.addEdge(PageOffset12, 4, PointerSymbol, 0);
+ return B;
+}
+
+/// Create a jump stub that jumps via the pointer at the given symbol and
+/// an anonymous symbol pointing to it. Return the anonymous symbol.
+///
+/// The stub block will be created by createPointerJumpStubBlock.
+inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
+ Section &StubSection,
+ Symbol &PointerSymbol) {
+ return G.addAnonymousSymbol(
+ createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0,
+ sizeof(PointerJumpStubContent), true, false);
+}
+
+/// Global Offset Table Builder.
+class GOTTableManager : public TableManager<GOTTableManager> {
+public:
+ static StringRef getSectionName() { return "$__GOT"; }
+
+ bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
+ Edge::Kind KindToSet = Edge::Invalid;
+ const char *BlockWorkingMem = B->getContent().data();
+ const char *FixupPtr = BlockWorkingMem + E.getOffset();
+
+ switch (E.getKind()) {
+ case aarch64::RequestGOTAndTransformToPage21:
+ case aarch64::RequestTLVPAndTransformToPage21: {
+ KindToSet = aarch64::Page21;
+ break;
+ }
+ case aarch64::RequestGOTAndTransformToPageOffset12:
+ case aarch64::RequestTLVPAndTransformToPageOffset12: {
+ KindToSet = aarch64::PageOffset12;
+ uint32_t RawInstr = *(const support::ulittle32_t *)FixupPtr;
+ (void)RawInstr;
+ assert(E.getAddend() == 0 &&
+ "GOTPageOffset12/TLVPageOffset12 with non-zero addend");
+ assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
+ "RawInstr isn't a 64-bit LDR immediate");
+ break;
+ }
+ case aarch64::RequestGOTAndTransformToPageOffset15: {
+ KindToSet = aarch64::GotPageOffset15;
+ uint32_t RawInstr = *(const support::ulittle32_t *)FixupPtr;
+ (void)RawInstr;
+ assert(E.getAddend() == 0 && "GOTPageOffset15 with non-zero addend");
+ assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
+ "RawInstr isn't a 64-bit LDR immediate");
+ break;
+ }
+ case aarch64::RequestGOTAndTransformToDelta32: {
+ KindToSet = aarch64::Delta32;
+ break;
+ }
+ default:
+ return false;
+ }
+ assert(KindToSet != Edge::Invalid &&
+ "Fell through switch, but no new kind to set");
+ DEBUG_WITH_TYPE("jitlink", {
+ dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
+ << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
+ << formatv("{0:x}", E.getOffset()) << ")\n";
+ });
+ E.setKind(KindToSet);
+ E.setTarget(getEntryForTarget(G, E.getTarget()));
+ return true;
+ }
+
+ Symbol &createEntry(LinkGraph &G, Symbol &Target) {
+ return createAnonymousPointer(G, getGOTSection(G), &Target);
+ }
+
+private:
+ Section &getGOTSection(LinkGraph &G) {
+ if (!GOTSection)
+ GOTSection = &G.createSection(getSectionName(),
+ orc::MemProt::Read | orc::MemProt::Exec);
+ return *GOTSection;
+ }
+
+ Section *GOTSection = nullptr;
+};
+
/// Apply fixup expression for edge to block content.
inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
using namespace support;
@@ -603,6 +767,26 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
*(ulittle32_t *)FixupPtr = FixedInstr;
break;
}
+ case GotPageOffset15: {
+ Section *GOTSection =
+ G.findSectionByName(aarch64::GOTTableManager::getSectionName());
+ assert(GOTSection && "GOT section not found");
+ uint64_t TargetOffset =
+ (E.getTarget().getAddress() + E.getAddend()).getValue() -
+ (GOTSection->getAddress() & ~static_cast<uint64_t>(4096 - 1));
+ if (TargetOffset > 0x7fff)
+ return make_error<JITLinkError>("PAGEOFF15 target is out of range");
+
+ uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
+ const unsigned ImmShift = 3;
+ if (TargetOffset & ((1 << ImmShift) - 1))
+ return make_error<JITLinkError>("PAGEOFF15 target is not aligned");
+
+ uint32_t EncodedImm = (TargetOffset >> ImmShift) << 10;
+ uint32_t FixedInstr = RawInstr | EncodedImm;
+ *(ulittle32_t *)FixupPtr = FixedInstr;
+ break;
+ }
default:
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -612,129 +796,6 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
return Error::success();
}
-/// aarch64 pointer size.
-constexpr uint64_t PointerSize = 8;
-
-/// AArch64 null pointer content.
-extern const char NullPointerContent[PointerSize];
-
-/// AArch64 pointer jump stub content.
-///
-/// Contains the instruction sequence for an indirect jump via an in-memory
-/// pointer:
-/// ADRP x16, ptr at page21
-/// LDR x16, [x16, ptr at pageoff12]
-/// BR x16
-extern const char PointerJumpStubContent[12];
-
-/// Creates a new pointer block in the given section and returns an
-/// Anonymous symbol pointing to it.
-///
-/// If InitialTarget is given then an Pointer64 relocation will be added to the
-/// block pointing at InitialTarget.
-///
-/// The pointer block will have the following default values:
-/// alignment: 64-bit
-/// alignment-offset: 0
-/// address: highest allowable (~7U)
-inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
- Symbol *InitialTarget = nullptr,
- uint64_t InitialAddend = 0) {
- auto &B = G.createContentBlock(PointerSection, NullPointerContent,
- orc::ExecutorAddr(~uint64_t(7)), 8, 0);
- if (InitialTarget)
- B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
- return G.addAnonymousSymbol(B, 0, 8, false, false);
-}
-
-/// Create a jump stub block that jumps via the pointer at the given symbol.
-///
-/// The stub block will have the following default values:
-/// alignment: 32-bit
-/// alignment-offset: 0
-/// address: highest allowable: (~11U)
-inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
- Symbol &PointerSymbol) {
- auto &B = G.createContentBlock(StubSection, PointerJumpStubContent,
- orc::ExecutorAddr(~uint64_t(11)), 4, 0);
- B.addEdge(Page21, 0, PointerSymbol, 0);
- B.addEdge(PageOffset12, 4, PointerSymbol, 0);
- return B;
-}
-
-/// Create a jump stub that jumps via the pointer at the given symbol and
-/// an anonymous symbol pointing to it. Return the anonymous symbol.
-///
-/// The stub block will be created by createPointerJumpStubBlock.
-inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
- Section &StubSection,
- Symbol &PointerSymbol) {
- return G.addAnonymousSymbol(
- createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0,
- sizeof(PointerJumpStubContent), true, false);
-}
-
-/// Global Offset Table Builder.
-class GOTTableManager : public TableManager<GOTTableManager> {
-public:
- static StringRef getSectionName() { return "$__GOT"; }
-
- bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
- Edge::Kind KindToSet = Edge::Invalid;
- const char *BlockWorkingMem = B->getContent().data();
- const char *FixupPtr = BlockWorkingMem + E.getOffset();
-
- switch (E.getKind()) {
- case aarch64::RequestGOTAndTransformToPage21:
- case aarch64::RequestTLVPAndTransformToPage21: {
- KindToSet = aarch64::Page21;
- break;
- }
- case aarch64::RequestGOTAndTransformToPageOffset12:
- case aarch64::RequestTLVPAndTransformToPageOffset12: {
- KindToSet = aarch64::PageOffset12;
- uint32_t RawInstr = *(const support::ulittle32_t *)FixupPtr;
- (void)RawInstr;
- assert(E.getAddend() == 0 &&
- "GOTPageOffset12/TLVPageOffset12 with non-zero addend");
- assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
- "RawInstr isn't a 64-bit LDR immediate");
- break;
- }
- case aarch64::RequestGOTAndTransformToDelta32: {
- KindToSet = aarch64::Delta32;
- break;
- }
- default:
- return false;
- }
- assert(KindToSet != Edge::Invalid &&
- "Fell through switch, but no new kind to set");
- DEBUG_WITH_TYPE("jitlink", {
- dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
- << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
- << formatv("{0:x}", E.getOffset()) << ")\n";
- });
- E.setKind(KindToSet);
- E.setTarget(getEntryForTarget(G, E.getTarget()));
- return true;
- }
-
- Symbol &createEntry(LinkGraph &G, Symbol &Target) {
- return createAnonymousPointer(G, getGOTSection(G), &Target);
- }
-
-private:
- Section &getGOTSection(LinkGraph &G) {
- if (!GOTSection)
- GOTSection = &G.createSection(getSectionName(),
- orc::MemProt::Read | orc::MemProt::Exec);
- return *GOTSection;
- }
-
- Section *GOTSection = nullptr;
-};
-
/// Procedure Linkage Table Builder.
class PLTTableManager : public TableManager<PLTTableManager> {
public:
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
index 9ce8aecb717ca..fcfb72e283e4d 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
@@ -70,6 +70,7 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
ELFPrel64,
ELFAdrGOTPage21,
ELFLd64GOTLo12,
+ ELFLd64GOTPAGELo15,
ELFTLSDescAdrPage21,
ELFTLSDescAddLo12,
ELFTLSDescLd64Lo12,
@@ -125,6 +126,8 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
return ELFAdrGOTPage21;
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
return ELFLd64GOTLo12;
+ case ELF::R_AARCH64_LD64_GOTPAGE_LO15:
+ return ELFLd64GOTPAGELo15;
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
return ELFTLSDescAdrPage21;
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
@@ -362,6 +365,10 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
Kind = aarch64::RequestGOTAndTransformToPageOffset12;
break;
}
+ case ELFLd64GOTPAGELo15: {
+ Kind = aarch64::RequestGOTAndTransformToPageOffset15;
+ break;
+ }
case ELFTLSDescAdrPage21: {
Kind = aarch64::RequestTLSDescEntryAndTransformToPage21;
break;
@@ -427,6 +434,8 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
return "ELFAdrGOTPage21";
case ELFLd64GOTLo12:
return "ELFLd64GOTLo12";
+ case ELFLd64GOTPAGELo15:
+ return "ELFLd64GOTPAGELo15";
case ELFTLSDescAdrPage21:
return "ELFTLSDescAdrPage21";
case ELFTLSDescAddLo12:
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s
index 75e367bee80a9..aef96f0e31ddb 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s
@@ -264,6 +264,20 @@ 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) = \
More information about the llvm-commits
mailing list