[llvm] 1b123d9 - Adds support for GOT relocations to i386/ELF backend

Kshitij Jain via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 24 18:06:48 PST 2022


Author: Kshitij Jain
Date: 2022-12-25T02:06:24Z
New Revision: 1b123d9fb5aaa0757b12441ae78f27ec4a25747e

URL: https://github.com/llvm/llvm-project/commit/1b123d9fb5aaa0757b12441ae78f27ec4a25747e
DIFF: https://github.com/llvm/llvm-project/commit/1b123d9fb5aaa0757b12441ae78f27ec4a25747e.diff

LOG: Adds support for GOT relocations to i386/ELF backend

This CR adds support for GOT relocations to the JITLink i386/ELF backend.

Reviewed By: lhames

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

Added: 
    llvm/test/ExecutionEngine/JITLink/i386/ELF_external_to_absolute_conversion.s
    llvm/test/ExecutionEngine/JITLink/i386/ELF_i386_small_pic_relocations.s

Modified: 
    llvm/include/llvm/ExecutionEngine/JITLink/i386.h
    llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp
    llvm/lib/ExecutionEngine/JITLink/i386.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/i386.h b/llvm/include/llvm/ExecutionEngine/JITLink/i386.h
index 8698f0aad623..a590713625d2 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/i386.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/i386.h
@@ -81,7 +81,19 @@ enum EdgeKind_i386 : Edge::Kind {
   ///
   PCRel16,
 
-  /// A 64-bit GOT delta.
+  /// A 32-bit delta.
+  ///
+  /// Delta from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target - Fixup + Addend : int64
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int32, otherwise
+  ///     an out-of-range error will be returned.
+  Delta32,
+
+  /// A 32-bit GOT delta.
   ///
   /// Delta from the global offset table to the target.
   ///
@@ -92,6 +104,26 @@ enum EdgeKind_i386 : Edge::Kind {
   ///   - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
   ///     symbol was not been defined.
   Delta32FromGOT,
+
+  /// A GOT entry offset within GOT getter/constructor, transformed to
+  /// Delta32FromGOT pointing at the GOT entry for the original target.
+  ///
+  /// Indicates that this edge should be transformed into a Delta32FromGOT
+  /// 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.
+  ///
+  /// Edges of this kind are usually handled by a GOT builder pass inserted by
+  /// default
+  ///
+  /// 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
+  RequestGOTAndTransformToDelta32FromGOT,
 };
 
 /// Returns a string name for the given i386 edge. For debugging purposes
@@ -110,7 +142,8 @@ inline bool isInRangeForImmS16(int32_t Value) {
 }
 
 /// Apply fixup expression for edge to block content.
-inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
+inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
+                        const Symbol *GOTSymbol) {
   using namespace i386;
   using namespace llvm::support;
 
@@ -155,6 +188,20 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
     break;
   }
 
+  case i386::Delta32: {
+    int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
+    *(little32_t *)FixupPtr = Value;
+    break;
+  }
+
+  case i386::Delta32FromGOT: {
+    assert(GOTSymbol && "No GOT section symbol");
+    int32_t Value =
+        E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend();
+    *(little32_t *)FixupPtr = Value;
+    break;
+  }
+
   default:
     return make_error<JITLinkError>(
         "In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -163,6 +210,79 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
 
   return Error::success();
 }
+
+/// i386 pointer size.
+constexpr uint32_t PointerSize = 4;
+
+/// i386 null pointer content.
+extern const char NullPointerContent[PointerSize];
+
+/// Creates a new pointer block in the given section and returns an anonymous
+/// symbol pointing to it.
+///
+/// If InitialTarget is given then an Pointer32 relocation will be added to the
+/// block pointing at InitialTarget.
+///
+/// The pointer block will have the following default values:
+///   alignment: 32-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(), 8, 0);
+  if (InitialTarget)
+    B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend);
+  return G.addAnonymousSymbol(B, 0, PointerSize, false, 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;
+    switch (E.getKind()) {
+    case i386::Delta32FromGOT: {
+      // we need to make sure that the GOT section exists, but don't otherwise
+      // need to fix up this edge
+      getGOTSection(G);
+      return false;
+    }
+    case i386::RequestGOTAndTransformToDelta32FromGOT:
+      KindToSet = i386::Delta32FromGOT;
+      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);
+    return *GOTSection;
+  }
+
+  Section *GOTSection = nullptr;
+};
+
 } // namespace llvm::jitlink::i386
 
 #endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp
index a5c4d7e660bc..07f5ecc78942 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp
@@ -17,11 +17,25 @@
 #include "llvm/ExecutionEngine/JITLink/i386.h"
 #include "llvm/Object/ELFObjectFile.h"
 
+#include "DefineExternalSectionStartAndEndSymbols.h"
+
 #define DEBUG_TYPE "jitlink"
 
 using namespace llvm;
 using namespace llvm::jitlink;
 
