mirror of
https://github.com/RPCS3/llvm.git
synced 2026-01-31 01:25:19 +01:00
to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351636 91177308-0d34-0410-b5e6-96231b3b80d8
450 lines
17 KiB
C++
450 lines
17 KiB
C++
//===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the OrcRemoteTargetServer class. It can be used to build a
|
|
// JIT server that can execute code sent from an OrcRemoteTargetClient.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
|
|
#define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
|
|
|
|
#include "llvm/ExecutionEngine/JITSymbol.h"
|
|
#include "llvm/ExecutionEngine/Orc/OrcError.h"
|
|
#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/Memory.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <system_error>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#define DEBUG_TYPE "orc-remote"
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
namespace remote {
|
|
|
|
template <typename ChannelT, typename TargetT>
|
|
class OrcRemoteTargetServer
|
|
: public rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel> {
|
|
public:
|
|
using SymbolLookupFtor =
|
|
std::function<JITTargetAddress(const std::string &Name)>;
|
|
|
|
using EHFrameRegistrationFtor =
|
|
std::function<void(uint8_t *Addr, uint32_t Size)>;
|
|
|
|
OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup,
|
|
EHFrameRegistrationFtor EHFramesRegister,
|
|
EHFrameRegistrationFtor EHFramesDeregister)
|
|
: rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel>(Channel, true),
|
|
SymbolLookup(std::move(SymbolLookup)),
|
|
EHFramesRegister(std::move(EHFramesRegister)),
|
|
EHFramesDeregister(std::move(EHFramesDeregister)) {
|
|
using ThisT = typename std::remove_reference<decltype(*this)>::type;
|
|
addHandler<exec::CallIntVoid>(*this, &ThisT::handleCallIntVoid);
|
|
addHandler<exec::CallMain>(*this, &ThisT::handleCallMain);
|
|
addHandler<exec::CallVoidVoid>(*this, &ThisT::handleCallVoidVoid);
|
|
addHandler<mem::CreateRemoteAllocator>(*this,
|
|
&ThisT::handleCreateRemoteAllocator);
|
|
addHandler<mem::DestroyRemoteAllocator>(
|
|
*this, &ThisT::handleDestroyRemoteAllocator);
|
|
addHandler<mem::ReadMem>(*this, &ThisT::handleReadMem);
|
|
addHandler<mem::ReserveMem>(*this, &ThisT::handleReserveMem);
|
|
addHandler<mem::SetProtections>(*this, &ThisT::handleSetProtections);
|
|
addHandler<mem::WriteMem>(*this, &ThisT::handleWriteMem);
|
|
addHandler<mem::WritePtr>(*this, &ThisT::handleWritePtr);
|
|
addHandler<eh::RegisterEHFrames>(*this, &ThisT::handleRegisterEHFrames);
|
|
addHandler<eh::DeregisterEHFrames>(*this, &ThisT::handleDeregisterEHFrames);
|
|
addHandler<stubs::CreateIndirectStubsOwner>(
|
|
*this, &ThisT::handleCreateIndirectStubsOwner);
|
|
addHandler<stubs::DestroyIndirectStubsOwner>(
|
|
*this, &ThisT::handleDestroyIndirectStubsOwner);
|
|
addHandler<stubs::EmitIndirectStubs>(*this,
|
|
&ThisT::handleEmitIndirectStubs);
|
|
addHandler<stubs::EmitResolverBlock>(*this,
|
|
&ThisT::handleEmitResolverBlock);
|
|
addHandler<stubs::EmitTrampolineBlock>(*this,
|
|
&ThisT::handleEmitTrampolineBlock);
|
|
addHandler<utils::GetSymbolAddress>(*this, &ThisT::handleGetSymbolAddress);
|
|
addHandler<utils::GetRemoteInfo>(*this, &ThisT::handleGetRemoteInfo);
|
|
addHandler<utils::TerminateSession>(*this, &ThisT::handleTerminateSession);
|
|
}
|
|
|
|
// FIXME: Remove move/copy ops once MSVC supports synthesizing move ops.
|
|
OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete;
|
|
OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete;
|
|
|
|
OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) = default;
|
|
OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete;
|
|
|
|
Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) {
|
|
return callB<utils::RequestCompile>(TrampolineAddr);
|
|
}
|
|
|
|
bool receivedTerminate() const { return TerminateFlag; }
|
|
|
|
private:
|
|
struct Allocator {
|
|
Allocator() = default;
|
|
Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {}
|
|
|
|
Allocator &operator=(Allocator &&Other) {
|
|
Allocs = std::move(Other.Allocs);
|
|
return *this;
|
|
}
|
|
|
|
~Allocator() {
|
|
for (auto &Alloc : Allocs)
|
|
sys::Memory::releaseMappedMemory(Alloc.second);
|
|
}
|
|
|
|
Error allocate(void *&Addr, size_t Size, uint32_t Align) {
|
|
std::error_code EC;
|
|
sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(
|
|
Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
|
|
if (EC)
|
|
return errorCodeToError(EC);
|
|
|
|
Addr = MB.base();
|
|
assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc");
|
|
Allocs[MB.base()] = std::move(MB);
|
|
return Error::success();
|
|
}
|
|
|
|
Error setProtections(void *block, unsigned Flags) {
|
|
auto I = Allocs.find(block);
|
|
if (I == Allocs.end())
|
|
return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized));
|
|
return errorCodeToError(
|
|
sys::Memory::protectMappedMemory(I->second, Flags));
|
|
}
|
|
|
|
private:
|
|
std::map<void *, sys::MemoryBlock> Allocs;
|
|
};
|
|
|
|
static Error doNothing() { return Error::success(); }
|
|
|
|
static JITTargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) {
|
|
auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr);
|
|
auto AddrOrErr = T->requestCompile(static_cast<JITTargetAddress>(
|
|
reinterpret_cast<uintptr_t>(TrampolineAddr)));
|
|
// FIXME: Allow customizable failure substitution functions.
|
|
assert(AddrOrErr && "Compile request failed");
|
|
return *AddrOrErr;
|
|
}
|
|
|
|
Expected<int32_t> handleCallIntVoid(JITTargetAddress Addr) {
|
|
using IntVoidFnTy = int (*)();
|
|
|
|
IntVoidFnTy Fn =
|
|
reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr));
|
|
|
|
LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n");
|
|
int Result = Fn();
|
|
LLVM_DEBUG(dbgs() << " Result = " << Result << "\n");
|
|
|
|
return Result;
|
|
}
|
|
|
|
Expected<int32_t> handleCallMain(JITTargetAddress Addr,
|
|
std::vector<std::string> Args) {
|
|
using MainFnTy = int (*)(int, const char *[]);
|
|
|
|
MainFnTy Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr));
|
|
int ArgC = Args.size() + 1;
|
|
int Idx = 1;
|
|
std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]);
|
|
ArgV[0] = "<jit process>";
|
|
for (auto &Arg : Args)
|
|
ArgV[Idx++] = Arg.c_str();
|
|
ArgV[ArgC] = 0;
|
|
LLVM_DEBUG(for (int Idx = 0; Idx < ArgC; ++Idx) {
|
|
llvm::dbgs() << "Arg " << Idx << ": " << ArgV[Idx] << "\n";
|
|
});
|
|
|
|
LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n");
|
|
int Result = Fn(ArgC, ArgV.get());
|
|
LLVM_DEBUG(dbgs() << " Result = " << Result << "\n");
|
|
|
|
return Result;
|
|
}
|
|
|
|
Error handleCallVoidVoid(JITTargetAddress Addr) {
|
|
using VoidVoidFnTy = void (*)();
|
|
|
|
VoidVoidFnTy Fn =
|
|
reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr));
|
|
|
|
LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n");
|
|
Fn();
|
|
LLVM_DEBUG(dbgs() << " Complete.\n");
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) {
|
|
auto I = Allocators.find(Id);
|
|
if (I != Allocators.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse));
|
|
LLVM_DEBUG(dbgs() << " Created allocator " << Id << "\n");
|
|
Allocators[Id] = Allocator();
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
|
|
auto I = IndirectStubsOwners.find(Id);
|
|
if (I != IndirectStubsOwners.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse));
|
|
LLVM_DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n");
|
|
IndirectStubsOwners[Id] = ISBlockOwnerList();
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
|
|
uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
|
|
LLVM_DEBUG(dbgs() << " Registering EH frames at "
|
|
<< format("0x%016x", TAddr) << ", Size = " << Size
|
|
<< " bytes\n");
|
|
EHFramesDeregister(Addr, Size);
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) {
|
|
auto I = Allocators.find(Id);
|
|
if (I == Allocators.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
|
|
Allocators.erase(I);
|
|
LLVM_DEBUG(dbgs() << " Destroyed allocator " << Id << "\n");
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
|
|
auto I = IndirectStubsOwners.find(Id);
|
|
if (I == IndirectStubsOwners.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
|
|
IndirectStubsOwners.erase(I);
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<std::tuple<JITTargetAddress, JITTargetAddress, uint32_t>>
|
|
handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id,
|
|
uint32_t NumStubsRequired) {
|
|
LLVM_DEBUG(dbgs() << " ISMgr " << Id << " request " << NumStubsRequired
|
|
<< " stubs.\n");
|
|
|
|
auto StubOwnerItr = IndirectStubsOwners.find(Id);
|
|
if (StubOwnerItr == IndirectStubsOwners.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
|
|
|
|
typename TargetT::IndirectStubsInfo IS;
|
|
if (auto Err =
|
|
TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr))
|
|
return std::move(Err);
|
|
|
|
JITTargetAddress StubsBase = static_cast<JITTargetAddress>(
|
|
reinterpret_cast<uintptr_t>(IS.getStub(0)));
|
|
JITTargetAddress PtrsBase = static_cast<JITTargetAddress>(
|
|
reinterpret_cast<uintptr_t>(IS.getPtr(0)));
|
|
uint32_t NumStubsEmitted = IS.getNumStubs();
|
|
|
|
auto &BlockList = StubOwnerItr->second;
|
|
BlockList.push_back(std::move(IS));
|
|
|
|
return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted);
|
|
}
|
|
|
|
Error handleEmitResolverBlock() {
|
|
std::error_code EC;
|
|
ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
|
|
TargetT::ResolverCodeSize, nullptr,
|
|
sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
|
|
if (EC)
|
|
return errorCodeToError(EC);
|
|
|
|
TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
|
|
&reenter, this);
|
|
|
|
return errorCodeToError(sys::Memory::protectMappedMemory(
|
|
ResolverBlock.getMemoryBlock(),
|
|
sys::Memory::MF_READ | sys::Memory::MF_EXEC));
|
|
}
|
|
|
|
Expected<std::tuple<JITTargetAddress, uint32_t>> handleEmitTrampolineBlock() {
|
|
std::error_code EC;
|
|
auto TrampolineBlock =
|
|
sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
|
|
sys::Process::getPageSize(), nullptr,
|
|
sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
|
|
if (EC)
|
|
return errorCodeToError(EC);
|
|
|
|
uint32_t NumTrampolines =
|
|
(sys::Process::getPageSize() - TargetT::PointerSize) /
|
|
TargetT::TrampolineSize;
|
|
|
|
uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
|
|
TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
|
|
NumTrampolines);
|
|
|
|
EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
|
|
sys::Memory::MF_READ |
|
|
sys::Memory::MF_EXEC);
|
|
|
|
TrampolineBlocks.push_back(std::move(TrampolineBlock));
|
|
|
|
auto TrampolineBaseAddr = static_cast<JITTargetAddress>(
|
|
reinterpret_cast<uintptr_t>(TrampolineMem));
|
|
|
|
return std::make_tuple(TrampolineBaseAddr, NumTrampolines);
|
|
}
|
|
|
|
Expected<JITTargetAddress> handleGetSymbolAddress(const std::string &Name) {
|
|
JITTargetAddress Addr = SymbolLookup(Name);
|
|
LLVM_DEBUG(dbgs() << " Symbol '" << Name
|
|
<< "' = " << format("0x%016x", Addr) << "\n");
|
|
return Addr;
|
|
}
|
|
|
|
Expected<std::tuple<std::string, uint32_t, uint32_t, uint32_t, uint32_t>>
|
|
handleGetRemoteInfo() {
|
|
std::string ProcessTriple = sys::getProcessTriple();
|
|
uint32_t PointerSize = TargetT::PointerSize;
|
|
uint32_t PageSize = sys::Process::getPageSize();
|
|
uint32_t TrampolineSize = TargetT::TrampolineSize;
|
|
uint32_t IndirectStubSize = TargetT::IndirectStubsInfo::StubSize;
|
|
LLVM_DEBUG(dbgs() << " Remote info:\n"
|
|
<< " triple = '" << ProcessTriple << "'\n"
|
|
<< " pointer size = " << PointerSize << "\n"
|
|
<< " page size = " << PageSize << "\n"
|
|
<< " trampoline size = " << TrampolineSize << "\n"
|
|
<< " indirect stub size = " << IndirectStubSize
|
|
<< "\n");
|
|
return std::make_tuple(ProcessTriple, PointerSize, PageSize, TrampolineSize,
|
|
IndirectStubSize);
|
|
}
|
|
|
|
Expected<std::vector<uint8_t>> handleReadMem(JITTargetAddress RSrc,
|
|
uint64_t Size) {
|
|
uint8_t *Src = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(RSrc));
|
|
|
|
LLVM_DEBUG(dbgs() << " Reading " << Size << " bytes from "
|
|
<< format("0x%016x", RSrc) << "\n");
|
|
|
|
std::vector<uint8_t> Buffer;
|
|
Buffer.resize(Size);
|
|
for (uint8_t *P = Src; Size != 0; --Size)
|
|
Buffer.push_back(*P++);
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
|
|
uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
|
|
LLVM_DEBUG(dbgs() << " Registering EH frames at "
|
|
<< format("0x%016x", TAddr) << ", Size = " << Size
|
|
<< " bytes\n");
|
|
EHFramesRegister(Addr, Size);
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<JITTargetAddress> handleReserveMem(ResourceIdMgr::ResourceId Id,
|
|
uint64_t Size, uint32_t Align) {
|
|
auto I = Allocators.find(Id);
|
|
if (I == Allocators.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
|
|
auto &Allocator = I->second;
|
|
void *LocalAllocAddr = nullptr;
|
|
if (auto Err = Allocator.allocate(LocalAllocAddr, Size, Align))
|
|
return std::move(Err);
|
|
|
|
LLVM_DEBUG(dbgs() << " Allocator " << Id << " reserved " << LocalAllocAddr
|
|
<< " (" << Size << " bytes, alignment " << Align
|
|
<< ")\n");
|
|
|
|
JITTargetAddress AllocAddr = static_cast<JITTargetAddress>(
|
|
reinterpret_cast<uintptr_t>(LocalAllocAddr));
|
|
|
|
return AllocAddr;
|
|
}
|
|
|
|
Error handleSetProtections(ResourceIdMgr::ResourceId Id,
|
|
JITTargetAddress Addr, uint32_t Flags) {
|
|
auto I = Allocators.find(Id);
|
|
if (I == Allocators.end())
|
|
return errorCodeToError(
|
|
orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
|
|
auto &Allocator = I->second;
|
|
void *LocalAddr = reinterpret_cast<void *>(static_cast<uintptr_t>(Addr));
|
|
LLVM_DEBUG(dbgs() << " Allocator " << Id << " set permissions on "
|
|
<< LocalAddr << " to "
|
|
<< (Flags & sys::Memory::MF_READ ? 'R' : '-')
|
|
<< (Flags & sys::Memory::MF_WRITE ? 'W' : '-')
|
|
<< (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n");
|
|
return Allocator.setProtections(LocalAddr, Flags);
|
|
}
|
|
|
|
Error handleTerminateSession() {
|
|
TerminateFlag = true;
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleWriteMem(DirectBufferWriter DBW) {
|
|
LLVM_DEBUG(dbgs() << " Writing " << DBW.getSize() << " bytes to "
|
|
<< format("0x%016x", DBW.getDst()) << "\n");
|
|
return Error::success();
|
|
}
|
|
|
|
Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) {
|
|
LLVM_DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr)
|
|
<< " = " << format("0x%016x", PtrVal) << "\n");
|
|
uintptr_t *Ptr =
|
|
reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr));
|
|
*Ptr = static_cast<uintptr_t>(PtrVal);
|
|
return Error::success();
|
|
}
|
|
|
|
SymbolLookupFtor SymbolLookup;
|
|
EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister;
|
|
std::map<ResourceIdMgr::ResourceId, Allocator> Allocators;
|
|
using ISBlockOwnerList = std::vector<typename TargetT::IndirectStubsInfo>;
|
|
std::map<ResourceIdMgr::ResourceId, ISBlockOwnerList> IndirectStubsOwners;
|
|
sys::OwningMemoryBlock ResolverBlock;
|
|
std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
|
|
bool TerminateFlag = false;
|
|
};
|
|
|
|
} // end namespace remote
|
|
} // end namespace orc
|
|
} // end namespace llvm
|
|
|
|
#undef DEBUG_TYPE
|
|
|
|
#endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
|