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