+namespace {
+constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
+
+Error buildTables_ELF_i386(LinkGraph &G) {
+  LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
+
+  i386::GOTTableManager GOT;
+  visitExistingEdges(G, GOT);
+  return Error::success();
+}
+} // namespace
+
 namespace llvm::jitlink {
 
 class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> {
@@ -30,11 +44,68 @@ class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> {
 public:
   ELFJITLinker_i386(std::unique_ptr<JITLinkContext> Ctx,
                     std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
-      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
+      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {
+    getPassConfig().PostAllocationPasses.push_back(
+        [this](LinkGraph &G) { return getOrCreateGOTSymbol(G); });
+  }
 
 private:
+  Symbol *GOTSymbol = nullptr;
+
+  Error getOrCreateGOTSymbol(LinkGraph &G) {
+    auto DefineExternalGOTSymbolIfPresent =
+        createDefineExternalSectionStartAndEndSymbolsPass(
+            [&](LinkGraph &LG, Symbol &Sym) -> SectionRangeSymbolDesc {
+              if (Sym.getName() == ELFGOTSymbolName)
+                if (auto *GOTSection = G.findSectionByName(
+                        i386::GOTTableManager::getSectionName())) {
+                  GOTSymbol = &Sym;
+                  return {*GOTSection, true};
+                }
+              return {};
+            });
+
+    // Try to attach _GLOBAL_OFFSET_TABLE_ to the GOT if it's defined as an
+    // external.
+    if (auto Err = DefineExternalGOTSymbolIfPresent(G))
+      return Err;
+
+    // If we succeeded then we're done.
+    if (GOTSymbol)
+      return Error::success();
+
+    // Otherwise look for a GOT section: If it already has a start symbol we'll
+    // record it, otherwise we'll create our own.
+    // If there's a GOT section but we didn't find an external GOT symbol...
+    if (auto *GOTSection =
+            G.findSectionByName(i386::GOTTableManager::getSectionName())) {
+
+      // Check for an existing defined symbol.
+      for (auto *Sym : GOTSection->symbols())
+        if (Sym->getName() == ELFGOTSymbolName) {
+          GOTSymbol = Sym;
+          return Error::success();
+        }
+
+      // If there's no defined symbol then create one.
+      SectionRange SR(*GOTSection);
+
+      if (SR.empty()) {
+        GOTSymbol =
+            &G.addAbsoluteSymbol(ELFGOTSymbolName, orc::ExecutorAddr(), 0,
+                                 Linkage::Strong, Scope::Local, true);
+      } else {
+        GOTSymbol =
+            &G.addDefinedSymbol(*SR.getFirstBlock(), 0, ELFGOTSymbolName, 0,
+                                Linkage::Strong, Scope::Local, false, true);
+      }
+    }
+
+    return Error::success();
+  }
+
   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
-    return i386::applyFixup(G, B, E);
+    return i386::applyFixup(G, B, E, GOTSymbol);
   }
 };
 
@@ -54,7 +125,11 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
       return EdgeKind_i386::Pointer16;
     case ELF::R_386_PC16:
       return EdgeKind_i386::PCRel16;
+    case ELF::R_386_GOT32:
+      return EdgeKind_i386::RequestGOTAndTransformToDelta32FromGOT;
     case ELF::R_386_GOTPC:
+      return EdgeKind_i386::Delta32;
+    case ELF::R_386_GOTOFF:
       return EdgeKind_i386::Delta32FromGOT;
     }
 
@@ -105,10 +180,6 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
     if (!Kind)
       return Kind.takeError();
 
-    // TODO: To be removed when GOT relative relocations are supported.
-    if (*Kind == i386::EdgeKind_i386::Delta32FromGOT)
-      return Error::success();
-
     int64_t Addend = 0;
 
     auto FixupAddress = orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset;
@@ -121,7 +192,6 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
     });
 
     BlockToFix.addEdge(std::move(GE));
-
     return Error::success();
   }
 
@@ -162,6 +232,9 @@ void link_ELF_i386(std::unique_ptr<LinkGraph> G,
       Config.PrePrunePasses.push_back(std::move(MarkLive));
     else
       Config.PrePrunePasses.push_back(markAllSymbolsLive);
+
+    // Add an in-place GOT build pass.
+    Config.PostPrunePasses.push_back(buildTables_ELF_i386);
   }
   if (auto Err = Ctx->modifyPassConfig(*G, Config))
     return Ctx->notifyFailed(std::move(Err));

diff  --git a/llvm/lib/ExecutionEngine/JITLink/i386.cpp b/llvm/lib/ExecutionEngine/JITLink/i386.cpp
index e9e5ecc3754e..c2c5761cd272 100644
--- a/llvm/lib/ExecutionEngine/JITLink/i386.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/i386.cpp
@@ -28,8 +28,16 @@ const char *getEdgeKindName(Edge::Kind K) {
     return "Pointer16";
   case PCRel16:
     return "PCRel16";
+  case Delta32:
+    return "Delta32";
+  case Delta32FromGOT:
+    return "Delta32FromGOT";
+  case RequestGOTAndTransformToDelta32FromGOT:
+    return "RequestGOTAndTransformToDelta32FromGOT";
   }
