209 lines · cpp
1//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- 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-exception6//7//===----------------------------------------------------------------------===//8 9#include "RemoteJITUtils.h"10 11#include "llvm/ADT/StringExtras.h"12#include "llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h"13#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"14#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"15#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"16#include "llvm/Support/FileSystem.h"17#include "llvm/Support/Path.h"18 19#ifdef LLVM_ON_UNIX20#include <netdb.h>21#include <netinet/in.h>22#include <sys/socket.h>23#include <unistd.h>24#endif // LLVM_ON_UNIX25 26using namespace llvm;27using namespace llvm::orc;28 29Expected<std::unique_ptr<DefinitionGenerator>>30loadDylib(ExecutionSession &ES, StringRef RemotePath) {31 if (auto Handle = ES.getExecutorProcessControl().getDylibMgr().loadDylib(32 RemotePath.data()))33 return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);34 else35 return Handle.takeError();36}37 38static void findLocalExecutorHelper() {}39std::string findLocalExecutor(const char *HostArgv0) {40 // This just needs to be some static symbol in the binary; C++ doesn't41 // allow taking the address of ::main however.42 uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);43 void *VoidPtr = reinterpret_cast<void *>(UIntPtr);44 SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));45 sys::path::remove_filename(FullName);46 sys::path::append(FullName, "llvm-jitlink-executor");47 return FullName.str().str();48}49 50#ifndef LLVM_ON_UNIX51 52// FIXME: Add support for Windows.53Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>54launchLocalExecutor(StringRef ExecutablePath) {55 return make_error<StringError>(56 "Remote JITing not yet supported on non-unix platforms",57 inconvertibleErrorCode());58}59 60// FIXME: Add support for Windows.61Expected<std::unique_ptr<SimpleRemoteEPC>>62connectTCPSocket(StringRef NetworkAddress) {63 return make_error<StringError>(64 "Remote JITing not yet supported on non-unix platforms",65 inconvertibleErrorCode());66}67 68#else69 70Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>71launchLocalExecutor(StringRef ExecutablePath) {72 constexpr int ReadEnd = 0;73 constexpr int WriteEnd = 1;74 75 if (!sys::fs::can_execute(ExecutablePath))76 return make_error<StringError>(77 formatv("Specified executor invalid: {0}", ExecutablePath),78 inconvertibleErrorCode());79 80 // Pipe FDs.81 int ToExecutor[2];82 int FromExecutor[2];83 84 // Create pipes to/from the executor..85 if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)86 return make_error<StringError>("Unable to create pipe for executor",87 inconvertibleErrorCode());88 89 pid_t ProcessID = fork();90 if (ProcessID == 0) {91 // In the child...92 93 // Close the parent ends of the pipes94 close(ToExecutor[WriteEnd]);95 close(FromExecutor[ReadEnd]);96 97 // Execute the child process.98 std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag;99 {100 ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);101 strcpy(ExecPath.get(), ExecutablePath.data());102 103 const char *TestOutputFlagStr = "test-jitloadergdb";104 TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1);105 strcpy(TestOutputFlag.get(), TestOutputFlagStr);106 107 std::string FDSpecifierStr("filedescs=");108 FDSpecifierStr += utostr(ToExecutor[ReadEnd]);109 FDSpecifierStr += ',';110 FDSpecifierStr += utostr(FromExecutor[WriteEnd]);111 FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);112 strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());113 }114 115 char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),116 FDSpecifier.get(), nullptr};117 int RC = execvp(ExecPath.get(), Args);118 if (RC != 0)119 return make_error<StringError>(120 "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",121 inconvertibleErrorCode());122 123 llvm_unreachable("Fork won't return in success case");124 }125 // else we're the parent...126 127 // Close the child ends of the pipes128 close(ToExecutor[ReadEnd]);129 close(FromExecutor[WriteEnd]);130 131 auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(132 std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),133 SimpleRemoteEPC::Setup(),134 FromExecutor[ReadEnd], ToExecutor[WriteEnd]);135 if (!EPC)136 return EPC.takeError();137 138 return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));139}140 141static Expected<int> connectTCPSocketImpl(std::string Host,142 std::string PortStr) {143 addrinfo *AI;144 addrinfo Hints{};145 Hints.ai_family = AF_INET;146 Hints.ai_socktype = SOCK_STREAM;147 Hints.ai_flags = AI_NUMERICSERV;148 149 if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))150 return make_error<StringError>(151 formatv("address resolution failed ({0})", gai_strerror(EC)),152 inconvertibleErrorCode());153 154 // Cycle through the returned addrinfo structures and connect to the first155 // reachable endpoint.156 int SockFD;157 addrinfo *Server;158 for (Server = AI; Server != nullptr; Server = Server->ai_next) {159 // If socket fails, maybe it's because the address family is not supported.160 // Skip to the next addrinfo structure.161 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)162 continue;163 164 // If connect works, we exit the loop with a working socket.165 if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)166 break;167 168 close(SockFD);169 }170 freeaddrinfo(AI);171 172 // Did we reach the end of the loop without connecting to a valid endpoint?173 if (Server == nullptr)174 return make_error<StringError>("invalid hostname",175 inconvertibleErrorCode());176 177 return SockFD;178}179 180Expected<std::unique_ptr<SimpleRemoteEPC>>181connectTCPSocket(StringRef NetworkAddress) {182 auto CreateErr = [NetworkAddress](StringRef Details) {183 return make_error<StringError>(184 formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,185 Details),186 inconvertibleErrorCode());187 };188 189 StringRef Host, PortStr;190 std::tie(Host, PortStr) = NetworkAddress.split(':');191 if (Host.empty())192 return CreateErr("host name cannot be empty");193 if (PortStr.empty())194 return CreateErr("port cannot be empty");195 int Port = 0;196 if (PortStr.getAsInteger(10, Port))197 return CreateErr("port number is not a valid integer");198 199 Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());200 if (!SockFD)201 return CreateErr(toString(SockFD.takeError()));202 203 return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(204 std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),205 SimpleRemoteEPC::Setup(), *SockFD);206}207 208#endif209