blob: 7989ec41952d718af9023d10369f51df1eace2aa [file] [log] [blame]
//===------ TargetProcessControl.cpp -- Target process control APIs -------===//
//
// 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/TPCIndirectionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h"
#include "llvm/Support/MathExtras.h"
#include <future>
using namespace llvm;
using namespace llvm::orc;
namespace llvm {
namespace orc {
class TPCIndirectionUtilsAccess {
public:
using IndirectStubInfo = TPCIndirectionUtils::IndirectStubInfo;
using IndirectStubInfoVector = TPCIndirectionUtils::IndirectStubInfoVector;
static Expected<IndirectStubInfoVector>
getIndirectStubs(TPCIndirectionUtils &TPCIU, unsigned NumStubs) {
return TPCIU.getIndirectStubs(NumStubs);
};
};
} // end namespace orc
} // end namespace llvm
namespace {
class TPCTrampolinePool : public TrampolinePool {
public:
TPCTrampolinePool(TPCIndirectionUtils &TPCIU);
Error deallocatePool();
protected:
Error grow() override;
using Allocation = jitlink::JITLinkMemoryManager::Allocation;
TPCIndirectionUtils &TPCIU;
unsigned TrampolineSize = 0;
unsigned TrampolinesPerPage = 0;
std::vector<std::unique_ptr<Allocation>> TrampolineBlocks;
};
class TPCIndirectStubsManager : public IndirectStubsManager,
private TPCIndirectionUtilsAccess {
public:
TPCIndirectStubsManager(TPCIndirectionUtils &TPCIU) : TPCIU(TPCIU) {}
Error deallocateStubs();
Error createStub(StringRef StubName, JITTargetAddress StubAddr,
JITSymbolFlags StubFlags) override;
Error createStubs(const StubInitsMap &StubInits) override;
JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override;
JITEvaluatedSymbol findPointer(StringRef Name) override;
Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override;
private:
using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;
std::mutex ISMMutex;
TPCIndirectionUtils &TPCIU;
StringMap<StubInfo> StubInfos;
};
TPCTrampolinePool::TPCTrampolinePool(TPCIndirectionUtils &TPCIU)
: TPCIU(TPCIU) {
auto &TPC = TPCIU.getTargetProcessControl();
auto &ABI = TPCIU.getABISupport();
TrampolineSize = ABI.getTrampolineSize();
TrampolinesPerPage =
(TPC.getPageSize() - ABI.getPointerSize()) / TrampolineSize;
}
Error TPCTrampolinePool::deallocatePool() {
Error Err = Error::success();
for (auto &Alloc : TrampolineBlocks)
Err = joinErrors(std::move(Err), Alloc->deallocate());
return Err;
}
Error TPCTrampolinePool::grow() {
assert(AvailableTrampolines.empty() &&
"Grow called with trampolines still available");
auto ResolverAddress = TPCIU.getResolverBlockAddress();
assert(ResolverAddress && "Resolver address can not be null");
auto &TPC = TPCIU.getTargetProcessControl();
constexpr auto TrampolinePagePermissions =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_EXEC);
auto PageSize = TPC.getPageSize();
jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
Request[TrampolinePagePermissions] = {PageSize, static_cast<size_t>(PageSize),
0};
auto Alloc = TPC.getMemMgr().allocate(nullptr, Request);
if (!Alloc)
return Alloc.takeError();
unsigned NumTrampolines = TrampolinesPerPage;
auto WorkingMemory = (*Alloc)->getWorkingMemory(TrampolinePagePermissions);
auto TargetAddress = (*Alloc)->getTargetMemory(TrampolinePagePermissions);
TPCIU.getABISupport().writeTrampolines(WorkingMemory.data(), TargetAddress,
ResolverAddress, NumTrampolines);
auto TargetAddr = (*Alloc)->getTargetMemory(TrampolinePagePermissions);
for (unsigned I = 0; I < NumTrampolines; ++I)
AvailableTrampolines.push_back(TargetAddr + (I * TrampolineSize));
if (auto Err = (*Alloc)->finalize())
return Err;
TrampolineBlocks.push_back(std::move(*Alloc));
return Error::success();
}
Error TPCIndirectStubsManager::createStub(StringRef StubName,
JITTargetAddress StubAddr,
JITSymbolFlags StubFlags) {
StubInitsMap SIM;
SIM[StubName] = std::make_pair(StubAddr, StubFlags);
return createStubs(SIM);
}
Error TPCIndirectStubsManager::createStubs(const StubInitsMap &StubInits) {
auto AvailableStubInfos = getIndirectStubs(TPCIU, StubInits.size());
if (!AvailableStubInfos)
return AvailableStubInfos.takeError();
{
std::lock_guard<std::mutex> Lock(ISMMutex);
unsigned ASIdx = 0;
for (auto &SI : StubInits) {
auto &A = (*AvailableStubInfos)[ASIdx++];
StubInfos[SI.first()] = std::make_pair(A, SI.second.second);
}
}
auto &MemAccess = TPCIU.getTargetProcessControl().getMemoryAccess();
switch (TPCIU.getABISupport().getPointerSize()) {
case 4: {
unsigned ASIdx = 0;
std::vector<tpctypes::UInt32Write> PtrUpdates;
for (auto &SI : StubInits)
PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
static_cast<uint32_t>(SI.second.first)});
return MemAccess.writeUInt32s(PtrUpdates);
}
case 8: {
unsigned ASIdx = 0;
std::vector<tpctypes::UInt64Write> PtrUpdates;
for (auto &SI : StubInits)
PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
static_cast<uint64_t>(SI.second.first)});
return MemAccess.writeUInt64s(PtrUpdates);
}
default:
return make_error<StringError>("Unsupported pointer size",
inconvertibleErrorCode());
}
}
JITEvaluatedSymbol TPCIndirectStubsManager::findStub(StringRef Name,
bool ExportedStubsOnly) {
std::lock_guard<std::mutex> Lock(ISMMutex);
auto I = StubInfos.find(Name);
if (I == StubInfos.end())
return nullptr;
return {I->second.first.StubAddress, I->second.second};
}
JITEvaluatedSymbol TPCIndirectStubsManager::findPointer(StringRef Name) {
std::lock_guard<std::mutex> Lock(ISMMutex);
auto I = StubInfos.find(Name);
if (I == StubInfos.end())
return nullptr;
return {I->second.first.PointerAddress, I->second.second};
}
Error TPCIndirectStubsManager::updatePointer(StringRef Name,
JITTargetAddress NewAddr) {
JITTargetAddress PtrAddr = 0;
{
std::lock_guard<std::mutex> Lock(ISMMutex);
auto I = StubInfos.find(Name);
if (I == StubInfos.end())
return make_error<StringError>("Unknown stub name",
inconvertibleErrorCode());
PtrAddr = I->second.first.PointerAddress;
}
auto &MemAccess = TPCIU.getTargetProcessControl().getMemoryAccess();
switch (TPCIU.getABISupport().getPointerSize()) {
case 4: {
tpctypes::UInt32Write PUpdate(PtrAddr, NewAddr);
return MemAccess.writeUInt32s(PUpdate);
}
case 8: {
tpctypes::UInt64Write PUpdate(PtrAddr, NewAddr);
return MemAccess.writeUInt64s(PUpdate);
}
default:
return make_error<StringError>("Unsupported pointer size",
inconvertibleErrorCode());
}
}
} // end anonymous namespace.
namespace llvm {
namespace orc {
TPCIndirectionUtils::ABISupport::~ABISupport() {}
Expected<std::unique_ptr<TPCIndirectionUtils>>
TPCIndirectionUtils::Create(TargetProcessControl &TPC) {
const auto &TT = TPC.getTargetTriple();
switch (TT.getArch()) {
default:
return make_error<StringError>(
std::string("No TPCIndirectionUtils available for ") + TT.str(),
inconvertibleErrorCode());
case Triple::aarch64:
case Triple::aarch64_32:
return CreateWithABI<OrcAArch64>(TPC);
case Triple::x86:
return CreateWithABI<OrcI386>(TPC);
case Triple::mips:
return CreateWithABI<OrcMips32Be>(TPC);
case Triple::mipsel:
return CreateWithABI<OrcMips32Le>(TPC);
case Triple::mips64:
case Triple::mips64el:
return CreateWithABI<OrcMips64>(TPC);
case Triple::x86_64:
if (TT.getOS() == Triple::OSType::Win32)
return CreateWithABI<OrcX86_64_Win32>(TPC);
else
return CreateWithABI<OrcX86_64_SysV>(TPC);
}
}
Error TPCIndirectionUtils::cleanup() {
Error Err = Error::success();
for (auto &A : IndirectStubAllocs)
Err = joinErrors(std::move(Err), A->deallocate());
if (TP)
Err = joinErrors(std::move(Err),
static_cast<TPCTrampolinePool &>(*TP).deallocatePool());
if (ResolverBlock)
Err = joinErrors(std::move(Err), ResolverBlock->deallocate());
return Err;
}
Expected<JITTargetAddress>
TPCIndirectionUtils::writeResolverBlock(JITTargetAddress ReentryFnAddr,
JITTargetAddress ReentryCtxAddr) {
assert(ABI && "ABI can not be null");
constexpr auto ResolverBlockPermissions =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_EXEC);
auto ResolverSize = ABI->getResolverCodeSize();
jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
Request[ResolverBlockPermissions] = {TPC.getPageSize(),
static_cast<size_t>(ResolverSize), 0};
auto Alloc = TPC.getMemMgr().allocate(nullptr, Request);
if (!Alloc)
return Alloc.takeError();
auto WorkingMemory = (*Alloc)->getWorkingMemory(ResolverBlockPermissions);
ResolverBlockAddr = (*Alloc)->getTargetMemory(ResolverBlockPermissions);
ABI->writeResolverCode(WorkingMemory.data(), ResolverBlockAddr, ReentryFnAddr,
ReentryCtxAddr);
if (auto Err = (*Alloc)->finalize())
return std::move(Err);
ResolverBlock = std::move(*Alloc);
return ResolverBlockAddr;
}
std::unique_ptr<IndirectStubsManager>
TPCIndirectionUtils::createIndirectStubsManager() {
return std::make_unique<TPCIndirectStubsManager>(*this);
}
TrampolinePool &TPCIndirectionUtils::getTrampolinePool() {
if (!TP)
TP = std::make_unique<TPCTrampolinePool>(*this);
return *TP;
}
LazyCallThroughManager &TPCIndirectionUtils::createLazyCallThroughManager(
ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) {
assert(!LCTM &&
"createLazyCallThroughManager can not have been called before");
LCTM = std::make_unique<LazyCallThroughManager>(ES, ErrorHandlerAddr,
&getTrampolinePool());
return *LCTM;
}
TPCIndirectionUtils::TPCIndirectionUtils(TargetProcessControl &TPC,
std::unique_ptr<ABISupport> ABI)
: TPC(TPC), ABI(std::move(ABI)) {
assert(this->ABI && "ABI can not be null");
assert(TPC.getPageSize() > getABISupport().getStubSize() &&
"Stubs larger than one page are not supported");
}
Expected<TPCIndirectionUtils::IndirectStubInfoVector>
TPCIndirectionUtils::getIndirectStubs(unsigned NumStubs) {
std::lock_guard<std::mutex> Lock(TPCUIMutex);
// If there aren't enough stubs available then allocate some more.
if (NumStubs > AvailableIndirectStubs.size()) {
auto NumStubsToAllocate = NumStubs;
auto PageSize = TPC.getPageSize();
auto StubBytes = alignTo(NumStubsToAllocate * ABI->getStubSize(), PageSize);
NumStubsToAllocate = StubBytes / ABI->getStubSize();
auto PointerBytes =
alignTo(NumStubsToAllocate * ABI->getPointerSize(), PageSize);
constexpr auto StubPagePermissions =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_EXEC);
constexpr auto PointerPagePermissions =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_WRITE);
jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
Request[StubPagePermissions] = {PageSize, static_cast<size_t>(StubBytes),
0};
Request[PointerPagePermissions] = {PageSize, 0, PointerBytes};
auto Alloc = TPC.getMemMgr().allocate(nullptr, Request);
if (!Alloc)
return Alloc.takeError();
auto StubTargetAddr = (*Alloc)->getTargetMemory(StubPagePermissions);
auto PointerTargetAddr = (*Alloc)->getTargetMemory(PointerPagePermissions);
ABI->writeIndirectStubsBlock(
(*Alloc)->getWorkingMemory(StubPagePermissions).data(), StubTargetAddr,
PointerTargetAddr, NumStubsToAllocate);
if (auto Err = (*Alloc)->finalize())
return std::move(Err);
for (unsigned I = 0; I != NumStubsToAllocate; ++I) {
AvailableIndirectStubs.push_back(
IndirectStubInfo(StubTargetAddr, PointerTargetAddr));
StubTargetAddr += ABI->getStubSize();
PointerTargetAddr += ABI->getPointerSize();
}
IndirectStubAllocs.push_back(std::move(*Alloc));
}
assert(NumStubs <= AvailableIndirectStubs.size() &&
"Sufficient stubs should have been allocated above");
IndirectStubInfoVector Result;
while (NumStubs--) {
Result.push_back(AvailableIndirectStubs.back());
AvailableIndirectStubs.pop_back();
}
return std::move(Result);
}
static JITTargetAddress reentry(JITTargetAddress LCTMAddr,
JITTargetAddress TrampolineAddr) {
auto &LCTM = *jitTargetAddressToPointer<LazyCallThroughManager *>(LCTMAddr);
std::promise<JITTargetAddress> LandingAddrP;
auto LandingAddrF = LandingAddrP.get_future();
LCTM.resolveTrampolineLandingAddress(
TrampolineAddr,
[&](JITTargetAddress Addr) { LandingAddrP.set_value(Addr); });
return LandingAddrF.get();
}
Error setUpInProcessLCTMReentryViaTPCIU(TPCIndirectionUtils &TPCIU) {
auto &LCTM = TPCIU.getLazyCallThroughManager();
return TPCIU
.writeResolverBlock(pointerToJITTargetAddress(&reentry),
pointerToJITTargetAddress(&LCTM))
.takeError();
}
} // end namespace orc
} // end namespace llvm