[LLVMdev] [llvm] r188726 - Adding PIC support for ELF on x86_64 platforms

Kaylor, Andrew andrew.kaylor at intel.com
Mon Jan 26 13:59:15 PST 2015


Hi Lang,

Yeah, I remember this case.  Basically what’s happening is that there are relocations for ELF on x86 that use a value that is present in the object image as part of the calculation for the final value that goes in the same location.  If you ever find yourself applying relocations for a second time (for instance, because the loaded object location is remapped for out-of-proc execution) the original value is no longer in the loaded object.

You could probably figure out a way to combine the placeholder value with the addend field, either while the relocation records are being built (though I’m not sure if the relevant sections have been loaded yet at that point) or the first time the relocation is applied (though it might be a bit cumbersome to know whether or not any given application was the first).

-Andy


From: Lang Hames [mailto:lhames at gmail.com]
Sent: Monday, January 26, 2015 1:14 PM
To: Kaylor, Andrew
Cc: Commit Messages and Patches for LLVM
Subject: Re: [llvm] r188726 - Adding PIC support for ELF on x86_64 platforms

Hi Andy,

In the following snippet, do you recall what made it necessary to read the value from the object file via Placeholder, rather than from the copied section?

+    // Get the placeholder value from the generated object since
+    // a previous relocation attempt may have overwritten the loaded version
+    uint64_t *Placeholder = reinterpret_cast<uint64_t*>(Section.ObjAddress
+                                                                   + Offset);
+    uint64_t *Target = reinterpret_cast<uint64_t*>(Section.Address + Offset);
+    uint64_t  FinalAddress = Section.LoadAddress + Offset;
+    *Target = *Placeholder + Value + Addend - FinalAddress;
+    break;

I''d like to make my new JIT APIs more aggressive about freeing the ObjectFile instances (ideally we'd be able to free immediately after a call to loadObject), but at the moment I have to hold it at least until resolveRelocations is called.

I had a quick chat with Tim Northover about this and our guess was that it had something to do with relocations being applied more than once?

Cheers,
Lang.


On Mon, Aug 19, 2013 at 4:27 PM, Andrew Kaylor <andrew.kaylor at intel.com<mailto:andrew.kaylor at intel.com>> wrote:
Author: akaylor
Date: Mon Aug 19 18:27:43 2013
New Revision: 188726

URL: http://llvm.org/viewvc/llvm-project?rev=188726&view=rev
Log:
Adding PIC support for ELF on x86_64 platforms

Modified:
    llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
    llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
    llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
    llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h

Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp?rev=188726&r1=188725&r2=188726&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp Mon Aug 19 18:27:43 2013
@@ -169,6 +169,9 @@ ObjectImage *RuntimeDyldImpl::loadObject
     }
   }

+  // Give the subclasses a chance to tie-up any loose ends.
+  finalizeLoad();
+
   return obj.take();
 }

@@ -424,6 +427,10 @@ uint8_t *RuntimeDyldImpl::createStubFunc
     writeInt16BE(Addr+6,  0x07F1);     // brc 15,%r1
     // 8-byte address stored at Addr + 8
     return Addr;
+  } else if (Arch == Triple::x86_64) {
+    *Addr      = 0xFF; // jmp
+    *(Addr+1)  = 0x25; // rip
+    // 32-bit PC-relative address of the GOT entry will be stored at Addr+2
   }
   return Addr;
 }
@@ -473,6 +480,7 @@ void RuntimeDyldImpl::resolveExternalSym
         // MemoryManager.
         uint8_t *Addr = (uint8_t*) MemMgr->getPointerToNamedFunction(Name.data(),
                                                                    true);
+        updateGOTEntries(Name, (uint64_t)Addr);
         DEBUG(dbgs() << "Resolving relocations Name: " << Name
                 << "\t" << format("%p", Addr)
                 << "\n");

Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp?rev=188726&r1=188725&r2=188726&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp Mon Aug 19 18:27:43 2013
@@ -202,7 +202,8 @@ void RuntimeDyldELF::resolveX86_64Reloca
                                              uint64_t Offset,
                                              uint64_t Value,
                                              uint32_t Type,
