| //===------- 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 <llvm::endianness Endianness> | |
| class TLSInfoTableManager_ELF_ppc64 | |
| : public TableManager<TLSInfoTableManager_ELF_ppc64<Endianness>> { | |
| 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<char> getTLSInfoEntryContent() const { | |
| return {reinterpret_cast<const char *>(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 <llvm::endianness Endianness> | |
| Symbol &createELFGOTHeader(LinkGraph &G, | |
| ppc64::TOCTableManager<Endianness> &TOC) { | |
| Symbol *TOCSymbol = nullptr; | |
| for (Symbol *Sym : G.defined_symbols()) | |
| if (LLVM_UNLIKELY(Sym->hasName() && *Sym->getName() == ELFTOCSymbolName)) { | |
| TOCSymbol = Sym; | |
| break; | |
| } | |
| if (LLVM_LIKELY(TOCSymbol == nullptr)) { | |
| for (Symbol *Sym : G.external_symbols()) | |
| if (Sym->hasName() && *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 <llvm::endianness Endianness> | |
| inline void | |
| registerExistingGOTEntries(LinkGraph &G, | |
| ppc64::TOCTableManager<Endianness> &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 <llvm::endianness Endianness> | |
| Error buildTables_ELF_ppc64(LinkGraph &G) { | |
| LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); | |
| ppc64::TOCTableManager<Endianness> 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<Endianness> PLT(TOC); | |
| TLSInfoTableManager_ELF_ppc64<Endianness> 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 <llvm::endianness Endianness> | |
| class ELFLinkGraphBuilder_ppc64 | |
| : public ELFLinkGraphBuilder<object::ELFType<Endianness, true>> { | |
| private: | |
| using ELFT = object::ELFType<Endianness, true>; | |
| using Base = ELFLinkGraphBuilder<ELFT>; | |
| using Base::G; // Use LinkGraph pointer from base class. | |
| Error addRelocations() override { | |
| LLVM_DEBUG(dbgs() << "Processing relocations:\n"); | |
| using Self = ELFLinkGraphBuilder_ppc64<Endianness>; | |
| for (const auto &RelSect : Base::Sections) { | |
| // Validate the section to read relocation entries from. | |
| if (RelSect.sh_type == ELF::SHT_REL) | |
| return make_error<StringError>("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<ELFT>; | |
| 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<StringError>("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<StringError>("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<StringError>( | |
| 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<JITLinkError>( | |
| "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<ELFT> &Obj, | |
| std::shared_ptr<orc::SymbolStringPool> SSP, | |
| Triple TT, SubtargetFeatures Features) | |
| : ELFLinkGraphBuilder<ELFT>(Obj, std::move(SSP), std::move(TT), | |
| std::move(Features), FileName, | |
| ppc64::getEdgeKindName) {} | |
| }; | |
| template <llvm::endianness Endianness> | |
| class ELFJITLinker_ppc64 : public JITLinker<ELFJITLinker_ppc64<Endianness>> { | |
| using JITLinkerBase = JITLinker<ELFJITLinker_ppc64<Endianness>>; | |
| friend JITLinkerBase; | |
| public: | |
| ELFJITLinker_ppc64(std::unique_ptr<JITLinkContext> Ctx, | |
| std::unique_ptr<LinkGraph> 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->hasName() && | |
| *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->hasName() && *Sym->getName() == ELFTOCSymbolName) { | |
| TOCSymbol = Sym; | |
| break; | |
| } | |
| } | |
| if (Section *TOCSection = G.findSectionByName( | |
| ppc64::TOCTableManager<Endianness>::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<Endianness>(G, B, E, TOCSymbol); | |
| } | |
| }; | |
| template <llvm::endianness Endianness> | |
| Expected<std::unique_ptr<LinkGraph>> | |
| createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer, | |
| std::shared_ptr<orc::SymbolStringPool> SSP) { | |
| 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<Endianness, true>; | |
| auto &ELFObjFile = cast<object::ELFObjectFile<ELFT>>(**ELFObj); | |
| return ELFLinkGraphBuilder_ppc64<Endianness>( | |
| (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), std::move(SSP), | |
| (*ELFObj)->makeTriple(), std::move(*Features)) | |
| .buildGraph(); | |
| } | |
| template <llvm::endianness Endianness> | |
| void link_ELF_ppc64(std::unique_ptr<LinkGraph> G, | |
| std::unique_ptr<JITLinkContext> 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<Endianness>); | |
| if (auto Err = Ctx->modifyPassConfig(*G, Config)) | |
| return Ctx->notifyFailed(std::move(Err)); | |
| ELFJITLinker_ppc64<Endianness>::link(std::move(Ctx), std::move(G), | |
| std::move(Config)); | |
| } | |
| Expected<std::unique_ptr<LinkGraph>> | |
| createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer, | |
| std::shared_ptr<orc::SymbolStringPool> SSP) { | |
| return createLinkGraphFromELFObject_ppc64<llvm::endianness::big>( | |
| std::move(ObjectBuffer), std::move(SSP)); | |
| } | |
| Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_ppc64le( | |
| MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) { | |
| return createLinkGraphFromELFObject_ppc64<llvm::endianness::little>( | |
| std::move(ObjectBuffer), std::move(SSP)); | |
| } | |
| /// jit-link the given object buffer, which must be a ELF ppc64 object file. | |
| void link_ELF_ppc64(std::unique_ptr<LinkGraph> G, | |
| std::unique_ptr<JITLinkContext> Ctx) { | |
| return link_ELF_ppc64<llvm::endianness::big>(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<LinkGraph> G, | |
| std::unique_ptr<JITLinkContext> Ctx) { | |
| return link_ELF_ppc64<llvm::endianness::little>(std::move(G), std::move(Ctx)); | |
| } | |
| } // end namespace llvm::jitlink |