Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp
35266 views
//===------- EPCIndirectionUtils.cpp -- EPC based indirection APIs --------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h"910#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"11#include "llvm/Support/MathExtras.h"1213#include <future>1415using namespace llvm;16using namespace llvm::orc;1718namespace llvm {19namespace orc {2021class EPCIndirectionUtilsAccess {22public:23using IndirectStubInfo = EPCIndirectionUtils::IndirectStubInfo;24using IndirectStubInfoVector = EPCIndirectionUtils::IndirectStubInfoVector;2526static Expected<IndirectStubInfoVector>27getIndirectStubs(EPCIndirectionUtils &EPCIU, unsigned NumStubs) {28return EPCIU.getIndirectStubs(NumStubs);29};30};3132} // end namespace orc33} // end namespace llvm3435namespace {3637class EPCTrampolinePool : public TrampolinePool {38public:39EPCTrampolinePool(EPCIndirectionUtils &EPCIU);40Error deallocatePool();4142protected:43Error grow() override;4445using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;4647EPCIndirectionUtils &EPCIU;48unsigned TrampolineSize = 0;49unsigned TrampolinesPerPage = 0;50std::vector<FinalizedAlloc> TrampolineBlocks;51};5253class EPCIndirectStubsManager : public IndirectStubsManager,54private EPCIndirectionUtilsAccess {55public:56EPCIndirectStubsManager(EPCIndirectionUtils &EPCIU) : EPCIU(EPCIU) {}5758Error deallocateStubs();5960Error createStub(StringRef StubName, ExecutorAddr StubAddr,61JITSymbolFlags StubFlags) override;6263Error createStubs(const StubInitsMap &StubInits) override;6465ExecutorSymbolDef findStub(StringRef Name, bool ExportedStubsOnly) override;6667ExecutorSymbolDef findPointer(StringRef Name) override;6869Error updatePointer(StringRef Name, ExecutorAddr NewAddr) override;7071private:72using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;7374std::mutex ISMMutex;75EPCIndirectionUtils &EPCIU;76StringMap<StubInfo> StubInfos;77};7879EPCTrampolinePool::EPCTrampolinePool(EPCIndirectionUtils &EPCIU)80: EPCIU(EPCIU) {81auto &EPC = EPCIU.getExecutorProcessControl();82auto &ABI = EPCIU.getABISupport();8384TrampolineSize = ABI.getTrampolineSize();85TrampolinesPerPage =86(EPC.getPageSize() - ABI.getPointerSize()) / TrampolineSize;87}8889Error EPCTrampolinePool::deallocatePool() {90std::promise<MSVCPError> DeallocResultP;91auto DeallocResultF = DeallocResultP.get_future();9293EPCIU.getExecutorProcessControl().getMemMgr().deallocate(94std::move(TrampolineBlocks),95[&](Error Err) { DeallocResultP.set_value(std::move(Err)); });9697return DeallocResultF.get();98}99100Error EPCTrampolinePool::grow() {101using namespace jitlink;102103assert(AvailableTrampolines.empty() &&104"Grow called with trampolines still available");105106auto ResolverAddress = EPCIU.getResolverBlockAddress();107assert(ResolverAddress && "Resolver address can not be null");108109auto &EPC = EPCIU.getExecutorProcessControl();110auto PageSize = EPC.getPageSize();111auto Alloc = SimpleSegmentAlloc::Create(112EPC.getMemMgr(), nullptr,113{{MemProt::Read | MemProt::Exec, {PageSize, Align(PageSize)}}});114if (!Alloc)115return Alloc.takeError();116117unsigned NumTrampolines = TrampolinesPerPage;118119auto SegInfo = Alloc->getSegInfo(MemProt::Read | MemProt::Exec);120EPCIU.getABISupport().writeTrampolines(121SegInfo.WorkingMem.data(), SegInfo.Addr, ResolverAddress, NumTrampolines);122for (unsigned I = 0; I < NumTrampolines; ++I)123AvailableTrampolines.push_back(SegInfo.Addr + (I * TrampolineSize));124125auto FA = Alloc->finalize();126if (!FA)127return FA.takeError();128129TrampolineBlocks.push_back(std::move(*FA));130131return Error::success();132}133134Error EPCIndirectStubsManager::createStub(StringRef StubName,135ExecutorAddr StubAddr,136JITSymbolFlags StubFlags) {137StubInitsMap SIM;138SIM[StubName] = std::make_pair(StubAddr, StubFlags);139return createStubs(SIM);140}141142Error EPCIndirectStubsManager::createStubs(const StubInitsMap &StubInits) {143auto AvailableStubInfos = getIndirectStubs(EPCIU, StubInits.size());144if (!AvailableStubInfos)145return AvailableStubInfos.takeError();146147{148std::lock_guard<std::mutex> Lock(ISMMutex);149unsigned ASIdx = 0;150for (auto &SI : StubInits) {151auto &A = (*AvailableStubInfos)[ASIdx++];152StubInfos[SI.first()] = std::make_pair(A, SI.second.second);153}154}155156auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();157switch (EPCIU.getABISupport().getPointerSize()) {158case 4: {159unsigned ASIdx = 0;160std::vector<tpctypes::UInt32Write> PtrUpdates;161for (auto &SI : StubInits)162PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,163static_cast<uint32_t>(SI.second.first.getValue())});164return MemAccess.writeUInt32s(PtrUpdates);165}166case 8: {167unsigned ASIdx = 0;168std::vector<tpctypes::UInt64Write> PtrUpdates;169for (auto &SI : StubInits)170PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,171static_cast<uint64_t>(SI.second.first.getValue())});172return MemAccess.writeUInt64s(PtrUpdates);173}174default:175return make_error<StringError>("Unsupported pointer size",176inconvertibleErrorCode());177}178}179180ExecutorSymbolDef EPCIndirectStubsManager::findStub(StringRef Name,181bool ExportedStubsOnly) {182std::lock_guard<std::mutex> Lock(ISMMutex);183auto I = StubInfos.find(Name);184if (I == StubInfos.end())185return ExecutorSymbolDef();186return {I->second.first.StubAddress, I->second.second};187}188189ExecutorSymbolDef EPCIndirectStubsManager::findPointer(StringRef Name) {190std::lock_guard<std::mutex> Lock(ISMMutex);191auto I = StubInfos.find(Name);192if (I == StubInfos.end())193return ExecutorSymbolDef();194return {I->second.first.PointerAddress, I->second.second};195}196197Error EPCIndirectStubsManager::updatePointer(StringRef Name,198ExecutorAddr NewAddr) {199200ExecutorAddr PtrAddr;201{202std::lock_guard<std::mutex> Lock(ISMMutex);203auto I = StubInfos.find(Name);204if (I == StubInfos.end())205return make_error<StringError>("Unknown stub name",206inconvertibleErrorCode());207PtrAddr = I->second.first.PointerAddress;208}209210auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();211switch (EPCIU.getABISupport().getPointerSize()) {212case 4: {213tpctypes::UInt32Write PUpdate(PtrAddr, NewAddr.getValue());214return MemAccess.writeUInt32s(PUpdate);215}216case 8: {217tpctypes::UInt64Write PUpdate(PtrAddr, NewAddr.getValue());218return MemAccess.writeUInt64s(PUpdate);219}220default:221return make_error<StringError>("Unsupported pointer size",222inconvertibleErrorCode());223}224}225226} // end anonymous namespace.227228namespace llvm {229namespace orc {230231EPCIndirectionUtils::ABISupport::~ABISupport() = default;232233Expected<std::unique_ptr<EPCIndirectionUtils>>234EPCIndirectionUtils::Create(ExecutorProcessControl &EPC) {235const auto &TT = EPC.getTargetTriple();236switch (TT.getArch()) {237default:238return make_error<StringError>(239std::string("No EPCIndirectionUtils available for ") + TT.str(),240inconvertibleErrorCode());241case Triple::aarch64:242case Triple::aarch64_32:243return CreateWithABI<OrcAArch64>(EPC);244245case Triple::x86:246return CreateWithABI<OrcI386>(EPC);247248case Triple::loongarch64:249return CreateWithABI<OrcLoongArch64>(EPC);250251case Triple::mips:252return CreateWithABI<OrcMips32Be>(EPC);253254case Triple::mipsel:255return CreateWithABI<OrcMips32Le>(EPC);256257case Triple::mips64:258case Triple::mips64el:259return CreateWithABI<OrcMips64>(EPC);260261case Triple::riscv64:262return CreateWithABI<OrcRiscv64>(EPC);263264case Triple::x86_64:265if (TT.getOS() == Triple::OSType::Win32)266return CreateWithABI<OrcX86_64_Win32>(EPC);267else268return CreateWithABI<OrcX86_64_SysV>(EPC);269}270}271272Error EPCIndirectionUtils::cleanup() {273274auto &MemMgr = EPC.getMemMgr();275auto Err = MemMgr.deallocate(std::move(IndirectStubAllocs));276277if (TP)278Err = joinErrors(std::move(Err),279static_cast<EPCTrampolinePool &>(*TP).deallocatePool());280281if (ResolverBlock)282Err =283joinErrors(std::move(Err), MemMgr.deallocate(std::move(ResolverBlock)));284285return Err;286}287288Expected<ExecutorAddr>289EPCIndirectionUtils::writeResolverBlock(ExecutorAddr ReentryFnAddr,290ExecutorAddr ReentryCtxAddr) {291using namespace jitlink;292293assert(ABI && "ABI can not be null");294auto ResolverSize = ABI->getResolverCodeSize();295296auto Alloc =297SimpleSegmentAlloc::Create(EPC.getMemMgr(), nullptr,298{{MemProt::Read | MemProt::Exec,299{ResolverSize, Align(EPC.getPageSize())}}});300301if (!Alloc)302return Alloc.takeError();303304auto SegInfo = Alloc->getSegInfo(MemProt::Read | MemProt::Exec);305ResolverBlockAddr = SegInfo.Addr;306ABI->writeResolverCode(SegInfo.WorkingMem.data(), ResolverBlockAddr,307ReentryFnAddr, ReentryCtxAddr);308309auto FA = Alloc->finalize();310if (!FA)311return FA.takeError();312313ResolverBlock = std::move(*FA);314return ResolverBlockAddr;315}316317std::unique_ptr<IndirectStubsManager>318EPCIndirectionUtils::createIndirectStubsManager() {319return std::make_unique<EPCIndirectStubsManager>(*this);320}321322TrampolinePool &EPCIndirectionUtils::getTrampolinePool() {323if (!TP)324TP = std::make_unique<EPCTrampolinePool>(*this);325return *TP;326}327328LazyCallThroughManager &EPCIndirectionUtils::createLazyCallThroughManager(329ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr) {330assert(!LCTM &&331"createLazyCallThroughManager can not have been called before");332LCTM = std::make_unique<LazyCallThroughManager>(ES, ErrorHandlerAddr,333&getTrampolinePool());334return *LCTM;335}336337EPCIndirectionUtils::EPCIndirectionUtils(ExecutorProcessControl &EPC,338std::unique_ptr<ABISupport> ABI)339: EPC(EPC), ABI(std::move(ABI)) {340assert(this->ABI && "ABI can not be null");341342assert(EPC.getPageSize() > getABISupport().getStubSize() &&343"Stubs larger than one page are not supported");344}345346Expected<EPCIndirectionUtils::IndirectStubInfoVector>347EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs) {348using namespace jitlink;349350std::lock_guard<std::mutex> Lock(EPCUIMutex);351352// If there aren't enough stubs available then allocate some more.353if (NumStubs > AvailableIndirectStubs.size()) {354auto NumStubsToAllocate = NumStubs;355auto PageSize = EPC.getPageSize();356auto StubBytes = alignTo(NumStubsToAllocate * ABI->getStubSize(), PageSize);357NumStubsToAllocate = StubBytes / ABI->getStubSize();358auto PtrBytes =359alignTo(NumStubsToAllocate * ABI->getPointerSize(), PageSize);360361auto StubProt = MemProt::Read | MemProt::Exec;362auto PtrProt = MemProt::Read | MemProt::Write;363364auto Alloc = SimpleSegmentAlloc::Create(365EPC.getMemMgr(), nullptr,366{{StubProt, {static_cast<size_t>(StubBytes), Align(PageSize)}},367{PtrProt, {static_cast<size_t>(PtrBytes), Align(PageSize)}}});368369if (!Alloc)370return Alloc.takeError();371372auto StubSeg = Alloc->getSegInfo(StubProt);373auto PtrSeg = Alloc->getSegInfo(PtrProt);374375ABI->writeIndirectStubsBlock(StubSeg.WorkingMem.data(), StubSeg.Addr,376PtrSeg.Addr, NumStubsToAllocate);377378auto FA = Alloc->finalize();379if (!FA)380return FA.takeError();381382IndirectStubAllocs.push_back(std::move(*FA));383384auto StubExecutorAddr = StubSeg.Addr;385auto PtrExecutorAddr = PtrSeg.Addr;386for (unsigned I = 0; I != NumStubsToAllocate; ++I) {387AvailableIndirectStubs.push_back(388IndirectStubInfo(StubExecutorAddr, PtrExecutorAddr));389StubExecutorAddr += ABI->getStubSize();390PtrExecutorAddr += ABI->getPointerSize();391}392}393394assert(NumStubs <= AvailableIndirectStubs.size() &&395"Sufficient stubs should have been allocated above");396397IndirectStubInfoVector Result;398while (NumStubs--) {399Result.push_back(AvailableIndirectStubs.back());400AvailableIndirectStubs.pop_back();401}402403return std::move(Result);404}405406static JITTargetAddress reentry(JITTargetAddress LCTMAddr,407JITTargetAddress TrampolineAddr) {408auto &LCTM = *jitTargetAddressToPointer<LazyCallThroughManager *>(LCTMAddr);409std::promise<ExecutorAddr> LandingAddrP;410auto LandingAddrF = LandingAddrP.get_future();411LCTM.resolveTrampolineLandingAddress(412ExecutorAddr(TrampolineAddr),413[&](ExecutorAddr Addr) { LandingAddrP.set_value(Addr); });414return LandingAddrF.get().getValue();415}416417Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU) {418auto &LCTM = EPCIU.getLazyCallThroughManager();419return EPCIU420.writeResolverBlock(ExecutorAddr::fromPtr(&reentry),421ExecutorAddr::fromPtr(&LCTM))422.takeError();423}424425} // end namespace orc426} // end namespace llvm427428429