From 752b5c9e6efe3d081862e531290f0552447dc0c0 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Sun, 20 Jul 2014 23:53:14 +0000 Subject: [PATCH] [PowerPC] ELFv2 dynamic loader support This patch enables the new ELFv2 ABI in the runtime dynamic loader. The loader has to implement the following features: - In the ELFv2 ABI, do not look up a function descriptor in .opd, but instead use the local entry point when resolving a direct call. - Update the TOC restore code to use the new TOC slot linkage area offset. - Create PLT stubs appropriate for the ELFv2 ABI. Note that this patch also adds common-code changes. These are necessary because the loader must check the newly added ELF flags: the e_flags header bits encoding the ABI version, and the st_other symbol table entry bits encoding the local entry point offset. There is currently no way to access these, so I've added ObjectFile::getPlatformFlags and SymbolRef::getOther accessors. Reviewed by Hal Finkel. llvm-svn: 213491 --- llvm/include/llvm/Object/ELFObjectFile.h | 13 +++++++++ llvm/include/llvm/Object/ObjectFile.h | 15 ++++++++++ .../ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp | 34 ++++++++++++++-------- .../ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp | 31 +++++++++++++++----- .../ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h | 2 +- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index cfb6b08..069f381 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -65,6 +65,7 @@ protected: uint32_t &Res) const override; std::error_code getSymbolSize(DataRefImpl Symb, uint64_t &Res) const override; uint32_t getSymbolFlags(DataRefImpl Symb) const override; + std::error_code getSymbolOther(DataRefImpl Symb, uint8_t &Res) const override; std::error_code getSymbolType(DataRefImpl Symb, SymbolRef::Type &Res) const override; std::error_code getSymbolSection(DataRefImpl Symb, @@ -202,6 +203,11 @@ public: unsigned getArch() const override; StringRef getLoadName() const override; + std::error_code getPlatformFlags(unsigned &Result) const override { + Result = EF.getHeader()->e_flags; + return object_error::success; + } + const ELFFile *getELFFile() const { return &EF; } bool isDyldType() const { return isDyldELFObject; } @@ -295,6 +301,13 @@ std::error_code ELFObjectFile::getSymbolSize(DataRefImpl Symb, } template +std::error_code ELFObjectFile::getSymbolOther(DataRefImpl Symb, + uint8_t &Result) const { + Result = toELFSymIter(Symb)->st_other; + return object_error::success; +} + +template std::error_code ELFObjectFile::getSymbolType(DataRefImpl Symb, SymbolRef::Type &Result) const { diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h index 646abf8..a4370a3 100644 --- a/llvm/include/llvm/Object/ObjectFile.h +++ b/llvm/include/llvm/Object/ObjectFile.h @@ -149,6 +149,7 @@ public: std::error_code getAlignment(uint32_t &Result) const; std::error_code getSize(uint64_t &Result) const; std::error_code getType(SymbolRef::Type &Result) const; + std::error_code getOther(uint8_t &Result) const; /// @brief Get section this symbol is defined in reference to. Result is /// end_sections() if it is undefined or is an absolute symbol. @@ -237,6 +238,10 @@ protected: SymbolRef::Type &Res) const = 0; virtual std::error_code getSymbolSection(DataRefImpl Symb, section_iterator &Res) const = 0; + virtual std::error_code getSymbolOther(DataRefImpl Symb, + uint8_t &Res) const { + return object_error::invalid_file_type; + } // Same as above for SectionRef. friend class SectionRef; @@ -328,6 +333,12 @@ public: /// LC_ID_DYLIB (install name) on MachO. virtual StringRef getLoadName() const = 0; + /// Returns platform-specific object flags, if any. + virtual std::error_code getPlatformFlags(unsigned &Result) const { + Result = 0; + return object_error::invalid_file_type; + } + /// @returns Pointer to ObjectFile subclass to handle this type of object. /// @param ObjectPath The path to the object file. ObjectPath.isObject must /// return true. @@ -383,6 +394,10 @@ inline std::error_code SymbolRef::getType(SymbolRef::Type &Result) const { return getObject()->getSymbolType(getRawDataRefImpl(), Result); } +inline std::error_code SymbolRef::getOther(uint8_t &Result) const { + return getObject()->getSymbolOther(getRawDataRefImpl(), Result); +} + inline const ObjectFile *SymbolRef::getObject() const { const SymbolicFile *O = BasicSymbolRef::getObject(); return cast(O); diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index f761c1d..d86a751 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -517,7 +517,8 @@ void RuntimeDyldImpl::addRelocationForSymbol(const RelocationEntry &RE, } } -uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) { +uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr, + unsigned AbiVariant) { if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be || Arch == Triple::arm64 || Arch == Triple::arm64_be) { // This stub has to be able to access the full address space, @@ -561,22 +562,31 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) { *StubAddr = NopInstr; return Addr; } else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) { - // PowerPC64 stub: the address points to a function descriptor - // instead of the function itself. Load the function address - // on r11 and sets it to control register. Also loads the function - // TOC in r2 and environment pointer to r11. + // Depending on which version of the ELF ABI is in use, we need to + // generate one of two variants of the stub. They both start with + // the same sequence to load the target address into r12. writeInt32BE(Addr, 0x3D800000); // lis r12, highest(addr) writeInt32BE(Addr+4, 0x618C0000); // ori r12, higher(addr) writeInt32BE(Addr+8, 0x798C07C6); // sldi r12, r12, 32 writeInt32BE(Addr+12, 0x658C0000); // oris r12, r12, h(addr) writeInt32BE(Addr+16, 0x618C0000); // ori r12, r12, l(addr) - writeInt32BE(Addr+20, 0xF8410028); // std r2, 40(r1) - writeInt32BE(Addr+24, 0xE96C0000); // ld r11, 0(r12) - writeInt32BE(Addr+28, 0xE84C0008); // ld r2, 0(r12) - writeInt32BE(Addr+32, 0x7D6903A6); // mtctr r11 - writeInt32BE(Addr+36, 0xE96C0010); // ld r11, 16(r2) - writeInt32BE(Addr+40, 0x4E800420); // bctr - + if (AbiVariant == 2) { + // PowerPC64 stub ELFv2 ABI: The address points to the function itself. + // The address is already in r12 as required by the ABI. Branch to it. + writeInt32BE(Addr+20, 0xF8410018); // std r2, 24(r1) + writeInt32BE(Addr+24, 0x7D8903A6); // mtctr r12 + writeInt32BE(Addr+28, 0x4E800420); // bctr + } else { + // PowerPC64 stub ELFv1 ABI: The address points to a function descriptor. + // Load the function address on r11 and sets it to control register. Also + // loads the function TOC in r2 and environment pointer to r11. + writeInt32BE(Addr+20, 0xF8410028); // std r2, 40(r1) + writeInt32BE(Addr+24, 0xE96C0000); // ld r11, 0(r12) + writeInt32BE(Addr+28, 0xE84C0008); // ld r2, 0(r12) + writeInt32BE(Addr+32, 0x7D6903A6); // mtctr r11 + writeInt32BE(Addr+36, 0xE96C0010); // ld r11, 16(r2) + writeInt32BE(Addr+40, 0x4E800420); // bctr + } return Addr; } else if (Arch == Triple::systemz) { writeInt16BE(Addr, 0xC418); // lgrl %r1,.+8 diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index 80e489c..728138e 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -1141,6 +1141,10 @@ relocation_iterator RuntimeDyldELF::processRelocationRef( } } else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) { if (RelType == ELF::R_PPC64_REL24) { + // Determine ABI variant in use for this object. + unsigned AbiVariant; + Obj.getObjectFile()->getPlatformFlags(AbiVariant); + AbiVariant &= ELF::EF_PPC64_ABI; // A PPC branch relocation will need a stub function if the target is // an external symbol (Symbol::ST_Unknown) or if the target address // is not within the signed 24-bits branch address. @@ -1148,10 +1152,18 @@ relocation_iterator RuntimeDyldELF::processRelocationRef( uint8_t *Target = Section.Address + Offset; bool RangeOverflow = false; if (SymType != SymbolRef::ST_Unknown) { - // A function call may points to the .opd entry, so the final symbol - // value - // in calculated based in the relocation values in .opd section. - findOPDEntrySection(Obj, ObjSectionToID, Value); + if (AbiVariant != 2) { + // In the ELFv1 ABI, a function call may point to the .opd entry, + // so the final symbol value is calculated based on the relocation + // values in the .opd section. + findOPDEntrySection(Obj, ObjSectionToID, Value); + } else { + // In the ELFv2 ABI, a function symbol may provide a local entry + // point, which must be used for direct calls. + uint8_t SymOther; + Symbol->getOther(SymOther); + Value.Addend += ELF::decodePPC64LocalEntryOffset(SymOther); + } uint8_t *RelocTarget = Sections[Value.SectionID].Address + Value.Addend; int32_t delta = static_cast(Target - RelocTarget); // If it is within 24-bits branch range, just set the branch target @@ -1179,7 +1191,8 @@ relocation_iterator RuntimeDyldELF::processRelocationRef( DEBUG(dbgs() << " Create a new stub function\n"); Stubs[Value] = Section.StubOffset; uint8_t *StubTargetAddr = - createStubFunction(Section.Address + Section.StubOffset); + createStubFunction(Section.Address + Section.StubOffset, + AbiVariant); RelocationEntry RE(SectionID, StubTargetAddr - Section.Address, ELF::R_PPC64_ADDR64, Value.Addend); @@ -1217,9 +1230,13 @@ relocation_iterator RuntimeDyldELF::processRelocationRef( RelType, 0); Section.StubOffset += getMaxStubSize(); } - if (SymType == SymbolRef::ST_Unknown) + if (SymType == SymbolRef::ST_Unknown) { // Restore the TOC for external calls - writeInt32BE(Target + 4, 0xE8410028); // ld r2,40(r1) + if (AbiVariant == 2) + writeInt32BE(Target + 4, 0xE8410018); // ld r2,28(r1) + else + writeInt32BE(Target + 4, 0xE8410028); // ld r2,40(r1) + } } } else if (RelType == ELF::R_PPC64_TOC16 || RelType == ELF::R_PPC64_TOC16_DS || diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index 0336cba..0211d2b 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -312,7 +312,7 @@ protected: /// \brief Emits long jump instruction to Addr. /// \return Pointer to the memory area for emitting target address. - uint8_t *createStubFunction(uint8_t *Addr); + uint8_t *createStubFunction(uint8_t *Addr, unsigned AbiVariant = 0); /// \brief Resolves relocations from Relocs list with address from Value. void resolveRelocationList(const RelocationList &Relocs, uint64_t Value); -- 2.7.4