-                                             int64_t Addend) {
+                                             int64_t  Addend,
+                                             uint64_t SymOffset) {
   switch (Type) {
   default:
     llvm_unreachable("Relocation type not implemented yet!");
@@ -227,6 +228,21 @@ void RuntimeDyldELF::resolveX86_64Reloca
                  << " at " << format("%p\n",Target));
     break;
   }
+  case ELF::R_X86_64_GOTPCREL: {
+    // findGOTEntry returns the 'G + GOT' part of the relocation calculation
+    // based on the load/target address of the GOT (not the current/local addr).
+    uint64_t GOTAddr = findGOTEntry(Value, SymOffset);
+    uint32_t *Target = reinterpret_cast<uint32_t*>(Section.Address + Offset);
+    uint64_t  FinalAddress = Section.LoadAddress + Offset;
+    // The processRelocationRef method combines the symbol offset and the addend
+    // and in most cases that's what we want.  For this relocation type, we need
+    // the raw addend, so we subtract the symbol offset to get it.
+    int64_t RealOffset = GOTAddr + Addend - SymOffset - FinalAddress;
+    assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
+    int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
+    *Target = TruncOffset;
+    break;
+  }
   case ELF::R_X86_64_PC32: {
     // Get the placeholder value from the generated object since
     // a previous relocation attempt may have overwritten the loaded version
@@ -240,6 +256,16 @@ void RuntimeDyldELF::resolveX86_64Reloca
     *Target = TruncOffset;
     break;
   }
+  case ELF::R_X86_64_PC64: {
+    // Get the placeholder value from the generated object since
+    // a previous relocation attempt may have overwritten the loaded version
+    uint64_t *Placeholder = reinterpret_cast<uint64_t*>(Section.ObjAddress
+                                                                   + Offset);
+    uint64_t *Target = reinterpret_cast<uint64_t*>(Section.Address + Offset);
+    uint64_t  FinalAddress = Section.LoadAddress + Offset;
+    *Target = *Placeholder + Value + Addend - FinalAddress;
+    break;
+  }
   }
 }

@@ -584,7 +610,7 @@ void RuntimeDyldELF::findOPDEntrySection
       // Finally compares the Symbol value and the target symbol offset
       // to check if this .opd entry refers to the symbol the relocation
       // points to.
-      if (Rel.Addend != (intptr_t)TargetSymbolOffset)
+      if (Rel.Addend != (int64_t)TargetSymbolOffset)
         continue;

       section_iterator tsi(Obj.end_sections());
@@ -757,17 +783,19 @@ void RuntimeDyldELF::resolveSystemZReloc
 void RuntimeDyldELF::resolveRelocation(const RelocationEntry &RE,
                                        uint64_t Value) {
   const SectionEntry &Section = Sections[RE.SectionID];
-  return resolveRelocation(Section, RE.Offset, Value, RE.RelType, RE.Addend);
+  return resolveRelocation(Section, RE.Offset, Value, RE.RelType, RE.Addend,
+                           RE.SymOffset);
 }

 void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section,
                                        uint64_t Offset,
                                        uint64_t Value,
                                        uint32_t Type,
