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 
11258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
12662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
13662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
14ac2daacbSStefan Gränitz #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
15258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
16258f055eSStefan Gränitz #include "llvm/Support/FileSystem.h"
17258f055eSStefan Gränitz #include "llvm/Support/Path.h"
18258f055eSStefan Gränitz 
19258f055eSStefan Gränitz #ifdef LLVM_ON_UNIX
20258f055eSStefan Gränitz #include <netdb.h>
21258f055eSStefan Gränitz #include <netinet/in.h>
22258f055eSStefan Gränitz #include <sys/socket.h>
23258f055eSStefan Gränitz #include <unistd.h>
24258f055eSStefan Gränitz #endif // LLVM_ON_UNIX
25258f055eSStefan Gränitz 
26258f055eSStefan Gränitz using namespace llvm;
27258f055eSStefan Gränitz using namespace llvm::orc;
28258f055eSStefan Gränitz 
addDebugSupport(ObjectLayer & ObjLayer)29ac2daacbSStefan Gränitz Error addDebugSupport(ObjectLayer &ObjLayer) {
30ac2daacbSStefan Gränitz   ExecutionSession &ES = ObjLayer.getExecutionSession();
31ac2daacbSStefan Gränitz   auto Registrar = createJITLoaderGDBRegistrar(ES);
32258f055eSStefan Gränitz   if (!Registrar)
33258f055eSStefan Gränitz     return Registrar.takeError();
34258f055eSStefan Gränitz 
35ac2daacbSStefan Gränitz   auto *ObjLinkingLayer = cast<ObjectLinkingLayer>(&ObjLayer);
36ac2daacbSStefan Gränitz   if (!ObjLinkingLayer)
37ac2daacbSStefan Gränitz     return createStringError(inconvertibleErrorCode(),
38ac2daacbSStefan Gränitz                              "No debug support for given object layer type");
39258f055eSStefan Gränitz 
40ac2daacbSStefan Gränitz   ObjLinkingLayer->addPlugin(
41ac2daacbSStefan Gränitz       std::make_unique<DebugObjectManagerPlugin>(ES, std::move(*Registrar)));
42258f055eSStefan Gränitz   return Error::success();
43258f055eSStefan Gränitz }
44258f055eSStefan Gränitz 
45258f055eSStefan Gränitz Expected<std::unique_ptr<DefinitionGenerator>>
loadDylib(ExecutionSession & ES,StringRef RemotePath)46ac2daacbSStefan Gränitz loadDylib(ExecutionSession &ES, StringRef RemotePath) {
47ac2daacbSStefan Gränitz   if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
48ac2daacbSStefan Gränitz     return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
49258f055eSStefan Gränitz   else
50258f055eSStefan Gränitz     return Handle.takeError();
51258f055eSStefan Gränitz }
52258f055eSStefan Gränitz 
findLocalExecutorHelper()53ac2daacbSStefan Gränitz static void findLocalExecutorHelper() {}
findLocalExecutor(const char * HostArgv0)54ac2daacbSStefan Gränitz std::string findLocalExecutor(const char *HostArgv0) {
55ac2daacbSStefan Gränitz   // This just needs to be some static symbol in the binary; C++ doesn't
56258f055eSStefan Gränitz   // allow taking the address of ::main however.
57ac2daacbSStefan Gränitz   uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
58ac2daacbSStefan Gränitz   void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
59ac2daacbSStefan Gränitz   SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
60258f055eSStefan Gränitz   sys::path::remove_filename(FullName);
61ac2daacbSStefan Gränitz   sys::path::append(FullName, "llvm-jitlink-executor");
62258f055eSStefan Gränitz   return FullName.str().str();
63258f055eSStefan Gränitz }
64258f055eSStefan Gränitz 
65258f055eSStefan Gränitz #ifndef LLVM_ON_UNIX
66258f055eSStefan Gränitz 
67258f055eSStefan Gränitz // FIXME: Add support for Windows.
68ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath)69ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) {
70258f055eSStefan Gränitz   return make_error<StringError>(
71258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
72258f055eSStefan Gränitz       inconvertibleErrorCode());
73258f055eSStefan Gränitz }
74258f055eSStefan Gränitz 
75258f055eSStefan Gränitz // FIXME: Add support for Windows.
76ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress)77ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) {
78258f055eSStefan Gränitz   return make_error<StringError>(
79258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
80258f055eSStefan Gränitz       inconvertibleErrorCode());
81258f055eSStefan Gränitz }
82258f055eSStefan Gränitz 
83258f055eSStefan Gränitz #else
84258f055eSStefan Gränitz 
85ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath)86ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) {
87258f055eSStefan Gränitz   constexpr int ReadEnd = 0;
88258f055eSStefan Gränitz   constexpr int WriteEnd = 1;
89258f055eSStefan Gränitz 
90ac2daacbSStefan Gränitz   if (!sys::fs::can_execute(ExecutablePath))
91ac2daacbSStefan Gränitz     return make_error<StringError>(
92ac2daacbSStefan Gränitz         formatv("Specified executor invalid: {0}", ExecutablePath),
93ac2daacbSStefan Gränitz         inconvertibleErrorCode());
94ac2daacbSStefan Gränitz 
95258f055eSStefan Gränitz   // Pipe FDs.
96258f055eSStefan Gränitz   int ToExecutor[2];
97258f055eSStefan Gränitz   int FromExecutor[2];
98258f055eSStefan Gränitz 
99258f055eSStefan Gränitz   // Create pipes to/from the executor..
100258f055eSStefan Gränitz   if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
101258f055eSStefan Gränitz     return make_error<StringError>("Unable to create pipe for executor",
102258f055eSStefan Gränitz                                    inconvertibleErrorCode());
103258f055eSStefan Gränitz 
104ac2daacbSStefan Gränitz   pid_t ProcessID = fork();
105258f055eSStefan Gränitz   if (ProcessID == 0) {
106258f055eSStefan Gränitz     // In the child...
107258f055eSStefan Gränitz 
108258f055eSStefan Gränitz     // Close the parent ends of the pipes
109258f055eSStefan Gränitz     close(ToExecutor[WriteEnd]);
110258f055eSStefan Gränitz     close(FromExecutor[ReadEnd]);
111258f055eSStefan Gränitz 
112258f055eSStefan Gränitz     // Execute the child process.
113258f055eSStefan Gränitz     std::unique_ptr<char[]> ExecPath, FDSpecifier;
114258f055eSStefan Gränitz     {
115258f055eSStefan Gränitz       ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
116258f055eSStefan Gränitz       strcpy(ExecPath.get(), ExecutablePath.data());
117258f055eSStefan Gränitz 
118258f055eSStefan Gränitz       std::string FDSpecifierStr("filedescs=");
119258f055eSStefan Gränitz       FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
120258f055eSStefan Gränitz       FDSpecifierStr += ',';
121258f055eSStefan Gränitz       FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
122258f055eSStefan Gränitz       FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
123258f055eSStefan Gränitz       strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
124258f055eSStefan Gränitz     }
125258f055eSStefan Gränitz 
126258f055eSStefan Gränitz     char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr};
127258f055eSStefan Gränitz     int RC = execvp(ExecPath.get(), Args);
128258f055eSStefan Gränitz     if (RC != 0)
129258f055eSStefan Gränitz       return make_error<StringError>(
130258f055eSStefan Gränitz           "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
131258f055eSStefan Gränitz           inconvertibleErrorCode());
132258f055eSStefan Gränitz 
133258f055eSStefan Gränitz     llvm_unreachable("Fork won't return in success case");
134258f055eSStefan Gränitz   }
135258f055eSStefan Gränitz   // else we're the parent...
136258f055eSStefan Gränitz 
137258f055eSStefan Gränitz   // Close the child ends of the pipes
138258f055eSStefan Gränitz   close(ToExecutor[ReadEnd]);
139258f055eSStefan Gränitz   close(FromExecutor[WriteEnd]);
140258f055eSStefan Gränitz 
141ac2daacbSStefan Gränitz   auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
1422e6c92c5SLang Hames       std::make_unique<DynamicThreadPoolTaskDispatcher>(),
143*abdb82b2SLang Hames       SimpleRemoteEPC::Setup(),
144ac2daacbSStefan Gränitz       FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
145ac2daacbSStefan Gränitz   if (!EPC)
146ac2daacbSStefan Gränitz     return EPC.takeError();
147258f055eSStefan Gränitz 
148ac2daacbSStefan Gränitz   return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
149258f055eSStefan Gränitz }
150258f055eSStefan Gränitz 
connectTCPSocketImpl(std::string Host,std::string PortStr)151258f055eSStefan Gränitz static Expected<int> connectTCPSocketImpl(std::string Host,
152258f055eSStefan Gränitz                                           std::string PortStr) {
153258f055eSStefan Gränitz   addrinfo *AI;
154258f055eSStefan Gränitz   addrinfo Hints{};
155258f055eSStefan Gränitz   Hints.ai_family = AF_INET;
156258f055eSStefan Gränitz   Hints.ai_socktype = SOCK_STREAM;
157258f055eSStefan Gränitz   Hints.ai_flags = AI_NUMERICSERV;
158258f055eSStefan Gränitz 
159258f055eSStefan Gränitz   if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
160258f055eSStefan Gränitz     return make_error<StringError>(
161258f055eSStefan Gränitz         formatv("address resolution failed ({0})", gai_strerror(EC)),
162258f055eSStefan Gränitz         inconvertibleErrorCode());
163258f055eSStefan Gränitz 
164258f055eSStefan Gränitz   // Cycle through the returned addrinfo structures and connect to the first
165258f055eSStefan Gränitz   // reachable endpoint.
166258f055eSStefan Gränitz   int SockFD;
167258f055eSStefan Gränitz   addrinfo *Server;
168258f055eSStefan Gränitz   for (Server = AI; Server != nullptr; Server = Server->ai_next) {
169258f055eSStefan Gränitz     // If socket fails, maybe it's because the address family is not supported.
170258f055eSStefan Gränitz     // Skip to the next addrinfo structure.
171258f055eSStefan Gränitz     if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
172258f055eSStefan Gränitz       continue;
173258f055eSStefan Gränitz 
174258f055eSStefan Gränitz     // If connect works, we exit the loop with a working socket.
175258f055eSStefan Gränitz     if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
176258f055eSStefan Gränitz       break;
177258f055eSStefan Gränitz 
178258f055eSStefan Gränitz     close(SockFD);
179258f055eSStefan Gränitz   }
180258f055eSStefan Gränitz   freeaddrinfo(AI);
181258f055eSStefan Gränitz 
182258f055eSStefan Gränitz   // Did we reach the end of the loop without connecting to a valid endpoint?
183258f055eSStefan Gränitz   if (Server == nullptr)
184258f055eSStefan Gränitz     return make_error<StringError>("invalid hostname",
185258f055eSStefan Gränitz                                    inconvertibleErrorCode());
186258f055eSStefan Gränitz 
187258f055eSStefan Gränitz   return SockFD;
188258f055eSStefan Gränitz }
189258f055eSStefan Gränitz 
190ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress)191ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) {
192258f055eSStefan Gränitz   auto CreateErr = [NetworkAddress](StringRef Details) {
193258f055eSStefan Gränitz     return make_error<StringError>(
194258f055eSStefan Gränitz         formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
195258f055eSStefan Gränitz                 Details),
196258f055eSStefan Gränitz         inconvertibleErrorCode());
197258f055eSStefan Gränitz   };
198258f055eSStefan Gränitz 
199258f055eSStefan Gränitz   StringRef Host, PortStr;
200258f055eSStefan Gränitz   std::tie(Host, PortStr) = NetworkAddress.split(':');
201258f055eSStefan Gränitz   if (Host.empty())
202258f055eSStefan Gränitz     return CreateErr("host name cannot be empty");
203258f055eSStefan Gränitz   if (PortStr.empty())
204258f055eSStefan Gränitz     return CreateErr("port cannot be empty");
205258f055eSStefan Gränitz   int Port = 0;
206258f055eSStefan Gränitz   if (PortStr.getAsInteger(10, Port))
207258f055eSStefan Gränitz     return CreateErr("port number is not a valid integer");
208258f055eSStefan Gränitz 
209258f055eSStefan Gränitz   Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
210258f055eSStefan Gränitz   if (!SockFD)
211258f055eSStefan Gränitz     return CreateErr(toString(SockFD.takeError()));
212258f055eSStefan Gränitz 
2132e6c92c5SLang Hames   return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
214*abdb82b2SLang Hames       std::make_unique<DynamicThreadPoolTaskDispatcher>(),
215*abdb82b2SLang Hames       SimpleRemoteEPC::Setup(), *SockFD);
216258f055eSStefan Gränitz }
217258f055eSStefan Gränitz 
218258f055eSStefan Gränitz #endif
219