| //===---- IndirectionUtils.cpp - Utilities for call indirection in Orc ----===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" | |
| #include "llvm/ExecutionEngine/JITLink/x86_64.h" | |
| #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" | |
| #include "llvm/MC/MCInstrAnalysis.h" | |
| #include "llvm/TargetParser/Triple.h" | |
| #include "llvm/Transforms/Utils/Cloning.h" | |
| #define DEBUG_TYPE "orc" | |
| using namespace llvm; | |
| using namespace llvm::orc; | |
| namespace { | |
| class CompileCallbackMaterializationUnit : public orc::MaterializationUnit { | |
| public: | |
| using CompileFunction = JITCompileCallbackManager::CompileFunction; | |
| CompileCallbackMaterializationUnit(SymbolStringPtr Name, | |
| CompileFunction Compile) | |
| : MaterializationUnit(Interface( | |
| SymbolFlagsMap({{Name, JITSymbolFlags::Exported}}), nullptr)), | |
| Name(std::move(Name)), Compile(std::move(Compile)) {} | |
| StringRef getName() const override { return "<Compile Callbacks>"; } | |
| private: | |
| void materialize(std::unique_ptr<MaterializationResponsibility> R) override { | |
| SymbolMap Result; | |
| Result[Name] = {Compile(), JITSymbolFlags::Exported}; | |
| // No dependencies, so these calls cannot fail. | |
| cantFail(R->notifyResolved(Result)); | |
| cantFail(R->notifyEmitted({})); | |
| } | |
| void discard(const JITDylib &JD, const SymbolStringPtr &Name) override { | |
| llvm_unreachable("Discard should never occur on a LMU?"); | |
| } | |
| SymbolStringPtr Name; | |
| CompileFunction Compile; | |
| }; | |
| } // namespace | |
| namespace llvm { | |
| namespace orc { | |
| TrampolinePool::~TrampolinePool() = default; | |
| void IndirectStubsManager::anchor() {} | |
| Expected<ExecutorAddr> | |
| JITCompileCallbackManager::getCompileCallback(CompileFunction Compile) { | |
| if (auto TrampolineAddr = TP->getTrampoline()) { | |
| auto CallbackName = | |
| ES.intern(std::string("cc") + std::to_string(++NextCallbackId)); | |
| std::lock_guard<std::mutex> Lock(CCMgrMutex); | |
| AddrToSymbol[*TrampolineAddr] = CallbackName; | |
| cantFail( | |
| CallbacksJD.define(std::make_unique<CompileCallbackMaterializationUnit>( | |
| std::move(CallbackName), std::move(Compile)))); | |
| return *TrampolineAddr; | |
| } else | |
| return TrampolineAddr.takeError(); | |
| } | |
| ExecutorAddr | |
| JITCompileCallbackManager::executeCompileCallback(ExecutorAddr TrampolineAddr) { | |
| SymbolStringPtr Name; | |
| { | |
| std::unique_lock<std::mutex> Lock(CCMgrMutex); | |
| auto I = AddrToSymbol.find(TrampolineAddr); | |
| // If this address is not associated with a compile callback then report an | |
| // error to the execution session and return ErrorHandlerAddress to the | |
| // callee. | |
| if (I == AddrToSymbol.end()) { | |
| Lock.unlock(); | |
| ES.reportError( | |
| make_error<StringError>("No compile callback for trampoline at " + | |
| formatv("{0:x}", TrampolineAddr), | |
| inconvertibleErrorCode())); | |
| return ErrorHandlerAddress; | |
| } else | |
| Name = I->second; | |
| } | |
| if (auto Sym = | |
| ES.lookup(makeJITDylibSearchOrder( | |
| &CallbacksJD, JITDylibLookupFlags::MatchAllSymbols), | |
| Name)) | |
| return Sym->getAddress(); | |
| else { | |
| llvm::dbgs() << "Didn't find callback.\n"; | |
| // If anything goes wrong materializing Sym then report it to the session | |
| // and return the ErrorHandlerAddress; | |
| ES.reportError(Sym.takeError()); | |
| return ErrorHandlerAddress; | |
| } | |
| } | |
| Error IndirectStubsManager::redirect(JITDylib &JD, const SymbolMap &NewDests) { | |
| for (auto &[Name, Dest] : NewDests) | |
| if (auto Err = updatePointer(*Name, Dest.getAddress())) | |
| return Err; | |
| return Error::success(); | |
| } | |
| void IndirectStubsManager::emitRedirectableSymbols( | |
| std::unique_ptr<MaterializationResponsibility> MR, SymbolMap InitialDests) { | |
| StubInitsMap StubInits; | |
| for (auto &[Name, Dest] : InitialDests) | |
| StubInits[*Name] = {Dest.getAddress(), Dest.getFlags()}; | |
| if (auto Err = createStubs(StubInits)) { | |
| MR->getExecutionSession().reportError(std::move(Err)); | |
| return MR->failMaterialization(); | |
| } | |
| SymbolMap Stubs; | |
| for (auto &[Name, Dest] : InitialDests) { | |
| auto StubSym = findStub(*Name, false); | |
| assert(StubSym.getAddress() && "Stub symbol should be present"); | |
| Stubs[Name] = StubSym; | |
| } | |
| if (auto Err = MR->notifyResolved(Stubs)) { | |
| MR->getExecutionSession().reportError(std::move(Err)); | |
| return MR->failMaterialization(); | |
| } | |
| if (auto Err = MR->notifyEmitted({})) { | |
| MR->getExecutionSession().reportError(std::move(Err)); | |
| return MR->failMaterialization(); | |
| } | |
| } | |
| Expected<std::unique_ptr<JITCompileCallbackManager>> | |
| createLocalCompileCallbackManager(const Triple &T, ExecutionSession &ES, | |
| ExecutorAddr ErrorHandlerAddress) { | |
| switch (T.getArch()) { | |
| default: | |
| return make_error<StringError>( | |
| std::string("No callback manager available for ") + T.str(), | |
| inconvertibleErrorCode()); | |
| case Triple::aarch64: | |
| case Triple::aarch64_32: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcAArch64> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::x86: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcI386> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::loongarch64: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcLoongArch64> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::mips: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcMips32Be> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::mipsel: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcMips32Le> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::mips64: | |
| case Triple::mips64el: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcMips64> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::riscv64: { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcRiscv64> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| case Triple::x86_64: { | |
| if (T.getOS() == Triple::OSType::Win32) { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcX86_64_Win32> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } else { | |
| typedef orc::LocalJITCompileCallbackManager<orc::OrcX86_64_SysV> CCMgrT; | |
| return CCMgrT::Create(ES, ErrorHandlerAddress); | |
| } | |
| } | |
| } | |
| } | |
| std::function<std::unique_ptr<IndirectStubsManager>()> | |
| createLocalIndirectStubsManagerBuilder(const Triple &T) { | |
| switch (T.getArch()) { | |
| default: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcGenericABI>>(); | |
| }; | |
| case Triple::aarch64: | |
| case Triple::aarch64_32: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcAArch64>>(); | |
| }; | |
| case Triple::x86: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcI386>>(); | |
| }; | |
| case Triple::loongarch64: | |
| return []() { | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcLoongArch64>>(); | |
| }; | |
| case Triple::mips: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcMips32Be>>(); | |
| }; | |
| case Triple::mipsel: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcMips32Le>>(); | |
| }; | |
| case Triple::mips64: | |
| case Triple::mips64el: | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcMips64>>(); | |
| }; | |
| case Triple::riscv64: | |
| return []() { | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcRiscv64>>(); | |
| }; | |
| case Triple::x86_64: | |
| if (T.getOS() == Triple::OSType::Win32) { | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcX86_64_Win32>>(); | |
| }; | |
| } else { | |
| return [](){ | |
| return std::make_unique< | |
| orc::LocalIndirectStubsManager<orc::OrcX86_64_SysV>>(); | |
| }; | |
| } | |
| } | |
| } | |
| Constant* createIRTypedAddress(FunctionType &FT, ExecutorAddr Addr) { | |
| Constant *AddrIntVal = | |
| ConstantInt::get(Type::getInt64Ty(FT.getContext()), Addr.getValue()); | |
| Constant *AddrPtrVal = ConstantExpr::getIntToPtr( | |
| AddrIntVal, PointerType::get(FT.getContext(), 0)); | |
| return AddrPtrVal; | |
| } | |
| GlobalVariable* createImplPointer(PointerType &PT, Module &M, | |
| const Twine &Name, Constant *Initializer) { | |
| auto IP = new GlobalVariable(M, &PT, false, GlobalValue::ExternalLinkage, | |
| Initializer, Name, nullptr, | |
| GlobalValue::NotThreadLocal, 0, true); | |
| IP->setVisibility(GlobalValue::HiddenVisibility); | |
| return IP; | |
| } | |
| void makeStub(Function &F, Value &ImplPointer) { | |
| assert(F.isDeclaration() && "Can't turn a definition into a stub."); | |
| assert(F.getParent() && "Function isn't in a module."); | |
| Module &M = *F.getParent(); | |
| BasicBlock *EntryBlock = BasicBlock::Create(M.getContext(), "entry", &F); | |
| IRBuilder<> Builder(EntryBlock); | |
| LoadInst *ImplAddr = Builder.CreateLoad(F.getType(), &ImplPointer); | |
| std::vector<Value*> CallArgs; | |
| for (auto &A : F.args()) | |
| CallArgs.push_back(&A); | |
| CallInst *Call = Builder.CreateCall(F.getFunctionType(), ImplAddr, CallArgs); | |
| Call->setTailCall(); | |
| Call->setAttributes(F.getAttributes()); | |
| if (F.getReturnType()->isVoidTy()) | |
| Builder.CreateRetVoid(); | |
| else | |
| Builder.CreateRet(Call); | |
| } | |
| std::vector<GlobalValue *> SymbolLinkagePromoter::operator()(Module &M) { | |
| std::vector<GlobalValue *> PromotedGlobals; | |
| for (auto &GV : M.global_values()) { | |
| bool Promoted = true; | |
| // Rename if necessary. | |
| if (!GV.hasName()) | |
| GV.setName("__orc_anon." + Twine(NextId++)); | |
| else if (GV.getName().starts_with("\01L")) | |
| GV.setName("__" + GV.getName().substr(1) + "." + Twine(NextId++)); | |
| else if (GV.hasLocalLinkage()) | |
| GV.setName("__orc_lcl." + GV.getName() + "." + Twine(NextId++)); | |
| else | |
| Promoted = false; | |
| if (GV.hasLocalLinkage()) { | |
| GV.setLinkage(GlobalValue::ExternalLinkage); | |
| GV.setVisibility(GlobalValue::HiddenVisibility); | |
| Promoted = true; | |
| } | |
| GV.setUnnamedAddr(GlobalValue::UnnamedAddr::None); | |
| if (Promoted) | |
| PromotedGlobals.push_back(&GV); | |
| } | |
| return PromotedGlobals; | |
| } | |
| Function* cloneFunctionDecl(Module &Dst, const Function &F, | |
| ValueToValueMapTy *VMap) { | |
| Function *NewF = | |
| Function::Create(F.getFunctionType(), F.getLinkage(), F.getName(), &Dst); | |
| NewF->copyAttributesFrom(&F); | |
| if (VMap) { | |
| (*VMap)[&F] = NewF; | |
| auto NewArgI = NewF->arg_begin(); | |
| for (auto ArgI = F.arg_begin(), ArgE = F.arg_end(); ArgI != ArgE; | |
| ++ArgI, ++NewArgI) | |
| (*VMap)[&*ArgI] = &*NewArgI; | |
| } | |
| return NewF; | |
| } | |
| GlobalVariable* cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV, | |
| ValueToValueMapTy *VMap) { | |
| GlobalVariable *NewGV = new GlobalVariable( | |
| Dst, GV.getValueType(), GV.isConstant(), | |
| GV.getLinkage(), nullptr, GV.getName(), nullptr, | |
| GV.getThreadLocalMode(), GV.getType()->getAddressSpace()); | |
| NewGV->copyAttributesFrom(&GV); | |
| if (VMap) | |
| (*VMap)[&GV] = NewGV; | |
| return NewGV; | |
| } | |
| GlobalAlias* cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA, | |
| ValueToValueMapTy &VMap) { | |
| assert(OrigA.getAliasee() && "Original alias doesn't have an aliasee?"); | |
| auto *NewA = GlobalAlias::create(OrigA.getValueType(), | |
| OrigA.getType()->getPointerAddressSpace(), | |
| OrigA.getLinkage(), OrigA.getName(), &Dst); | |
| NewA->copyAttributesFrom(&OrigA); | |
| VMap[&OrigA] = NewA; | |
| return NewA; | |
| } | |
| Error addFunctionPointerRelocationsToCurrentSymbol(jitlink::Symbol &Sym, | |
| jitlink::LinkGraph &G, | |
| MCDisassembler &Disassembler, | |
| MCInstrAnalysis &MIA) { | |
| // AArch64 appears to already come with the necessary relocations. Among other | |
| // architectures, only x86_64 is currently implemented here. | |
| if (G.getTargetTriple().getArch() != Triple::x86_64) | |
| return Error::success(); | |
| raw_null_ostream CommentStream; | |
| auto &STI = Disassembler.getSubtargetInfo(); | |
| // Determine the function bounds | |
| auto &B = Sym.getBlock(); | |
| assert(!B.isZeroFill() && "expected content block"); | |
| auto SymAddress = Sym.getAddress(); | |
| auto SymStartInBlock = | |
| (const uint8_t *)B.getContent().data() + Sym.getOffset(); | |
| auto SymSize = Sym.getSize() ? Sym.getSize() : B.getSize() - Sym.getOffset(); | |
| auto Content = ArrayRef(SymStartInBlock, SymSize); | |
| LLVM_DEBUG(dbgs() << "Adding self-relocations to " << Sym.getName() << "\n"); | |
| SmallDenseSet<uintptr_t, 8> ExistingRelocations; | |
| for (auto &E : B.edges()) { | |
| if (E.isRelocation()) | |
| ExistingRelocations.insert(E.getOffset()); | |
| } | |
| size_t I = 0; | |
| while (I < Content.size()) { | |
| MCInst Instr; | |
| uint64_t InstrSize = 0; | |
| uint64_t InstrStart = SymAddress.getValue() + I; | |
| auto DecodeStatus = Disassembler.getInstruction( | |
| Instr, InstrSize, Content.drop_front(I), InstrStart, CommentStream); | |
| if (DecodeStatus != MCDisassembler::Success) { | |
| LLVM_DEBUG(dbgs() << "Aborting due to disassembly failure at address " | |
| << InstrStart); | |
| return make_error<StringError>( | |
| formatv("failed to disassemble at address {0:x16}", InstrStart), | |
| inconvertibleErrorCode()); | |
| } | |
| // Advance to the next instruction. | |
| I += InstrSize; | |
| // Check for a PC-relative address equal to the symbol itself. | |
| auto PCRelAddr = | |
| MIA.evaluateMemoryOperandAddress(Instr, &STI, InstrStart, InstrSize); | |
| if (!PCRelAddr || *PCRelAddr != SymAddress.getValue()) | |
| continue; | |
| auto RelocOffInInstr = | |
| MIA.getMemoryOperandRelocationOffset(Instr, InstrSize); | |
| if (!RelocOffInInstr || InstrSize - *RelocOffInInstr != 4) { | |
| LLVM_DEBUG(dbgs() << "Skipping unknown self-relocation at " | |
| << InstrStart); | |
| continue; | |
| } | |
| auto RelocOffInBlock = orc::ExecutorAddr(InstrStart) + *RelocOffInInstr - | |
| SymAddress + Sym.getOffset(); | |
| if (ExistingRelocations.contains(RelocOffInBlock)) | |
| continue; | |
| LLVM_DEBUG(dbgs() << "Adding delta32 self-relocation at " << InstrStart); | |
| B.addEdge(jitlink::x86_64::Delta32, RelocOffInBlock, Sym, /*Addend=*/-4); | |
| } | |
| return Error::success(); | |
| } | |
| } // End namespace orc. | |
| } // End namespace llvm. |