blob: ce94bf1e039aad9ff80fc69890259926971828ed [file] [log] [blame]
//===- 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, nullptr, 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<shared::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.Dealloc)
DeallocationActions.push_back(ActPair.Dealloc);
// 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]
.Dealloc.runWithSPSRetErrorMerged());
// 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 *>();
if (!Seg.Content.empty())
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)},
toSysMemoryProtectionFlags(Seg.AG.getMemProt())))
return BailOut(errorCodeToError(EC));
if ((Seg.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
}
// Run finalization actions.
for (auto &ActPair : FR.Actions) {
if (auto Err = ActPair.Finalize.runWithSPSRetErrorMerged())
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().runWithSPSRetErrorMerged());
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