[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