-                                       int64_t Addend) {
+                                       int64_t  Addend,
+                                       uint64_t SymOffset) {
   switch (Arch) {
   case Triple::x86_64:
-    resolveX86_64Relocation(Section, Offset, Value, Type, Addend);
+    resolveX86_64Relocation(Section, Offset, Value, Type, Addend, SymOffset);
     break;
   case Triple::x86:
     resolveX86Relocation(Section, Offset,
@@ -830,6 +858,7 @@ void RuntimeDyldELF::processRelocationRe
   }
   if (lsi != Symbols.end()) {
     Value.SectionID = lsi->second.first;
+    Value.Offset = lsi->second.second;
     Value.Addend = lsi->second.second + Addend;
   } else {
     // Search for the symbol in the global symbol table
@@ -838,6 +867,7 @@ void RuntimeDyldELF::processRelocationRe
       gsi = GlobalSymbolTable.find(TargetName.data());
     if (gsi != GlobalSymbolTable.end()) {
       Value.SectionID = gsi->second.first;
+      Value.Offset = gsi->second.second;
       Value.Addend = gsi->second.second + Addend;
     } else {
       switch (SymType) {
@@ -860,6 +890,7 @@ void RuntimeDyldELF::processRelocationRe
           Value.Addend = Addend;
           break;
         }
+        case SymbolRef::ST_Data:
         case SymbolRef::ST_Unknown: {
           Value.SymbolName = TargetName.data();
           Value.Addend = Addend;
@@ -1150,8 +1181,67 @@ void RuntimeDyldELF::processRelocationRe
                         ELF::R_390_PC32DBL, Addend);
     else
       resolveRelocation(Section, Offset, StubAddress, RelType, Addend);
+  } else if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_PLT32) {
+    // The way the PLT relocations normally work is that the linker allocates the
+    // PLT and this relocation makes a PC-relative call into the PLT.  The PLT
+    // entry will then jump to an address provided by the GOT.  On first call, the
+    // GOT address will point back into PLT code that resolves the symbol.  After
+    // the first call, the GOT entry points to the actual function.
+    //
+    // For local functions we're ignoring all of that here and just replacing
+    // the PLT32 relocation type with PC32, which will translate the relocation
+    // into a PC-relative call directly to the function. For external symbols we
+    // can't be sure the function will be within 2^32 bytes of the call site, so
+    // we need to create a stub, which calls into the GOT.  This case is
+    // equivalent to the usual PLT implementation except that we use the stub
+    // mechanism in RuntimeDyld (which puts stubs at the end of the section)
+    // rather than allocating a PLT section.
+    if (Value.SymbolName) {
+      // This is a call to an external function.
+      // Look for an existing stub.
+      SectionEntry &Section = Sections[SectionID];
+      StubMap::const_iterator i = Stubs.find(Value);
+      uintptr_t StubAddress;
+      if (i != Stubs.end()) {
+        StubAddress = uintptr_t(Section.Address) + i->second;
+        DEBUG(dbgs() << " Stub function found\n");
+      } else {
+        // Create a new stub function (equivalent to a PLT entry).
+        DEBUG(dbgs() << " Create a new stub function\n");
+
+        uintptr_t BaseAddress = uintptr_t(Section.Address);
+        uintptr_t StubAlignment = getStubAlignment();
+        StubAddress = (BaseAddress + Section.StubOffset +
+                      StubAlignment - 1) & -StubAlignment;
+        unsigned StubOffset = StubAddress - BaseAddress;
+        Stubs[Value] = StubOffset;
+        createStubFunction((uint8_t *)StubAddress);
+
+        // Create a GOT entry for the external function.
+        GOTEntries.push_back(Value);
+
+        // Make our stub function a relative call to the GOT entry.
+        RelocationEntry RE(SectionID, StubOffset + 2,
+                           ELF::R_X86_64_GOTPCREL, -4);
+        addRelocationForSymbol(RE, Value.SymbolName);
+
+        // Bump our stub offset counter
+        Section.StubOffset = StubOffset + getMaxStubSize();
+      }
+
+      // Make the target call a call into the stub table.
+      resolveRelocation(Section, Offset, StubAddress,
+                      ELF::R_X86_64_PC32, Addend);
+    } else {
+      RelocationEntry RE(SectionID, Offset, ELF::R_X86_64_PC32, Value.Addend,
+                         Value.Offset);
+      addRelocationForSection(RE, Value.SectionID);
+    }
   } else {
-    RelocationEntry RE(SectionID, Offset, RelType, Value.Addend);
+    if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
+      GOTEntries.push_back(Value);
+    }
+    RelocationEntry RE(SectionID, Offset, RelType, Value.Addend, Value.Offset);
     if (Value.SymbolName)
       addRelocationForSymbol(RE, Value.SymbolName);
     else
@@ -1159,6 +1249,106 @@ void RuntimeDyldELF::processRelocationRe
   }
 }

