From ff9fa05905be716435460b5f6d32689041bf895b Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Mon, 19 Aug 2013 23:27:43 +0000 Subject: Adding PIC support for ELF on x86_64 platforms git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@188726 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp | 8 + lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp | 202 ++++++++++++++++++++- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h | 19 +- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h | 31 +++- 4 files changed, 244 insertions(+), 16 deletions(-) diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index a00b951e0a..d36126a9e2 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -169,6 +169,9 @@ ObjectImage *RuntimeDyldImpl::loadObject(ObjectBuffer *InputBuffer) { } } + // Give the subclasses a chance to tie-up any loose ends. + finalizeLoad(); + return obj.take(); } @@ -424,6 +427,10 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) { 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::resolveExternalSymbols() { // 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"); diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index 6ce4f672bf..6504f0e2d7 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -202,7 +202,8 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section, 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_64Relocation(const SectionEntry &Section, << " 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(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_64Relocation(const SectionEntry &Section, *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(Section.ObjAddress + + Offset); + uint64_t *Target = reinterpret_cast(Section.Address + Offset); + uint64_t FinalAddress = Section.LoadAddress + Offset; + *Target = *Placeholder + Value + Addend - FinalAddress; + break; + } } } @@ -584,7 +610,7 @@ void RuntimeDyldELF::findOPDEntrySection(ObjectImage &Obj, // 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::resolveSystemZRelocation(const SectionEntry &Section, 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::processRelocationRef(unsigned SectionID, } 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::processRelocationRef(unsigned SectionID, 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::processRelocationRef(unsigned SectionID, 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::processRelocationRef(unsigned SectionID, 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::processRelocationRef(unsigned SectionID, } } +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; diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index 794c7ece10..14fb5740e5 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -35,13 +35,15 @@ class RuntimeDyldELF : public RuntimeDyldImpl { 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 RuntimeDyldImpl { 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 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(); }; diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index 14d945b5b7..77e25626d2 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -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 -- cgit v1.2.3