Home | History | Annotate | Line # | Download | only in Orc
      1 //===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This file defines the OrcRemoteTargetServer class. It can be used to build a
     10 // JIT server that can execute code sent from an OrcRemoteTargetClient.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
     15 #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
     16 
     17 #include "llvm/ExecutionEngine/JITSymbol.h"
     18 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
     19 #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h"
     20 #include "llvm/ExecutionEngine/Orc/Shared/OrcError.h"
     21 #include "llvm/Support/Debug.h"
     22 #include "llvm/Support/Error.h"
     23 #include "llvm/Support/Format.h"
     24 #include "llvm/Support/Host.h"
     25 #include "llvm/Support/Memory.h"
     26 #include "llvm/Support/Process.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 #include <algorithm>
     29 #include <cassert>
     30 #include <cstddef>
     31 #include <cstdint>
     32 #include <functional>
     33 #include <map>
     34 #include <memory>
     35 #include <string>
     36 #include <system_error>
     37 #include <tuple>
     38 #include <type_traits>
     39 #include <vector>
     40 
     41 #define DEBUG_TYPE "orc-remote"
     42 
     43 namespace llvm {
     44 namespace orc {
     45 namespace remote {
     46 
     47 template <typename ChannelT, typename TargetT>
     48 class OrcRemoteTargetServer
     49     : public shared::SingleThreadedRPCEndpoint<shared::RawByteChannel> {
     50 public:
     51   using SymbolLookupFtor =
     52       std::function<JITTargetAddress(const std::string &Name)>;
     53 
     54   using EHFrameRegistrationFtor =
     55       std::function<void(uint8_t *Addr, uint32_t Size)>;
     56 
     57   OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup,
     58                         EHFrameRegistrationFtor EHFramesRegister,
     59                         EHFrameRegistrationFtor EHFramesDeregister)
     60       : shared::SingleThreadedRPCEndpoint<shared::RawByteChannel>(Channel,
     61                                                                   true),
     62         SymbolLookup(std::move(SymbolLookup)),
     63         EHFramesRegister(std::move(EHFramesRegister)),
     64         EHFramesDeregister(std::move(EHFramesDeregister)) {
     65     using ThisT = std::remove_reference_t<decltype(*this)>;
     66     addHandler<exec::CallIntVoid>(*this, &ThisT::handleCallIntVoid);
     67     addHandler<exec::CallIntInt>(*this, &ThisT::handleCallIntInt);
     68     addHandler<exec::CallMain>(*this, &ThisT::handleCallMain);
     69     addHandler<exec::CallVoidVoid>(*this, &ThisT::handleCallVoidVoid);
     70     addHandler<mem::CreateRemoteAllocator>(*this,
     71                                            &ThisT::handleCreateRemoteAllocator);
     72     addHandler<mem::DestroyRemoteAllocator>(
     73         *this, &ThisT::handleDestroyRemoteAllocator);
     74     addHandler<mem::ReadMem>(*this, &ThisT::handleReadMem);
     75     addHandler<mem::ReserveMem>(*this, &ThisT::handleReserveMem);
     76     addHandler<mem::SetProtections>(*this, &ThisT::handleSetProtections);
     77     addHandler<mem::WriteMem>(*this, &ThisT::handleWriteMem);
     78     addHandler<mem::WritePtr>(*this, &ThisT::handleWritePtr);
     79     addHandler<eh::RegisterEHFrames>(*this, &ThisT::handleRegisterEHFrames);
     80     addHandler<eh::DeregisterEHFrames>(*this, &ThisT::handleDeregisterEHFrames);
     81     addHandler<stubs::CreateIndirectStubsOwner>(
     82         *this, &ThisT::handleCreateIndirectStubsOwner);
     83     addHandler<stubs::DestroyIndirectStubsOwner>(
     84         *this, &ThisT::handleDestroyIndirectStubsOwner);
     85     addHandler<stubs::EmitIndirectStubs>(*this,
     86                                          &ThisT::handleEmitIndirectStubs);
     87     addHandler<stubs::EmitResolverBlock>(*this,
     88                                          &ThisT::handleEmitResolverBlock);
     89     addHandler<stubs::EmitTrampolineBlock>(*this,
     90                                            &ThisT::handleEmitTrampolineBlock);
     91     addHandler<utils::GetSymbolAddress>(*this, &ThisT::handleGetSymbolAddress);
     92     addHandler<utils::GetRemoteInfo>(*this, &ThisT::handleGetRemoteInfo);
     93     addHandler<utils::TerminateSession>(*this, &ThisT::handleTerminateSession);
     94   }
     95 
     96   // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops.
     97   OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete;
     98   OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete;
     99 
    100   OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) = default;
    101   OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete;
    102 
    103   Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) {
    104     return callB<utils::RequestCompile>(TrampolineAddr);
    105   }
    106 
    107   bool receivedTerminate() const { return TerminateFlag; }
    108 
    109 private:
    110   struct Allocator {
    111     Allocator() = default;
    112     Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {}
    113 
    114     Allocator &operator=(Allocator &&Other) {
    115       Allocs = std::move(Other.Allocs);
    116       return *this;
    117     }
    118 
    119     ~Allocator() {
    120       for (auto &Alloc : Allocs)
    121         sys::Memory::releaseMappedMemory(Alloc.second);
    122     }
    123 
    124     Error allocate(void *&Addr, size_t Size, uint32_t Align) {
    125       std::error_code EC;
    126       sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(
    127           Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
    128       if (EC)
    129         return errorCodeToError(EC);
    130 
    131       Addr = MB.base();
    132       assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc");
    133       Allocs[MB.base()] = std::move(MB);
    134       return Error::success();
    135     }
    136 
    137     Error setProtections(void *block, unsigned Flags) {
    138       auto I = Allocs.find(block);
    139       if (I == Allocs.end())
    140         return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized));
    141       return errorCodeToError(
    142           sys::Memory::protectMappedMemory(I->second, Flags));
    143     }
    144 
    145   private:
    146     std::map<void *, sys::MemoryBlock> Allocs;
    147   };
    148 
    149   static Error doNothing() { return Error::success(); }
    150 
    151   static JITTargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) {
    152     auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr);
    153     auto AddrOrErr = T->requestCompile(static_cast<JITTargetAddress>(
    154         reinterpret_cast<uintptr_t>(TrampolineAddr)));
    155     // FIXME: Allow customizable failure substitution functions.
    156     assert(AddrOrErr && "Compile request failed");
    157     return *AddrOrErr;
    158   }
    159 
    160   Expected<int32_t> handleCallIntVoid(JITTargetAddress Addr) {
    161     using IntVoidFnTy = int (*)();
    162 
    163     IntVoidFnTy Fn =
    164         reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr));
    165 
    166     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
    167     int Result = Fn();
    168     LLVM_DEBUG(dbgs() << "  Result = " << Result << "\n");
    169 
    170     return Result;
    171   }
    172 
    173   Expected<int32_t> handleCallIntInt(JITTargetAddress Addr, int Arg) {
    174     using IntIntFnTy = int (*)(int);
    175 
    176     IntIntFnTy Fn = reinterpret_cast<IntIntFnTy>(static_cast<uintptr_t>(Addr));
    177 
    178     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr)
    179                       << " with argument " << Arg << "\n");
    180     int Result = Fn(Arg);
    181     LLVM_DEBUG(dbgs() << "  Result = " << Result << "\n");
    182 
    183     return Result;
    184   }
    185 
    186   Expected<int32_t> handleCallMain(JITTargetAddress Addr,
    187                                    std::vector<std::string> Args) {
    188     using MainFnTy = int (*)(int, const char *[]);
    189 
    190     MainFnTy Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr));
    191     int ArgC = Args.size() + 1;
    192     int Idx = 1;
    193     std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]);
    194     ArgV[0] = "<jit process>";
    195     for (auto &Arg : Args)
    196       ArgV[Idx++] = Arg.c_str();
    197     ArgV[ArgC] = 0;
    198     LLVM_DEBUG(for (int Idx = 0; Idx < ArgC; ++Idx) {
    199       llvm::dbgs() << "Arg " << Idx << ": " << ArgV[Idx] << "\n";
    200     });
    201 
    202     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
    203     int Result = Fn(ArgC, ArgV.get());
    204     LLVM_DEBUG(dbgs() << "  Result = " << Result << "\n");
    205 
    206     return Result;
    207   }
    208 
    209   Error handleCallVoidVoid(JITTargetAddress Addr) {
    210     using VoidVoidFnTy = void (*)();
    211 
    212     VoidVoidFnTy Fn =
    213         reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr));
    214 
    215     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
    216     Fn();
    217     LLVM_DEBUG(dbgs() << "  Complete.\n");
    218 
    219     return Error::success();
    220   }
    221 
    222   Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) {
    223     auto I = Allocators.find(Id);
    224     if (I != Allocators.end())
    225       return errorCodeToError(
    226                orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse));
    227     LLVM_DEBUG(dbgs() << "  Created allocator " << Id << "\n");
    228     Allocators[Id] = Allocator();
    229     return Error::success();
    230   }
    231 
    232   Error handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
    233     auto I = IndirectStubsOwners.find(Id);
    234     if (I != IndirectStubsOwners.end())
    235       return errorCodeToError(
    236                orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse));
    237     LLVM_DEBUG(dbgs() << "  Create indirect stubs owner " << Id << "\n");
    238     IndirectStubsOwners[Id] = ISBlockOwnerList();
    239     return Error::success();
    240   }
    241 
    242   Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
    243     uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
    244     LLVM_DEBUG(dbgs() << "  Registering EH frames at "
    245                       << format("0x%016x", TAddr) << ", Size = " << Size
    246                       << " bytes\n");
    247     EHFramesDeregister(Addr, Size);
    248     return Error::success();
    249   }
    250 
    251   Error handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) {
    252     auto I = Allocators.find(Id);
    253     if (I == Allocators.end())
    254       return errorCodeToError(
    255                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
    256     Allocators.erase(I);
    257     LLVM_DEBUG(dbgs() << "  Destroyed allocator " << Id << "\n");
    258     return Error::success();
    259   }
    260 
    261   Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
    262     auto I = IndirectStubsOwners.find(Id);
    263     if (I == IndirectStubsOwners.end())
    264       return errorCodeToError(
    265                orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
    266     IndirectStubsOwners.erase(I);
    267     return Error::success();
    268   }
    269 
    270   Expected<std::tuple<JITTargetAddress, JITTargetAddress, uint32_t>>
    271   handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id,
    272                           uint32_t NumStubsRequired) {
    273     LLVM_DEBUG(dbgs() << "  ISMgr " << Id << " request " << NumStubsRequired
    274                       << " stubs.\n");
    275 
    276     auto StubOwnerItr = IndirectStubsOwners.find(Id);
    277     if (StubOwnerItr == IndirectStubsOwners.end())
    278       return errorCodeToError(
    279                orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
    280 
    281     auto IS = LocalIndirectStubsInfo<TargetT>::create(
    282         NumStubsRequired, sys::Process::getPageSizeEstimate());
    283     if (!IS)
    284       return IS.takeError();
    285 
    286     JITTargetAddress StubsBase = pointerToJITTargetAddress(IS->getStub(0));
    287     JITTargetAddress PtrsBase = pointerToJITTargetAddress(IS->getPtr(0));
    288     uint32_t NumStubsEmitted = IS->getNumStubs();
    289 
    290     auto &BlockList = StubOwnerItr->second;
    291     BlockList.push_back(std::move(*IS));
    292 
    293     return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted);
    294   }
    295 
    296   Error handleEmitResolverBlock() {
    297     std::error_code EC;
    298     ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
    299         TargetT::ResolverCodeSize, nullptr,
    300         sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
    301     if (EC)
    302       return errorCodeToError(EC);
    303 
    304     TargetT::writeResolverCode(static_cast<char *>(ResolverBlock.base()),
    305                                pointerToJITTargetAddress(ResolverBlock.base()),
    306                                pointerToJITTargetAddress(&reenter),
    307                                pointerToJITTargetAddress(this));
    308 
    309     return errorCodeToError(sys::Memory::protectMappedMemory(
    310         ResolverBlock.getMemoryBlock(),
    311         sys::Memory::MF_READ | sys::Memory::MF_EXEC));
    312   }
    313 
    314   Expected<std::tuple<JITTargetAddress, uint32_t>> handleEmitTrampolineBlock() {
    315     std::error_code EC;
    316     auto TrampolineBlock =
    317         sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
    318             sys::Process::getPageSizeEstimate(), nullptr,
    319             sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
    320     if (EC)
    321       return errorCodeToError(EC);
    322 
    323     uint32_t NumTrampolines =
    324         (sys::Process::getPageSizeEstimate() - TargetT::PointerSize) /
    325         TargetT::TrampolineSize;
    326 
    327     char *TrampolineMem = static_cast<char *>(TrampolineBlock.base());
    328     TargetT::writeTrampolines(
    329         TrampolineMem, pointerToJITTargetAddress(TrampolineMem),
    330         pointerToJITTargetAddress(ResolverBlock.base()), NumTrampolines);
    331 
    332     EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
    333                                           sys::Memory::MF_READ |
    334                                               sys::Memory::MF_EXEC);
    335 
    336     TrampolineBlocks.push_back(std::move(TrampolineBlock));
    337 
    338     return std::make_tuple(pointerToJITTargetAddress(TrampolineMem),
    339                            NumTrampolines);
    340   }
    341 
    342   Expected<JITTargetAddress> handleGetSymbolAddress(const std::string &Name) {
    343     JITTargetAddress Addr = SymbolLookup(Name);
    344     LLVM_DEBUG(dbgs() << "  Symbol '" << Name
    345                       << "' =  " << format("0x%016x", Addr) << "\n");
    346     return Addr;
    347   }
    348 
    349   Expected<std::tuple<std::string, uint32_t, uint32_t, uint32_t, uint32_t>>
    350   handleGetRemoteInfo() {
    351     std::string ProcessTriple = sys::getProcessTriple();
    352     uint32_t PointerSize = TargetT::PointerSize;
    353     uint32_t PageSize = sys::Process::getPageSizeEstimate();
    354     uint32_t TrampolineSize = TargetT::TrampolineSize;
    355     uint32_t IndirectStubSize = TargetT::StubSize;
    356     LLVM_DEBUG(dbgs() << "  Remote info:\n"
    357                       << "    triple             = '" << ProcessTriple << "'\n"
    358                       << "    pointer size       = " << PointerSize << "\n"
    359                       << "    page size          = " << PageSize << "\n"
    360                       << "    trampoline size    = " << TrampolineSize << "\n"
    361                       << "    indirect stub size = " << IndirectStubSize
    362                       << "\n");
    363     return std::make_tuple(ProcessTriple, PointerSize, PageSize, TrampolineSize,
    364                            IndirectStubSize);
    365   }
    366 
    367   Expected<std::vector<uint8_t>> handleReadMem(JITTargetAddress RSrc,
    368                                                uint64_t Size) {
    369     uint8_t *Src = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(RSrc));
    370 
    371     LLVM_DEBUG(dbgs() << "  Reading " << Size << " bytes from "
    372                       << format("0x%016x", RSrc) << "\n");
    373 
    374     std::vector<uint8_t> Buffer;
    375     Buffer.resize(Size);
    376     for (uint8_t *P = Src; Size != 0; --Size)
    377       Buffer.push_back(*P++);
    378 
    379     return Buffer;
    380   }
    381 
    382   Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
    383     uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
    384     LLVM_DEBUG(dbgs() << "  Registering EH frames at "
    385                       << format("0x%016x", TAddr) << ", Size = " << Size
    386                       << " bytes\n");
    387     EHFramesRegister(Addr, Size);
    388     return Error::success();
    389   }
    390 
    391   Expected<JITTargetAddress> handleReserveMem(ResourceIdMgr::ResourceId Id,
    392                                               uint64_t Size, uint32_t Align) {
    393     auto I = Allocators.find(Id);
    394     if (I == Allocators.end())
    395       return errorCodeToError(
    396                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
    397     auto &Allocator = I->second;
    398     void *LocalAllocAddr = nullptr;
    399     if (auto Err = Allocator.allocate(LocalAllocAddr, Size, Align))
    400       return std::move(Err);
    401 
    402     LLVM_DEBUG(dbgs() << "  Allocator " << Id << " reserved " << LocalAllocAddr
    403                       << " (" << Size << " bytes, alignment " << Align
    404                       << ")\n");
    405 
    406     JITTargetAddress AllocAddr = static_cast<JITTargetAddress>(
    407         reinterpret_cast<uintptr_t>(LocalAllocAddr));
    408 
    409     return AllocAddr;
    410   }
    411 
    412   Error handleSetProtections(ResourceIdMgr::ResourceId Id,
    413                              JITTargetAddress Addr, uint32_t Flags) {
    414     auto I = Allocators.find(Id);
    415     if (I == Allocators.end())
    416       return errorCodeToError(
    417                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
    418     auto &Allocator = I->second;
    419     void *LocalAddr = reinterpret_cast<void *>(static_cast<uintptr_t>(Addr));
    420     LLVM_DEBUG(dbgs() << "  Allocator " << Id << " set permissions on "
    421                       << LocalAddr << " to "
    422                       << (Flags & sys::Memory::MF_READ ? 'R' : '-')
    423                       << (Flags & sys::Memory::MF_WRITE ? 'W' : '-')
    424                       << (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n");
    425     return Allocator.setProtections(LocalAddr, Flags);
    426   }
    427 
    428   Error handleTerminateSession() {
    429     TerminateFlag = true;
    430     return Error::success();
    431   }
    432 
    433   Error handleWriteMem(DirectBufferWriter DBW) {
    434     LLVM_DEBUG(dbgs() << "  Writing " << DBW.getSize() << " bytes to "
    435                       << format("0x%016x", DBW.getDst()) << "\n");
    436     return Error::success();
    437   }
    438 
    439   Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) {
    440     LLVM_DEBUG(dbgs() << "  Writing pointer *" << format("0x%016x", Addr)
    441                       << " = " << format("0x%016x", PtrVal) << "\n");
    442     uintptr_t *Ptr =
    443         reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr));
    444     *Ptr = static_cast<uintptr_t>(PtrVal);
    445     return Error::success();
    446   }
    447 
    448   SymbolLookupFtor SymbolLookup;
    449   EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister;
    450   std::map<ResourceIdMgr::ResourceId, Allocator> Allocators;
    451   using ISBlockOwnerList = std::vector<LocalIndirectStubsInfo<TargetT>>;
    452   std::map<ResourceIdMgr::ResourceId, ISBlockOwnerList> IndirectStubsOwners;
    453   sys::OwningMemoryBlock ResolverBlock;
    454   std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
    455   bool TerminateFlag = false;
    456 };
    457 
    458 } // end namespace remote
    459 } // end namespace orc
    460 } // end namespace llvm
    461 
    462 #undef DEBUG_TYPE
    463 
    464 #endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
    465