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 OrcRemoteTargetServer(ChannelT & Channel,SymbolLookupFtor SymbolLookup,EHFrameRegistrationFtor EHFramesRegister,EHFrameRegistrationFtor EHFramesDeregister)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 requestCompile(JITTargetAddress TrampolineAddr)103 Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) { 104 return callB<utils::RequestCompile>(TrampolineAddr); 105 } 106 receivedTerminate()107 bool receivedTerminate() const { return TerminateFlag; } 108 109 private: 110 struct Allocator { 111 Allocator() = default; AllocatorAllocator112 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 ~AllocatorAllocator119 ~Allocator() { 120 for (auto &Alloc : Allocs) 121 sys::Memory::releaseMappedMemory(Alloc.second); 122 } 123 allocateAllocator124 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 setProtectionsAllocator137 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 doNothing()149 static Error doNothing() { return Error::success(); } 150 reenter(void * JITTargetAddr,void * TrampolineAddr)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 handleCallIntVoid(JITTargetAddress Addr)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 handleCallIntInt(JITTargetAddress Addr,int Arg)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 handleCallMain(JITTargetAddress Addr,std::vector<std::string> Args)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 handleCallVoidVoid(JITTargetAddress Addr)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 handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id)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 handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id)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 handleDeregisterEHFrames(JITTargetAddress TAddr,uint32_t Size)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 handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id)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 handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id)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>> handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id,uint32_t NumStubsRequired)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 handleEmitResolverBlock()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 handleEmitTrampolineBlock()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 handleGetSymbolAddress(const std::string & Name)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>> handleGetRemoteInfo()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 handleReadMem(JITTargetAddress RSrc,uint64_t Size)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 handleRegisterEHFrames(JITTargetAddress TAddr,uint32_t Size)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 handleReserveMem(ResourceIdMgr::ResourceId Id,uint64_t Size,uint32_t Align)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 handleSetProtections(ResourceIdMgr::ResourceId Id,JITTargetAddress Addr,uint32_t Flags)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 handleTerminateSession()428 Error handleTerminateSession() { 429 TerminateFlag = true; 430 return Error::success(); 431 } 432 handleWriteMem(DirectBufferWriter DBW)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 handleWritePtr(JITTargetAddress Addr,JITTargetAddress PtrVal)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