+void RuntimeDyldELF::updateGOTEntries(StringRef Name, uint64_t Addr) {
+  for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
+    if (GOTEntries[i].SymbolName != 0 && GOTEntries[i].SymbolName == Name) {
+      GOTEntries[i].Offset = Addr;
+    }
+  }
+}
+
+size_t RuntimeDyldELF::getGOTEntrySize() {
+  // We don't use the GOT in all of these cases, but it's essentially free
+  // to put them all here.
+  size_t Result = 0;
+  switch (Arch) {
+  case Triple::x86_64:
+  case Triple::aarch64:
+  case Triple::ppc64:
+  case Triple::ppc64le:
+  case Triple::systemz:
+    Result = sizeof(uint64_t);
+    break;
+  case Triple::x86:
+  case Triple::arm:
+  case Triple::thumb:
+  case Triple::mips:
+  case Triple::mipsel:
+    Result = sizeof(uint32_t);
+    break;
+  default: llvm_unreachable("Unsupported CPU type!");
+  }
+  return Result;
+}
+
+uint64_t RuntimeDyldELF::findGOTEntry(uint64_t LoadAddress,
+                                      uint64_t Offset) {
+  assert(GOTSectionID != 0
+         && "Attempting to lookup GOT entry but the GOT was never allocated.");
+  if (GOTSectionID == 0) {
+    return 0;
+  }
+
+  size_t GOTEntrySize = getGOTEntrySize();
+
+  // Find the matching entry in our vector.
+  int GOTIndex = -1;
+  uint64_t SymbolOffset = 0;
+  for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
+    if (GOTEntries[i].SymbolName == 0) {
+      if (getSectionLoadAddress(GOTEntries[i].SectionID) == LoadAddress &&
+          GOTEntries[i].Offset == Offset) {
+        GOTIndex = i;
+        SymbolOffset = GOTEntries[i].Offset;
+        break;
+      }
+    } else {
+      // GOT entries for external symbols use the addend as the address when
+      // the external symbol has been resolved.
+      if (GOTEntries[i].Offset == LoadAddress) {
+        GOTIndex = i;
+        // Don't use the Addend here.  The relocation handler will use it.
+        break;
+      }
+    }
+  }
+  assert(GOTIndex != -1 && "Unable to find requested GOT entry.");
+  if (GOTIndex == -1)
+    return 0;
+
+  if (GOTEntrySize == sizeof(uint64_t)) {
+    uint64_t *LocalGOTAddr = (uint64_t*)getSectionAddress(GOTSectionID);
+    // Fill in this entry with the address of the symbol being referenced.
+    LocalGOTAddr[GOTIndex] = LoadAddress + SymbolOffset;
+  } else {
+    uint32_t *LocalGOTAddr = (uint32_t*)getSectionAddress(GOTSectionID);
+    // Fill in this entry with the address of the symbol being referenced.
+    LocalGOTAddr[GOTIndex] = (uint32_t)(LoadAddress + SymbolOffset);
+  }
+
+  // Calculate the load address of this entry
+  return getSectionLoadAddress(GOTSectionID) + (GOTIndex * GOTEntrySize);
+}
+
+void RuntimeDyldELF::finalizeLoad() {
+  // Allocate the GOT if necessary
+  size_t numGOTEntries = GOTEntries.size();
+  if (numGOTEntries != 0) {
+    // Allocate memory for the section
+    unsigned SectionID = Sections.size();
+    size_t TotalSize = numGOTEntries * getGOTEntrySize();
+    uint8_t *Addr = MemMgr->allocateDataSection(TotalSize, getGOTEntrySize(),
+                                                SectionID, false);
+    if (!Addr)
+      report_fatal_error("Unable to allocate memory for GOT!");
+    Sections.push_back(SectionEntry(".got", Addr, TotalSize, 0));
+    // For now, initialize all GOT entries to zero.  We'll fill them in as
+    // needed when GOT-based relocations are applied.
+    memset(Addr, 0, TotalSize);
+    GOTSectionID = SectionID;
+  }
+}
+
 bool RuntimeDyldELF::isCompatibleFormat(const ObjectBuffer *Buffer) const {
   if (Buffer->getBufferSize() < strlen(ELF::ElfMagic))
     return false;

Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h?rev=188726&r1=188725&r2=188726&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h (original)
+++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h Mon Aug 19 18:27:43 2013
@@ -35,13 +35,15 @@ class RuntimeDyldELF : public RuntimeDyl
                          uint64_t Offset,
                          uint64_t Value,
                          uint32_t Type,
-                         int64_t Addend);
+                         int64_t Addend,
+                         uint64_t SymOffset=0);

   void resolveX86_64Relocation(const SectionEntry &Section,
                                uint64_t Offset,
                                uint64_t Value,
                                uint32_t Type,
-                               int64_t Addend);
+                               int64_t  Addend,
+                               uint64_t SymOffset);

   void resolveX86Relocation(const SectionEntry &Section,
                             uint64_t Offset,
@@ -84,8 +86,18 @@ class RuntimeDyldELF : public RuntimeDyl
                            ObjSectionToIDMap &LocalSections,
                            RelocationValueRef &Rel);

