1258f055eSStefan Gränitz //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===// 2258f055eSStefan Gränitz // 3258f055eSStefan Gränitz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4258f055eSStefan Gränitz // See https://llvm.org/LICENSE.txt for license information. 5258f055eSStefan Gränitz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6258f055eSStefan Gränitz // 7258f055eSStefan Gränitz //===----------------------------------------------------------------------===// 8258f055eSStefan Gränitz 9258f055eSStefan Gränitz #include "RemoteJITUtils.h" 10258f055eSStefan Gränitz 11b0abd489SElliot Goodrich #include "llvm/ADT/StringExtras.h" 12258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" 13662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" 14662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" 15ac2daacbSStefan Gränitz #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" 16258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" 17258f055eSStefan Gränitz #include "llvm/Support/FileSystem.h" 18258f055eSStefan Gränitz #include "llvm/Support/Path.h" 19258f055eSStefan Gränitz 20258f055eSStefan Gränitz #ifdef LLVM_ON_UNIX 21258f055eSStefan Gränitz #include <netdb.h> 22258f055eSStefan Gränitz #include <netinet/in.h> 23258f055eSStefan Gränitz #include <sys/socket.h> 24258f055eSStefan Gränitz #include <unistd.h> 25258f055eSStefan Gränitz #endif // LLVM_ON_UNIX 26258f055eSStefan Gränitz 27258f055eSStefan Gränitz using namespace llvm; 28258f055eSStefan Gränitz using namespace llvm::orc; 29258f055eSStefan Gränitz 30258f055eSStefan Gränitz Expected<std::unique_ptr<DefinitionGenerator>> 31ac2daacbSStefan Gränitz loadDylib(ExecutionSession &ES, StringRef RemotePath) { 32*e5754838SLang Hames if (auto Handle = ES.getExecutorProcessControl().getDylibMgr().loadDylib( 33*e5754838SLang Hames RemotePath.data())) 34ac2daacbSStefan Gränitz return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle); 35258f055eSStefan Gränitz else 36258f055eSStefan Gränitz return Handle.takeError(); 37258f055eSStefan Gränitz } 38258f055eSStefan Gränitz 39ac2daacbSStefan Gränitz static void findLocalExecutorHelper() {} 40ac2daacbSStefan Gränitz std::string findLocalExecutor(const char *HostArgv0) { 41ac2daacbSStefan Gränitz // This just needs to be some static symbol in the binary; C++ doesn't 42258f055eSStefan Gränitz // allow taking the address of ::main however. 43ac2daacbSStefan Gränitz uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper); 44ac2daacbSStefan Gränitz void *VoidPtr = reinterpret_cast<void *>(UIntPtr); 45ac2daacbSStefan Gränitz SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr)); 46258f055eSStefan Gränitz sys::path::remove_filename(FullName); 47ac2daacbSStefan Gränitz sys::path::append(FullName, "llvm-jitlink-executor"); 48258f055eSStefan Gränitz return FullName.str().str(); 49258f055eSStefan Gränitz } 50258f055eSStefan Gränitz 51258f055eSStefan Gränitz #ifndef LLVM_ON_UNIX 52258f055eSStefan Gränitz 53258f055eSStefan Gränitz // FIXME: Add support for Windows. 54ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>> 55ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) { 56258f055eSStefan Gränitz return make_error<StringError>( 57258f055eSStefan Gränitz "Remote JITing not yet supported on non-unix platforms", 58258f055eSStefan Gränitz inconvertibleErrorCode()); 59258f055eSStefan Gränitz } 60258f055eSStefan Gränitz 61258f055eSStefan Gränitz // FIXME: Add support for Windows. 62ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>> 63ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) { 64258f055eSStefan Gränitz return make_error<StringError>( 65258f055eSStefan Gränitz "Remote JITing not yet supported on non-unix platforms", 66258f055eSStefan Gränitz inconvertibleErrorCode()); 67258f055eSStefan Gränitz } 68258f055eSStefan Gränitz 69258f055eSStefan Gränitz #else 70258f055eSStefan Gränitz 71ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>> 72ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) { 73258f055eSStefan Gränitz constexpr int ReadEnd = 0; 74258f055eSStefan Gränitz constexpr int WriteEnd = 1; 75258f055eSStefan Gränitz 76ac2daacbSStefan Gränitz if (!sys::fs::can_execute(ExecutablePath)) 77ac2daacbSStefan Gränitz return make_error<StringError>( 78ac2daacbSStefan Gränitz formatv("Specified executor invalid: {0}", ExecutablePath), 79ac2daacbSStefan Gränitz inconvertibleErrorCode()); 80ac2daacbSStefan Gränitz 81258f055eSStefan Gränitz // Pipe FDs. 82258f055eSStefan Gränitz int ToExecutor[2]; 83258f055eSStefan Gränitz int FromExecutor[2]; 84258f055eSStefan Gränitz 85258f055eSStefan Gränitz // Create pipes to/from the executor.. 86258f055eSStefan Gränitz if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0) 87258f055eSStefan Gränitz return make_error<StringError>("Unable to create pipe for executor", 88258f055eSStefan Gränitz inconvertibleErrorCode()); 89258f055eSStefan Gränitz 90ac2daacbSStefan Gränitz pid_t ProcessID = fork(); 91258f055eSStefan Gränitz if (ProcessID == 0) { 92258f055eSStefan Gränitz // In the child... 93258f055eSStefan Gränitz 94258f055eSStefan Gränitz // Close the parent ends of the pipes 95258f055eSStefan Gränitz close(ToExecutor[WriteEnd]); 96258f055eSStefan Gränitz close(FromExecutor[ReadEnd]); 97258f055eSStefan Gränitz 98258f055eSStefan Gränitz // Execute the child process. 99b7d5b0d0SStefan Gränitz std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag; 100258f055eSStefan Gränitz { 101258f055eSStefan Gränitz ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1); 102258f055eSStefan Gränitz strcpy(ExecPath.get(), ExecutablePath.data()); 103258f055eSStefan Gränitz 104b7d5b0d0SStefan Gränitz const char *TestOutputFlagStr = "test-jitloadergdb"; 105b7d5b0d0SStefan Gränitz TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1); 106b7d5b0d0SStefan Gränitz strcpy(TestOutputFlag.get(), TestOutputFlagStr); 107b7d5b0d0SStefan Gränitz 108258f055eSStefan Gränitz std::string FDSpecifierStr("filedescs="); 109258f055eSStefan Gränitz FDSpecifierStr += utostr(ToExecutor[ReadEnd]); 110258f055eSStefan Gränitz FDSpecifierStr += ','; 111258f055eSStefan Gränitz FDSpecifierStr += utostr(FromExecutor[WriteEnd]); 112258f055eSStefan Gränitz FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1); 113258f055eSStefan Gränitz strcpy(FDSpecifier.get(), FDSpecifierStr.c_str()); 114258f055eSStefan Gränitz } 115258f055eSStefan Gränitz 116b7d5b0d0SStefan Gränitz char *const Args[] = {ExecPath.get(), TestOutputFlag.get(), 117b7d5b0d0SStefan Gränitz FDSpecifier.get(), nullptr}; 118258f055eSStefan Gränitz int RC = execvp(ExecPath.get(), Args); 119258f055eSStefan Gränitz if (RC != 0) 120258f055eSStefan Gränitz return make_error<StringError>( 121258f055eSStefan Gränitz "Unable to launch out-of-process executor '" + ExecutablePath + "'\n", 122258f055eSStefan Gränitz inconvertibleErrorCode()); 123258f055eSStefan Gränitz 124258f055eSStefan Gränitz llvm_unreachable("Fork won't return in success case"); 125258f055eSStefan Gränitz } 126258f055eSStefan Gränitz // else we're the parent... 127258f055eSStefan Gränitz 128258f055eSStefan Gränitz // Close the child ends of the pipes 129258f055eSStefan Gränitz close(ToExecutor[ReadEnd]); 130258f055eSStefan Gränitz close(FromExecutor[WriteEnd]); 131258f055eSStefan Gränitz 132ac2daacbSStefan Gränitz auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( 13369703b1fSLang Hames std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), 134abdb82b2SLang Hames SimpleRemoteEPC::Setup(), 135ac2daacbSStefan Gränitz FromExecutor[ReadEnd], ToExecutor[WriteEnd]); 136ac2daacbSStefan Gränitz if (!EPC) 137ac2daacbSStefan Gränitz return EPC.takeError(); 138258f055eSStefan Gränitz 139ac2daacbSStefan Gränitz return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID)); 140258f055eSStefan Gränitz } 141258f055eSStefan Gränitz 142258f055eSStefan Gränitz static Expected<int> connectTCPSocketImpl(std::string Host, 143258f055eSStefan Gränitz std::string PortStr) { 144258f055eSStefan Gränitz addrinfo *AI; 145258f055eSStefan Gränitz addrinfo Hints{}; 146258f055eSStefan Gränitz Hints.ai_family = AF_INET; 147258f055eSStefan Gränitz Hints.ai_socktype = SOCK_STREAM; 148258f055eSStefan Gränitz Hints.ai_flags = AI_NUMERICSERV; 149258f055eSStefan Gränitz 150258f055eSStefan Gränitz if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI)) 151258f055eSStefan Gränitz return make_error<StringError>( 152258f055eSStefan Gränitz formatv("address resolution failed ({0})", gai_strerror(EC)), 153258f055eSStefan Gränitz inconvertibleErrorCode()); 154258f055eSStefan Gränitz 155258f055eSStefan Gränitz // Cycle through the returned addrinfo structures and connect to the first 156258f055eSStefan Gränitz // reachable endpoint. 157258f055eSStefan Gränitz int SockFD; 158258f055eSStefan Gränitz addrinfo *Server; 159258f055eSStefan Gränitz for (Server = AI; Server != nullptr; Server = Server->ai_next) { 160258f055eSStefan Gränitz // If socket fails, maybe it's because the address family is not supported. 161258f055eSStefan Gränitz // Skip to the next addrinfo structure. 162258f055eSStefan Gränitz if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) 163258f055eSStefan Gränitz continue; 164258f055eSStefan Gränitz 165258f055eSStefan Gränitz // If connect works, we exit the loop with a working socket. 166258f055eSStefan Gränitz if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) 167258f055eSStefan Gränitz break; 168258f055eSStefan Gränitz 169258f055eSStefan Gränitz close(SockFD); 170258f055eSStefan Gränitz } 171258f055eSStefan Gränitz freeaddrinfo(AI); 172258f055eSStefan Gränitz 173258f055eSStefan Gränitz // Did we reach the end of the loop without connecting to a valid endpoint? 174258f055eSStefan Gränitz if (Server == nullptr) 175258f055eSStefan Gränitz return make_error<StringError>("invalid hostname", 176258f055eSStefan Gränitz inconvertibleErrorCode()); 177258f055eSStefan Gränitz 178258f055eSStefan Gränitz return SockFD; 179258f055eSStefan Gränitz } 180258f055eSStefan Gränitz 181ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>> 182ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) { 183258f055eSStefan Gränitz auto CreateErr = [NetworkAddress](StringRef Details) { 184258f055eSStefan Gränitz return make_error<StringError>( 185258f055eSStefan Gränitz formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress, 186258f055eSStefan Gränitz Details), 187258f055eSStefan Gränitz inconvertibleErrorCode()); 188258f055eSStefan Gränitz }; 189258f055eSStefan Gränitz 190258f055eSStefan Gränitz StringRef Host, PortStr; 191258f055eSStefan Gränitz std::tie(Host, PortStr) = NetworkAddress.split(':'); 192258f055eSStefan Gränitz if (Host.empty()) 193258f055eSStefan Gränitz return CreateErr("host name cannot be empty"); 194258f055eSStefan Gränitz if (PortStr.empty()) 195258f055eSStefan Gränitz return CreateErr("port cannot be empty"); 196258f055eSStefan Gränitz int Port = 0; 197258f055eSStefan Gränitz if (PortStr.getAsInteger(10, Port)) 198258f055eSStefan Gränitz return CreateErr("port number is not a valid integer"); 199258f055eSStefan Gränitz 200258f055eSStefan Gränitz Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); 201258f055eSStefan Gränitz if (!SockFD) 202258f055eSStefan Gränitz return CreateErr(toString(SockFD.takeError())); 203258f055eSStefan Gränitz 2042e6c92c5SLang Hames return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( 20569703b1fSLang Hames std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), 206abdb82b2SLang Hames SimpleRemoteEPC::Setup(), *SockFD); 207258f055eSStefan Gränitz } 208258f055eSStefan Gränitz 209258f055eSStefan Gränitz #endif 210