//===------- ELF_ppc64.cpp -JIT linker implementation for ELF/ppc64 -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // ELF/ppc64 jit-link implementation. // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/JITLink/ELF_ppc64.h" #include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" #include "llvm/ExecutionEngine/JITLink/TableManager.h" #include "llvm/ExecutionEngine/JITLink/ppc64.h" #include "llvm/Object/ELFObjectFile.h" #include "EHFrameSupportImpl.h" #include "ELFLinkGraphBuilder.h" #include "JITLinkGeneric.h" #define DEBUG_TYPE "jitlink" namespace { using namespace llvm; using namespace llvm::jitlink; constexpr StringRef ELFTOCSymbolName = ".TOC."; constexpr StringRef TOCSymbolAliasIdent = "__TOC__"; constexpr uint64_t ELFTOCBaseOffset = 0x8000; constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO"; template class TLSInfoTableManager_ELF_ppc64 : public TableManager> { public: static const uint8_t TLSInfoEntryContent[16]; static StringRef getSectionName() { return ELFTLSInfoSectionName; } bool visitEdge(LinkGraph &G, Block *B, Edge &E) { Edge::Kind K = E.getKind(); switch (K) { case ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16HA: E.setKind(ppc64::TOCDelta16HA); E.setTarget(this->getEntryForTarget(G, E.getTarget())); return true; case ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16LO: E.setKind(ppc64::TOCDelta16LO); E.setTarget(this->getEntryForTarget(G, E.getTarget())); return true; case ppc64::RequestTLSDescInGOTAndTransformToDelta34: E.setKind(ppc64::Delta34); E.setTarget(this->getEntryForTarget(G, E.getTarget())); return true; default: return false; } } Symbol &createEntry(LinkGraph &G, Symbol &Target) { // The TLS Info entry's key value will be written by // `fixTLVSectionsAndEdges`, so create mutable content. auto &TLSInfoEntry = G.createMutableContentBlock( getTLSInfoSection(G), G.allocateContent(getTLSInfoEntryContent()), orc::ExecutorAddr(), 8, 0); TLSInfoEntry.addEdge(ppc64::Pointer64, 8, Target, 0); return G.addAnonymousSymbol(TLSInfoEntry, 0, 16, false, false); } private: Section &getTLSInfoSection(LinkGraph &G) { if (!TLSInfoTable) TLSInfoTable = &G.createSection(ELFTLSInfoSectionName, orc::MemProt::Read); return *TLSInfoTable; } ArrayRef getTLSInfoEntryContent() const { return {reinterpret_cast(TLSInfoEntryContent), sizeof(TLSInfoEntryContent)}; } Section *TLSInfoTable = nullptr; }; template <> const uint8_t TLSInfoTableManager_ELF_ppc64< llvm::endianness::little>::TLSInfoEntryContent[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*pthread key */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*data address*/ }; template <> const uint8_t TLSInfoTableManager_ELF_ppc64< llvm::endianness::big>::TLSInfoEntryContent[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*pthread key */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*data address*/ }; template Symbol &createELFGOTHeader(LinkGraph &G, ppc64::TOCTableManager &TOC) { Symbol *TOCSymbol = nullptr; for (Symbol *Sym : G.defined_symbols()) if (LLVM_UNLIKELY(Sym->getName() == ELFTOCSymbolName)) { TOCSymbol = Sym; break; } if (LLVM_LIKELY(TOCSymbol == nullptr)) { for (Symbol *Sym : G.external_symbols()) if (Sym->getName() == ELFTOCSymbolName) { TOCSymbol = Sym; break; } } if (!TOCSymbol) TOCSymbol = &G.addExternalSymbol(ELFTOCSymbolName, 0, false); return TOC.getEntryForTarget(G, *TOCSymbol); } // Register preexisting GOT entries with TOC table manager. template inline void registerExistingGOTEntries(LinkGraph &G, ppc64::TOCTableManager &TOC) { auto isGOTEntry = [](const Edge &E) { return E.getKind() == ppc64::Pointer64 && E.getTarget().isExternal(); }; if (Section *dotTOCSection = G.findSectionByName(".toc")) { for (Block *B : dotTOCSection->blocks()) for (Edge &E : B->edges()) if (isGOTEntry(E)) TOC.registerPreExistingEntry(E.getTarget(), G.addAnonymousSymbol(*B, E.getOffset(), G.getPointerSize(), false, false)); } } template Error buildTables_ELF_ppc64(LinkGraph &G) { LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); ppc64::TOCTableManager TOC; // Before visiting edges, we create a header containing the address of TOC // base as ELFABIv2 suggests: // > The GOT consists of an 8-byte header that contains the TOC base (the // first TOC base when multiple TOCs are present), followed by an array of // 8-byte addresses. createELFGOTHeader(G, TOC); // There might be compiler-generated GOT entries in ELF relocatable file. registerExistingGOTEntries(G, TOC); ppc64::PLTTableManager PLT(TOC); TLSInfoTableManager_ELF_ppc64 TLSInfo; visitExistingEdges(G, TOC, PLT, TLSInfo); // After visiting edges in LinkGraph, we have GOT entries built in the // synthesized section. // Merge sections included in TOC into synthesized TOC section, // thus TOC is compact and reducing chances of relocation // overflow. if (Section *TOCSection = G.findSectionByName(TOC.getSectionName())) { // .got and .plt are not normally present in a relocatable object file // because they are linker generated. if (Section *gotSection = G.findSectionByName(".got")) G.mergeSections(*TOCSection, *gotSection); if (Section *tocSection = G.findSectionByName(".toc")) G.mergeSections(*TOCSection, *tocSection); if (Section *sdataSection = G.findSectionByName(".sdata")) G.mergeSections(*TOCSection, *sdataSection); if (Section *sbssSection = G.findSectionByName(".sbss")) G.mergeSections(*TOCSection, *sbssSection); // .tocbss no longer appears in ELFABIv2. Leave it here to be compatible // with rtdyld. if (Section *tocbssSection = G.findSectionByName(".tocbss")) G.mergeSections(*TOCSection, *tocbssSection); if (Section *pltSection = G.findSectionByName(".plt")) G.mergeSections(*TOCSection, *pltSection); } return Error::success(); } } // namespace namespace llvm::jitlink { template class ELFLinkGraphBuilder_ppc64 : public ELFLinkGraphBuilder> { private: using ELFT = object::ELFType; using Base = ELFLinkGraphBuilder; using Base::G; // Use LinkGraph pointer from base class. Error addRelocations() override { LLVM_DEBUG(dbgs() << "Processing relocations:\n"); using Self = ELFLinkGraphBuilder_ppc64; for (const auto &RelSect : Base::Sections) { // Validate the section to read relocation entries from. if (RelSect.sh_type == ELF::SHT_REL) return make_error("No SHT_REL in valid " + G->getTargetTriple().getArchName() + " ELF object files", inconvertibleErrorCode()); if (Error Err = Base::forEachRelaRelocation(RelSect, this, &Self::addSingleRelocation)) return Err; } return Error::success(); } Error addSingleRelocation(const typename ELFT::Rela &Rel, const typename ELFT::Shdr &FixupSection, Block &BlockToFix) { using Base = ELFLinkGraphBuilder; auto ELFReloc = Rel.getType(false); // R_PPC64_NONE is a no-op. if (LLVM_UNLIKELY(ELFReloc == ELF::R_PPC64_NONE)) return Error::success(); // TLS model markers. We only support global-dynamic model now. if (ELFReloc == ELF::R_PPC64_TLSGD) return Error::success(); if (ELFReloc == ELF::R_PPC64_TLSLD) return make_error("Local-dynamic TLS model is not supported", inconvertibleErrorCode()); if (ELFReloc == ELF::R_PPC64_PCREL_OPT) // TODO: Support PCREL optimization, now ignore it. return Error::success(); if (ELFReloc == ELF::R_PPC64_TPREL34) return make_error("Local-exec TLS model is not supported", inconvertibleErrorCode()); auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec); if (!ObjSymbol) return ObjSymbol.takeError(); uint32_t SymbolIndex = Rel.getSymbol(false); Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex); if (!GraphSymbol) return make_error( formatv("Could not find symbol at given index, did you add it to " "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}", SymbolIndex, (*ObjSymbol)->st_shndx, Base::GraphSymbols.size()), inconvertibleErrorCode()); int64_t Addend = Rel.r_addend; orc::ExecutorAddr FixupAddress = orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset; Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); Edge::Kind Kind = Edge::Invalid; switch (ELFReloc) { default: return make_error( "In " + G->getName() + ": Unsupported ppc64 relocation type " + object::getELFRelocationTypeName(ELF::EM_PPC64, ELFReloc)); case ELF::R_PPC64_ADDR64: Kind = ppc64::Pointer64; break; case ELF::R_PPC64_ADDR32: Kind = ppc64::Pointer32; break; case ELF::R_PPC64_ADDR16: Kind = ppc64::Pointer16; break; case ELF::R_PPC64_ADDR16_DS: Kind = ppc64::Pointer16DS; break; case ELF::R_PPC64_ADDR16_HA: Kind = ppc64::Pointer16HA; break; case ELF::R_PPC64_ADDR16_HI: Kind = ppc64::Pointer16HI; break; case ELF::R_PPC64_ADDR16_HIGH: Kind = ppc64::Pointer16HIGH; break; case ELF::R_PPC64_ADDR16_HIGHA: Kind = ppc64::Pointer16HIGHA; break; case ELF::R_PPC64_ADDR16_HIGHER: Kind = ppc64::Pointer16HIGHER; break; case ELF::R_PPC64_ADDR16_HIGHERA: Kind = ppc64::Pointer16HIGHERA; break; case ELF::R_PPC64_ADDR16_HIGHEST: Kind = ppc64::Pointer16HIGHEST; break; case ELF::R_PPC64_ADDR16_HIGHESTA: Kind = ppc64::Pointer16HIGHESTA; break; case ELF::R_PPC64_ADDR16_LO: Kind = ppc64::Pointer16LO; break; case ELF::R_PPC64_ADDR16_LO_DS: Kind = ppc64::Pointer16LODS; break; case ELF::R_PPC64_ADDR14: Kind = ppc64::Pointer14; break; case ELF::R_PPC64_TOC: Kind = ppc64::TOC; break; case ELF::R_PPC64_TOC16: Kind = ppc64::TOCDelta16; break; case ELF::R_PPC64_TOC16_HA: Kind = ppc64::TOCDelta16HA; break; case ELF::R_PPC64_TOC16_HI: Kind = ppc64::TOCDelta16HI; break; case ELF::R_PPC64_TOC16_DS: Kind = ppc64::TOCDelta16DS; break; case ELF::R_PPC64_TOC16_LO: Kind = ppc64::TOCDelta16LO; break; case ELF::R_PPC64_TOC16_LO_DS: Kind = ppc64::TOCDelta16LODS; break; case ELF::R_PPC64_REL16: Kind = ppc64::Delta16; break; case ELF::R_PPC64_REL16_HA: Kind = ppc64::Delta16HA; break; case ELF::R_PPC64_REL16_HI: Kind = ppc64::Delta16HI; break; case ELF::R_PPC64_REL16_LO: Kind = ppc64::Delta16LO; break; case ELF::R_PPC64_REL32: Kind = ppc64::Delta32; break; case ELF::R_PPC64_REL24_NOTOC: Kind = ppc64::RequestCallNoTOC; break; case ELF::R_PPC64_REL24: Kind = ppc64::RequestCall; // Determining a target is external or not is deferred in PostPrunePass. // We assume branching to local entry by default, since in PostPrunePass, // we don't have any context to determine LocalEntryOffset. If it finally // turns out to be an external call, we'll have a stub for the external // target, the target of this edge will be the stub and its addend will be // set 0. Addend += ELF::decodePPC64LocalEntryOffset((*ObjSymbol)->st_other); break; case ELF::R_PPC64_REL64: Kind = ppc64::Delta64; break; case ELF::R_PPC64_PCREL34: Kind = ppc64::Delta34; break; case ELF::R_PPC64_GOT_PCREL34: Kind = ppc64::RequestGOTAndTransformToDelta34; break; case ELF::R_PPC64_GOT_TLSGD16_HA: Kind = ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16HA; break; case ELF::R_PPC64_GOT_TLSGD16_LO: Kind = ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16LO; break; case ELF::R_PPC64_GOT_TLSGD_PCREL34: Kind = ppc64::RequestTLSDescInGOTAndTransformToDelta34; break; } Edge GE(Kind, Offset, *GraphSymbol, Addend); BlockToFix.addEdge(std::move(GE)); return Error::success(); } public: ELFLinkGraphBuilder_ppc64(StringRef FileName, const object::ELFFile &Obj, Triple TT, SubtargetFeatures Features) : ELFLinkGraphBuilder(Obj, std::move(TT), std::move(Features), FileName, ppc64::getEdgeKindName) {} }; template class ELFJITLinker_ppc64 : public JITLinker> { using JITLinkerBase = JITLinker>; friend JITLinkerBase; public: ELFJITLinker_ppc64(std::unique_ptr Ctx, std::unique_ptr G, PassConfiguration PassConfig) : JITLinkerBase(std::move(Ctx), std::move(G), std::move(PassConfig)) { JITLinkerBase::getPassConfig().PostAllocationPasses.push_back( [this](LinkGraph &G) { return defineTOCBase(G); }); } private: Symbol *TOCSymbol = nullptr; Error defineTOCBase(LinkGraph &G) { for (Symbol *Sym : G.defined_symbols()) { if (LLVM_UNLIKELY(Sym->getName() == ELFTOCSymbolName)) { TOCSymbol = Sym; return Error::success(); } } assert(TOCSymbol == nullptr && "TOCSymbol should not be defined at this point"); for (Symbol *Sym : G.external_symbols()) { if (Sym->getName() == ELFTOCSymbolName) { TOCSymbol = Sym; break; } } if (Section *TOCSection = G.findSectionByName( ppc64::TOCTableManager::getSectionName())) { assert(!TOCSection->empty() && "TOC section should have reserved an " "entry for containing the TOC base"); SectionRange SR(*TOCSection); orc::ExecutorAddr TOCBaseAddr(SR.getFirstBlock()->getAddress() + ELFTOCBaseOffset); assert(TOCSymbol && TOCSymbol->isExternal() && ".TOC. should be a external symbol at this point"); G.makeAbsolute(*TOCSymbol, TOCBaseAddr); // Create an alias of .TOC. so that rtdyld checker can recognize. G.addAbsoluteSymbol(TOCSymbolAliasIdent, TOCSymbol->getAddress(), TOCSymbol->getSize(), TOCSymbol->getLinkage(), TOCSymbol->getScope(), TOCSymbol->isLive()); return Error::success(); } // If TOC section doesn't exist, which means no TOC relocation is found, we // don't need a TOCSymbol. return Error::success(); } Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { return ppc64::applyFixup(G, B, E, TOCSymbol); } }; template Expected> createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer) { LLVM_DEBUG({ dbgs() << "Building jitlink graph for new input " << ObjectBuffer.getBufferIdentifier() << "...\n"; }); auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer); if (!ELFObj) return ELFObj.takeError(); auto Features = (*ELFObj)->getFeatures(); if (!Features) return Features.takeError(); using ELFT = object::ELFType; auto &ELFObjFile = cast>(**ELFObj); return ELFLinkGraphBuilder_ppc64( (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), (*ELFObj)->makeTriple(), std::move(*Features)) .buildGraph(); } template void link_ELF_ppc64(std::unique_ptr G, std::unique_ptr Ctx) { PassConfiguration Config; if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) { // Construct a JITLinker and run the link function. // Add eh-frame passes. Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame")); Config.PrePrunePasses.push_back(EHFrameEdgeFixer( ".eh_frame", G->getPointerSize(), ppc64::Pointer32, ppc64::Pointer64, ppc64::Delta32, ppc64::Delta64, ppc64::NegDelta32)); Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame")); // Add a mark-live pass. if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple())) Config.PrePrunePasses.push_back(std::move(MarkLive)); else Config.PrePrunePasses.push_back(markAllSymbolsLive); } Config.PostPrunePasses.push_back(buildTables_ELF_ppc64); if (auto Err = Ctx->modifyPassConfig(*G, Config)) return Ctx->notifyFailed(std::move(Err)); ELFJITLinker_ppc64::link(std::move(Ctx), std::move(G), std::move(Config)); } Expected> createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer) { return createLinkGraphFromELFObject_ppc64( std::move(ObjectBuffer)); } Expected> createLinkGraphFromELFObject_ppc64le(MemoryBufferRef ObjectBuffer) { return createLinkGraphFromELFObject_ppc64( std::move(ObjectBuffer)); } /// jit-link the given object buffer, which must be a ELF ppc64 object file. void link_ELF_ppc64(std::unique_ptr G, std::unique_ptr Ctx) { return link_ELF_ppc64(std::move(G), std::move(Ctx)); } /// jit-link the given object buffer, which must be a ELF ppc64le object file. void link_ELF_ppc64le(std::unique_ptr G, std::unique_ptr Ctx) { return link_ELF_ppc64(std::move(G), std::move(Ctx)); } } // end namespace llvm::jitlink