+  uint64_t findGOTEntry(uint64_t LoadAddr, uint64_t Offset);
+  size_t getGOTEntrySize();
+
+  virtual void updateGOTEntries(StringRef Name, uint64_t Addr);
+
+  SmallVector<RelocationValueRef, 2>  GOTEntries;
+  unsigned GOTSectionID;
+
 public:
-  RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm) {}
+  RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm),
+                                            GOTSectionID(0)
+                                          {}

   virtual void resolveRelocation(const RelocationEntry &RE, uint64_t Value);
   virtual void processRelocationRef(unsigned SectionID,
@@ -97,6 +109,7 @@ public:
   virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const;
   virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
   virtual StringRef getEHFrameSection();
+  virtual void finalizeLoad();
   virtual ~RuntimeDyldELF();
 };


Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h?rev=188726&r1=188725&r2=188726&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h (original)
+++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h Mon Aug 19 18:27:43 2013
@@ -80,14 +80,18 @@ public:
   unsigned SectionID;

   /// Offset - offset into the section.
-  uintptr_t Offset;
+  uint64_t Offset;

   /// RelType - relocation type.
   uint32_t RelType;

   /// Addend - the relocation addend encoded in the instruction itself.  Also
   /// used to make a relocation section relative instead of symbol relative.
-  intptr_t Addend;
+  int64_t Addend;
+
+  /// SymOffset - Section offset of the relocation entry's symbol (used for GOT
+  /// lookup).
+  uint64_t SymOffset;

   /// True if this is a PCRel relocation (MachO specific).
   bool IsPCRel;
@@ -97,20 +101,26 @@ public:

   RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend)
     : SectionID(id), Offset(offset), RelType(type), Addend(addend),
-      IsPCRel(false), Size(0) {}
+      SymOffset(0), IsPCRel(false), Size(0) {}
+
+  RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend,
+                  uint64_t symoffset)
+    : SectionID(id), Offset(offset), RelType(type), Addend(addend),
+      SymOffset(symoffset), IsPCRel(false), Size(0) {}

   RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend,
                   bool IsPCRel, unsigned Size)
     : SectionID(id), Offset(offset), RelType(type), Addend(addend),
-      IsPCRel(IsPCRel), Size(Size) {}
+      SymOffset(0), IsPCRel(IsPCRel), Size(Size) {}
 };

 class RelocationValueRef {
 public:
   unsigned  SectionID;
-  intptr_t  Addend;
+  uint64_t  Offset;
+  int64_t   Addend;
   const char *SymbolName;
-  RelocationValueRef(): SectionID(0), Addend(0), SymbolName(0) {}
+  RelocationValueRef(): SectionID(0), Offset(0), Addend(0), SymbolName(0) {}

   inline bool operator==(const RelocationValueRef &Other) const {
     return std::memcmp(this, &Other, sizeof(RelocationValueRef)) == 0;
@@ -175,7 +185,7 @@ protected:
     else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le)
       return 44;
     else if (Arch == Triple::x86_64)
-      return 8; // GOT
+      return 6; // 2-byte jmp instruction + 32-bit relative address
     else if (Arch == Triple::systemz)
       return 16;
     else
@@ -292,6 +302,11 @@ protected:

   /// \brief Resolve relocations to external symbols.
   void resolveExternalSymbols();
+
+  /// \brief Update GOT entries for external symbols.
+  // The base class does nothing.  ELF overrides this.
+  virtual void updateGOTEntries(StringRef Name, uint64_t Addr) {}
+
   virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
 public:
   RuntimeDyldImpl(RTDyldMemoryManager *mm) : MemMgr(mm), HasError(false) {}
@@ -336,6 +351,8 @@ public:
   virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0;

   virtual StringRef getEHFrameSection();
+
+  virtual void finalizeLoad() {}
 };

 } // end namespace llvm


_______________________________________________
llvm-commits mailing list
llvm-commits at cs.uiuc.edu<mailto:llvm-commits at cs.uiuc.edu>
http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150126/ee11bc95/attachment.html>


More information about the llvm-dev mailing list