+
   return getGenericEdgeKindName(K);
 }
 
+const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00};
 } // namespace llvm::jitlink::i386

diff  --git a/llvm/test/ExecutionEngine/JITLink/i386/ELF_external_to_absolute_conversion.s b/llvm/test/ExecutionEngine/JITLink/i386/ELF_external_to_absolute_conversion.s
new file mode 100644
index 000000000000..15f7ecb3642f
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/i386/ELF_external_to_absolute_conversion.s
@@ -0,0 +1,41 @@
+# RUN: llvm-mc -triple=i386-unknown-linux-gnu -filetype=obj -o %t %s
+# RUN: llvm-jitlink -noexec %t
+#
+# Check that symbol scope is demoted to local when external symbols are
+# converted to absolutes. This is demotion is necessary to avoid "unexpected
+# definition" errors.
+#
+# The reference to _GLOBAL_OFFSET_TABLE_ will trigger creation of an external
+# _GLOBAL_OFFSET_TABLE_ symbol, and the GOTOFF relocation will force creation
+# of a GOT symbol without actually introducing any GOT entries. Together these
+# should cause the external _GLOBAL_OFFSET_TABLE_ symbol to be converted to an
+# absolute symbol with address zero. If the scope is not demoted correctly this
+# will trigger an "unexpected definition" error.
+
+        .text
+        .globl  main                            
+        .p2align        4, 0x90
+        .type   main, at function
+main:                    
+        pushl   %ebp
+        movl    %esp, %ebp
+        pushl   %eax
+        calll   .L0$pb
+.L0$pb:
+        popl    %eax
+.Ltmp0:
+        addl    $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
+        movl    $0, -4(%ebp)
+        movl    a at GOTOFF(%eax), %eax
+        addl    $4, %esp
+        popl    %ebp
+        retl
+        .size   main, .-main
+
+
+        .type   a, at object                       # @a
+        .data
+        .p2align        2
+a:
+        .long   42                              # 0x2a
+        .size   a, 4
\ No newline at end of file

diff  --git a/llvm/test/ExecutionEngine/JITLink/i386/ELF_i386_small_pic_relocations.s b/llvm/test/ExecutionEngine/JITLink/i386/ELF_i386_small_pic_relocations.s
new file mode 100644
index 000000000000..cb379d51f5d0
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/i386/ELF_i386_small_pic_relocations.s
@@ -0,0 +1,68 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=i386-unknown-linux-gnu -position-independent \
+# RUN:  -filetype=obj -o %t/elf_sm_pic_reloc.o %s
+# RUN: llvm-jitlink -noexec \
+# RUN:     -slab-allocate 100Kb -slab-address 0xfff00000 -slab-page-size 4096 \
+# RUN:     -check %s %t/elf_sm_pic_reloc.o
+#
+# Test ELF small/PIC relocations.
+
+        .text
+        .globl  main
+        .p2align        4, 0x90
+        .type   main, at function
+main:
+        ret
+        .size   main, .-main
+
+
+# Test GOT32 handling.
+# 
+# We want to check both the offset to the GOT entry and its contents. 
+# jitlink-check: decode_operand(test_got, 4) = got_addr(elf_sm_pic_reloc.o, named_data1) - _GLOBAL_OFFSET_TABLE_
+# jitlink-check: *{4}(got_addr(elf_sm_pic_reloc.o, named_data1)) = named_data1
+# 
+# jitlink-check: decode_operand(test_got+6, 4) = got_addr(elf_sm_pic_reloc.o, named_data2) - _GLOBAL_OFFSET_TABLE_
+# jitlink-check: *{4}(got_addr(elf_sm_pic_reloc.o, named_data2)) = named_data2
+
+        .globl test_got
+        .p2align      4, 0x90
+        .type   test_got, at function
+test_got:
+	leal    named_data1 at GOT, %eax
+        leal    named_data2 at GOT, %eax
+        .size   test_got, .-test_got
+
+
+
+# Test GOTOFF64 handling.
+# jitlink-check: decode_operand(test_gotoff, 1) = named_func - _GLOBAL_OFFSET_TABLE_
+        .globl test_gotoff
+        .p2align     4, 0x90
+        .type  test_gotoff, at function
+test_gotoff:
+        mov $named_func at GOTOFF, %eax
+        .size   test_gotoff, .-test_gotoff
+
+
+        .globl  named_func
+        .p2align       4, 0x90
+        .type   named_func, at function
+named_func:
+        xor    %eax, %eax
+        .size   named_func, .-named_func
+
+
+        .data
+
+        .type   named_data1, at object
+        .p2align        3
+named_data1:
+        .quad   42
+        .size   named_data1, 8
+        
+        .type   named_data2, at object
+        .p2align        3
+named_data2:
+        .quad   42
+        .size   named_data2, 8


        


More information about the llvm-commits mailing list