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