[llvm] 995f199 - [JITLink][PowerPC] Correct handling of R_PPC64_REL24_NOTOC

Kai Luo via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 23 23:01:00 PDT 2023


Author: Kai Luo
Date: 2023-07-24T14:00:53+08:00
New Revision: 995f199f0a7645bac81f1786a623d66ca44dd415

URL: https://github.com/llvm/llvm-project/commit/995f199f0a7645bac81f1786a623d66ca44dd415
DIFF: https://github.com/llvm/llvm-project/commit/995f199f0a7645bac81f1786a623d66ca44dd415.diff

LOG: [JITLink][PowerPC] Correct handling of R_PPC64_REL24_NOTOC

According to the ELFv2 ABI

> This relocation type is used to specify a function call where the TOC pointer is not initialized. It is similar to R_PPC64_REL24 in that it specifies a symbol to be resolved. If the symbol resolves to a function that requires a TOC pointer (as determined by st_other bits) then a link editor must arrange for the call to be via the global entry point of the called function. Any stub code must not rely on a valid TOC base address in r2.

This patch fixes handling of `R_PPC64_REL24_NOTOC` by using the same stub code sequence as lld.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D155672

Added: 
    

Modified: 
    llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h
    llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp
    llvm/lib/ExecutionEngine/JITLink/ppc64.cpp
    llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64le_relocations.s

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h
index d4b7256e563a9e..6d6a5a1a93458b 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h
@@ -36,15 +36,69 @@ enum EdgeKind_ppc64 : Edge::Kind {
   CallBranchDelta,
   // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
   CallBranchDeltaRestoreTOC,
-  // Need PLT call stub.
+  // Need PLT call stub using TOC, TOC pointer is not saved before branching.
   RequestPLTCallStub,
-  // Need PLT call stub following a save of r2.
+  // Need PLT call stub using TOC, TOC pointer is saved before branching.
   RequestPLTCallStubSaveTOC,
+  // Need PLT call stub without using TOC.
+  RequestPLTCallStubNoTOC,
+};
+
+enum PLTCallStubKind {
+  LongBranch,
+  LongBranchSaveR2,
+  LongBranchNoTOC,
 };
 
 extern const char NullPointerContent[8];
 extern const char PointerJumpStubContent_big[20];
 extern const char PointerJumpStubContent_little[20];
+extern const char PointerJumpStubNoTOCContent_big[32];
+extern const char PointerJumpStubNoTOCContent_little[32];
+
+struct PLTCallStubReloc {
+  Edge::Kind K;
+  size_t Offset;
+  Edge::AddendT A;
+};
+
+struct PLTCallStubInfo {
+  ArrayRef<char> Content;
+  SmallVector<PLTCallStubReloc, 2> Relocs;
+};
+
+template <support::endianness Endianness>
+inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
+  constexpr bool isLE = Endianness == support::endianness::little;
+  switch (StubKind) {
+  case LongBranch: {
+    ArrayRef<char> Content =
+        isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
+    // Skip save r2.
+    Content = Content.slice(4);
+    return PLTCallStubInfo{
+        Content,
+        {{TOCDelta16HA, 0, 0}, {TOCDelta16LO, 4, 0}},
+    };
+  }
+  case LongBranchSaveR2: {
+    ArrayRef<char> Content =
+        isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
+    return PLTCallStubInfo{
+        Content,
+        {{TOCDelta16HA, 4, 0}, {TOCDelta16LO, 8, 0}},
+    };
+  }
+  case LongBranchNoTOC: {
+    ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
+                                  : PointerJumpStubNoTOCContent_big;
+    return PLTCallStubInfo{
+        Content,
+        {{Delta16HA, 16, 8}, {Delta16LO, 20, 12}},
+    };
+  }
+  }
+}
 
 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
                                       Symbol *InitialTarget = nullptr,
@@ -60,32 +114,16 @@ inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
 }
 
 template <support::endianness Endianness>
-inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
-                                         Symbol &PointerSymbol, bool SaveR2) {
-  constexpr bool isLE = Endianness == support::endianness::little;
-  ArrayRef<char> C =
-      isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
-  if (!SaveR2)
-    // Skip storing r2.
-    C = C.slice(4);
-  Block &B = G.createContentBlock(StubSection, C, orc::ExecutorAddr(), 4, 0);
-  size_t Offset = SaveR2 ? 4 : 0;
-  B.addEdge(TOCDelta16HA, Offset, PointerSymbol, 0);
-  B.addEdge(TOCDelta16LO, Offset + 4, PointerSymbol, 0);
-  return B;
-}
-
-template <support::endianness Endianness>
-inline Symbol &
-createAnonymousPointerJumpStub(LinkGraph &G, Section &StubSection,
-                               Symbol &PointerSymbol, bool SaveR2) {
-  constexpr bool isLE = Endianness == support::endianness::little;
-  constexpr ArrayRef<char> Stub =
-      isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
-  return G.addAnonymousSymbol(createPointerJumpStubBlock<Endianness>(
-                                  G, StubSection, PointerSymbol, SaveR2),
-                              0, SaveR2 ? sizeof(Stub) : sizeof(Stub) - 4, true,
-                              false);
+inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
+                                              Section &StubSection,
+                                              Symbol &PointerSymbol,
+                                              PLTCallStubKind StubKind) {
+  PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
+  Block &B = G.createContentBlock(StubSection, StubInfo.Content,
+                                  orc::ExecutorAddr(), 4, 0);
+  for (auto const &Reloc : StubInfo.Relocs)
+    B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
+  return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
 }
 
 template <support::endianness Endianness>
@@ -138,13 +176,13 @@ class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
     Edge::Kind K = E.getKind();
     if (K == ppc64::RequestPLTCallStubSaveTOC && E.getTarget().isExternal()) {
       E.setKind(ppc64::CallBranchDeltaRestoreTOC);
-      this->SaveR2InStub = true;
+      this->StubKind = LongBranchSaveR2;
       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
       return true;
     }
-    if (K == ppc64::RequestPLTCallStub && E.getTarget().isExternal()) {
+    if (K == ppc64::RequestPLTCallStubNoTOC && E.getTarget().isExternal()) {
       E.setKind(ppc64::CallBranchDelta);
-      this->SaveR2InStub = false;
+      this->StubKind = LongBranchNoTOC;
       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
       return true;
     }
@@ -154,7 +192,7 @@ class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
     return createAnonymousPointerJumpStub<Endianness>(
         G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
-        this->SaveR2InStub);
+        this->StubKind);
   }
 
 private:
@@ -168,7 +206,7 @@ class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
 
   TOCTableManager<Endianness> &TOC;
   Section *PLTSection = nullptr;
-  bool SaveR2InStub = false;
+  PLTCallStubKind StubKind;
 };
 
 /// Returns a string name for the given ppc64 edge. For debugging purposes

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp
index f6f965b7063869..a30b9ce51c8482 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp
@@ -230,7 +230,7 @@ class ELFLinkGraphBuilder_ppc64
         Addend += ELF::decodePPC64LocalEntryOffset((*ObjSymbol)->st_other);
       } else {
         Kind = ELFReloc == ELF::R_PPC64_REL24 ? ppc64::RequestPLTCallStubSaveTOC
-                                              : ppc64::RequestPLTCallStub;
+                                              : ppc64::RequestPLTCallStubNoTOC;
       }
       break;
     }

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp b/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp
index 3ad959107a2286..4e21eace21d086 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp
@@ -35,6 +35,29 @@ const char PointerJumpStubContent_big[20] = {
     0x4e,       (char)0x80, 0x04, 0x20,       // bctr
 };
 
