| //===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===// |
| // |
| // 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/TargetProcess/SimpleExecutorMemoryManager.h" |
| |
| #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| #define DEBUG_TYPE "orc" |
| |
| namespace llvm { |
| namespace orc { |
| namespace rt_bootstrap { |
| |
| SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() { |
| assert(Allocations.empty() && "shutdown not called?"); |
| } |
| |
| Expected<ExecutorAddr> SimpleExecutorMemoryManager::allocate(uint64_t Size) { |
| std::error_code EC; |
| auto MB = sys::Memory::allocateMappedMemory( |
| Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); |
| if (EC) |
| return errorCodeToError(EC); |
| std::lock_guard<std::mutex> Lock(M); |
| assert(!Allocations.count(MB.base()) && "Duplicate allocation addr"); |
| Allocations[MB.base()].Size = Size; |
| return ExecutorAddr::fromPtr(MB.base()); |
| } |
| |
| Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) { |
| ExecutorAddr Base(~0ULL); |
| std::vector<tpctypes::WrapperFunctionCall> DeallocationActions; |
| size_t SuccessfulFinalizationActions = 0; |
| |
| if (FR.Segments.empty()) { |
| // NOTE: Finalizing nothing is currently a no-op. Should it be an error? |
| if (FR.Actions.empty()) |
| return Error::success(); |
| else |
| return make_error<StringError>("Finalization actions attached to empty " |
| "finalization request", |
| inconvertibleErrorCode()); |
| } |
| |
| for (auto &Seg : FR.Segments) |
| Base = std::min(Base, Seg.Addr); |
| |
| for (auto &ActPair : FR.Actions) |
| if (ActPair.Deallocate.Func) |
| DeallocationActions.push_back(ActPair.Deallocate); |
| |
| // Get the Allocation for this finalization. |
| size_t AllocSize = 0; |
| { |
| std::lock_guard<std::mutex> Lock(M); |
| auto I = Allocations.find(Base.toPtr<void *>()); |
| if (I == Allocations.end()) |
| return make_error<StringError>("Attempt to finalize unrecognized " |
| "allocation " + |
| formatv("{0:x}", Base.getValue()), |
| inconvertibleErrorCode()); |
| AllocSize = I->second.Size; |
| I->second.DeallocationActions = std::move(DeallocationActions); |
| } |
| ExecutorAddr AllocEnd = Base + ExecutorAddrDiff(AllocSize); |
| |
| // Bail-out function: this will run deallocation actions corresponding to any |
| // completed finalization actions, then deallocate memory. |
| auto BailOut = [&](Error Err) { |
| std::pair<void *, Allocation> AllocToDestroy; |
| |
| // Get allocation to destory. |
| { |
| std::lock_guard<std::mutex> Lock(M); |
| auto I = Allocations.find(Base.toPtr<void *>()); |
| |
| // Check for missing allocation (effective a double free). |
| if (I == Allocations.end()) |
| return joinErrors( |
| std::move(Err), |
| make_error<StringError>("No allocation entry found " |
| "for " + |
| formatv("{0:x}", Base.getValue()), |
| inconvertibleErrorCode())); |
| AllocToDestroy = std::move(*I); |
| Allocations.erase(I); |
| } |
| |
| // Run deallocation actions for all completed finalization actions. |
| while (SuccessfulFinalizationActions) |
| Err = |
| joinErrors(std::move(Err), FR.Actions[--SuccessfulFinalizationActions] |
| .Deallocate.runWithSPSRet()); |
| |
| // Deallocate memory. |
| sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size); |
| if (auto EC = sys::Memory::releaseMappedMemory(MB)) |
| Err = joinErrors(std::move(Err), errorCodeToError(EC)); |
| |
| return Err; |
| }; |
| |
| // Copy content and apply permissions. |
| for (auto &Seg : FR.Segments) { |
| |
| // Check segment ranges. |
| if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size())) |
| return BailOut(make_error<StringError>( |
| formatv("Segment {0:x} content size ({1:x} bytes) " |
| "exceeds segment size ({2:x} bytes)", |
| Seg.Addr.getValue(), Seg.Content.size(), Seg.Size), |
| inconvertibleErrorCode())); |
| ExecutorAddr SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size); |
| if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd)) |
| return BailOut(make_error<StringError>( |
| formatv("Segment {0:x} -- {1:x} crosses boundary of " |
| "allocation {2:x} -- {3:x}", |
| Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(), |
| AllocEnd.getValue()), |
| inconvertibleErrorCode())); |
| |
| char *Mem = Seg.Addr.toPtr<char *>(); |
| memcpy(Mem, Seg.Content.data(), Seg.Content.size()); |
| memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size()); |
| assert(Seg.Size <= std::numeric_limits<size_t>::max()); |
| if (auto EC = sys::Memory::protectMappedMemory( |
| {Mem, static_cast<size_t>(Seg.Size)}, |
| tpctypes::fromWireProtectionFlags(Seg.Prot))) |
| return BailOut(errorCodeToError(EC)); |
| if (Seg.Prot & tpctypes::WPF_Exec) |
| sys::Memory::InvalidateInstructionCache(Mem, Seg.Size); |
| } |
| |
| // Run finalization actions. |
| for (auto &ActPair : FR.Actions) { |
| if (auto Err = ActPair.Finalize.runWithSPSRet()) |
| return BailOut(std::move(Err)); |
| ++SuccessfulFinalizationActions; |
| } |
| |
| return Error::success(); |
| } |
| |
| Error SimpleExecutorMemoryManager::deallocate( |
| const std::vector<ExecutorAddr> &Bases) { |
| std::vector<std::pair<void *, Allocation>> AllocPairs; |
| AllocPairs.reserve(Bases.size()); |
| |
| // Get allocation to destory. |
| Error Err = Error::success(); |
| { |
| std::lock_guard<std::mutex> Lock(M); |
| for (auto &Base : Bases) { |
| auto I = Allocations.find(Base.toPtr<void *>()); |
| |
| // Check for missing allocation (effective a double free). |
| if (I != Allocations.end()) { |
| AllocPairs.push_back(std::move(*I)); |
| Allocations.erase(I); |
| } else |
| Err = joinErrors( |
| std::move(Err), |
| make_error<StringError>("No allocation entry found " |
| "for " + |
| formatv("{0:x}", Base.getValue()), |
| inconvertibleErrorCode())); |
| } |
| } |
| |
| while (!AllocPairs.empty()) { |
| auto &P = AllocPairs.back(); |
| Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second)); |
| AllocPairs.pop_back(); |
| } |
| |
| return Err; |
| } |
| |
| Error SimpleExecutorMemoryManager::shutdown() { |
| |
| AllocationsMap AM; |
| { |
| std::lock_guard<std::mutex> Lock(M); |
| AM = std::move(Allocations); |
| } |
| |
| Error Err = Error::success(); |
| for (auto &KV : AM) |
| Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second)); |
| return Err; |
| } |
| |
| void SimpleExecutorMemoryManager::addBootstrapSymbols( |
| StringMap<ExecutorAddr> &M) { |
| M[rt::SimpleExecutorMemoryManagerInstanceName] = ExecutorAddr::fromPtr(this); |
| M[rt::SimpleExecutorMemoryManagerReserveWrapperName] = |
| ExecutorAddr::fromPtr(&reserveWrapper); |
| M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] = |
| ExecutorAddr::fromPtr(&finalizeWrapper); |
| M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] = |
| ExecutorAddr::fromPtr(&deallocateWrapper); |
| } |
| |
| Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) { |
| Error Err = Error::success(); |
| |
| while (!A.DeallocationActions.empty()) { |
| Err = joinErrors(std::move(Err), |
| A.DeallocationActions.back().runWithSPSRet()); |
| A.DeallocationActions.pop_back(); |
| } |
| |
| sys::MemoryBlock MB(Base, A.Size); |
| if (auto EC = sys::Memory::releaseMappedMemory(MB)) |
| Err = joinErrors(std::move(Err), errorCodeToError(EC)); |
| |
| return Err; |
| } |
| |
| llvm::orc::shared::CWrapperFunctionResult |
| SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData, |
| size_t ArgSize) { |
| return shared::WrapperFunction< |
| rt::SPSSimpleExecutorMemoryManagerReserveSignature>:: |
| handle(ArgData, ArgSize, |
| shared::makeMethodWrapperHandler( |
| &SimpleExecutorMemoryManager::allocate)) |
| .release(); |
| } |
| |
| llvm::orc::shared::CWrapperFunctionResult |
| SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData, |
| size_t ArgSize) { |
| return shared::WrapperFunction< |
| rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>:: |
| handle(ArgData, ArgSize, |
| shared::makeMethodWrapperHandler( |
| &SimpleExecutorMemoryManager::finalize)) |
| .release(); |
| } |
| |
| llvm::orc::shared::CWrapperFunctionResult |
| SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData, |
| size_t ArgSize) { |
| return shared::WrapperFunction< |
| rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>:: |
| handle(ArgData, ArgSize, |
| shared::makeMethodWrapperHandler( |
| &SimpleExecutorMemoryManager::deallocate)) |
| .release(); |
| } |
| |
| } // namespace rt_bootstrap |
| } // end namespace orc |
| } // end namespace llvm |