+// TODO: We can use prefixed instructions if LLJIT is running on power10.
+const char PointerJumpStubNoTOCContent_little[32] = {
+    (char)0xa6, 0x02,       (char)0x88, 0x7d,       // mflr 12
+    0x05,       (char)0x00, (char)0x9f, 0x42,       // bcl 20,31,.+4
+    (char)0xa6, 0x02,       0x68,       0x7d,       // mflr 11
+    (char)0xa6, 0x03,       (char)0x88, 0x7d,       // mtlr 12
+    0x00,       0x00,       (char)0x8b, 0x3d,       // addis 12,11,OffHa
+    0x00,       0x00,       (char)0x8c, (char)0xe9, // ld 12, OffLo(12)
+    (char)0xa6, 0x03,       (char)0x89, 0x7d,       // mtctr 12
+    0x20,       0x04,       (char)0x80, 0x4e,       // bctr
+};
+
+const char PointerJumpStubNoTOCContent_big[32] = {
+    0x7d,       (char)0x88, 0x02, (char)0xa6, // mflr 12
+    0x42,       (char)0x9f, 0x00, 0x05,       // bcl 20,31,.+4
+    0x7d,       0x68,       0x02, (char)0xa6, // mflr 11
+    0x7d,       (char)0x88, 0x03, (char)0xa6, // mtlr 12
+    0x3d,       (char)0x8b, 0x00, 0x00,       // addis 12,11,OffHa
+    (char)0xe9, (char)0x8c, 0x00, 0x00,       // ld 12, OffLo(12)
+    0x7d,       (char)0x89, 0x03, (char)0xa6, // mtctr 12
+    0x4e,       (char)0x80, 0x04, 0x20,       // bctr
+};
+
 const char *getEdgeKindName(Edge::Kind K) {
   switch (K) {
   case Pointer64:
@@ -69,6 +92,8 @@ const char *getEdgeKindName(Edge::Kind K) {
     return "RequestPLTCallStub";
   case RequestPLTCallStubSaveTOC:
     return "RequestPLTCallStubSaveTOC";
+  case RequestPLTCallStubNoTOC:
+    return "RequestPLTCallStubNoTOC";
   default:
     return getGenericEdgeKindName(static_cast<Edge::Kind>(K));
   }

diff  --git a/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64le_relocations.s b/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64le_relocations.s
index 230a106ce81377..0890525622d310 100644
--- a/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64le_relocations.s
+++ b/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64le_relocations.s
@@ -4,6 +4,7 @@
 # RUN: llvm-jitlink --noexec \
 # RUN:              --abs external_data=0xdeadbeef \
 # RUN:              --abs external_func=0xcafef00d \
+# RUN:              --abs external_func_notoc=0x88880000 \
 # RUN:              --check %s %t/elf_reloc.o
 
 # jitlink-check: section_addr(elf_reloc.o, $__GOT) + 0x8000 = __TOC__
@@ -52,6 +53,8 @@ test_tocrel16:
 # jitlink-check: *{8}(got_addr(elf_reloc.o, external_func)) = external_func
 # jitlink-check: decode_operand(test_external_call, 0) = \
 # jitlink-check:   (stub_addr(elf_reloc.o, external_func) - test_external_call) >> 2
+# Check r2 is saved.
+# jitlink-check: *{4}(stub_addr(elf_reloc.o, external_func)) = 0xf8410018
   .global test_external_call
   .p2align 4
   .type test_external_call, at function
@@ -61,6 +64,24 @@ test_external_call:
   blr
   .size test_external_call, .-test_external_call
 
+# FIXME: Current implementation allows only one plt call stub for a target function,
+# so we can't re-use `external_func` as target here.
+# Check R_PPC64_REL24_NOTOC
+# jitlink-check: *{8}(got_addr(elf_reloc.o, external_func_notoc)) = external_func_notoc
+# jitlink-check: decode_operand(test_external_call_notoc, 0) = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func_notoc) - test_external_call_notoc) >> 2
+# jitlink-check: (*{4}(stub_addr(elf_reloc.o, external_func_notoc) + 16)) & 0xffff = \
+# jitlink-check:   ((((got_addr(elf_reloc.o, external_func_notoc) - stub_addr(elf_reloc.o, external_func_notoc)) - 8) + 0x8000) >> 16) & 0xffff
+# jitlink-check: (*{4}(stub_addr(elf_reloc.o, external_func_notoc) + 20)) & 0xffff = \
+# jitlink-check:   ((got_addr(elf_reloc.o, external_func_notoc) - stub_addr(elf_reloc.o, external_func_notoc)) - 8) & 0xffff
+  .global test_external_call_notoc
+  .p2align 4
+  .type test_external_call_notoc, at function
+test_external_call_notoc:
+  bl external_func_notoc at notoc
+  blr
+  .size test_external_call_notoc, .-test_external_call_notoc
+
  .section .toc,"aw", at progbits
 .LC0:
   .tc external_data[TC],external_data


        


More information about the llvm-commits mailing list