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/ADT/StringExtras.h"
12#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
13#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
14#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
15#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
16#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/Path.h"
19
20#ifdef LLVM_ON_UNIX
21#include <netdb.h>
22#include <netinet/in.h>
23#include <sys/socket.h>
24#include <unistd.h>
25#endif // LLVM_ON_UNIX
26
27using namespace llvm;
28using namespace llvm::orc;
29
30Expected<std::unique_ptr<DefinitionGenerator>>
31loadDylib(ExecutionSession &ES, StringRef RemotePath) {
32 if (auto Handle = ES.getExecutorProcessControl().loadDylib(DylibPath: RemotePath.data()))
33 return std::make_unique<EPCDynamicLibrarySearchGenerator>(args&: ES, args&: *Handle);
34 else
35 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't
41 // 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(argv0: HostArgv0, MainExecAddr: VoidPtr));
45 sys::path::remove_filename(path&: FullName);
46 sys::path::append(path&: FullName, a: "llvm-jitlink-executor");
47 return FullName.str().str();
48}
49
50#ifndef LLVM_ON_UNIX
51
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#else
69
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(Path: ExecutablePath))
76 return make_error<StringError>(
77 Args: formatv(Fmt: "Specified executor invalid: {0}", Vals&: ExecutablePath),
78 Args: inconvertibleErrorCode());
79
80 // Pipe FDs.
81 int ToExecutor[2];
82 int FromExecutor[2];
83
84 // Create pipes to/from the executor..
85 if (pipe(pipedes: ToExecutor) != 0 || pipe(pipedes: FromExecutor) != 0)
86 return make_error<StringError>(Args: "Unable to create pipe for executor",
87 Args: inconvertibleErrorCode());
88
89 pid_t ProcessID = fork();
90 if (ProcessID == 0) {
91 // In the child...
92
93 // Close the parent ends of the pipes
94 close(fd: ToExecutor[WriteEnd]);
95 close(fd: FromExecutor[ReadEnd]);
96
97 // Execute the child process.
98 std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag;
99 {
100 ExecPath = std::make_unique<char[]>(num: ExecutablePath.size() + 1);
101 strcpy(dest: ExecPath.get(), src: ExecutablePath.data());
102
103 const char *TestOutputFlagStr = "test-jitloadergdb";
104 TestOutputFlag = std::make_unique<char[]>(num: strlen(s: TestOutputFlagStr) + 1);
105 strcpy(dest: TestOutputFlag.get(), src: TestOutputFlagStr);
106
107 std::string FDSpecifierStr("filedescs=");
108 FDSpecifierStr += utostr(X: ToExecutor[ReadEnd]);
109 FDSpecifierStr += ',';
110 FDSpecifierStr += utostr(X: FromExecutor[WriteEnd]);
111 FDSpecifier = std::make_unique<char[]>(num: FDSpecifierStr.size() + 1);
112 strcpy(dest: FDSpecifier.get(), src: FDSpecifierStr.c_str());
113 }
114
115 char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),
116 FDSpecifier.get(), nullptr};
117 int RC = execvp(file: ExecPath.get(), argv: Args);
118 if (RC != 0)
119 return make_error<StringError>(
120 Args: "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
121 Args: 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 pipes
128 close(fd: ToExecutor[ReadEnd]);
129 close(fd: FromExecutor[WriteEnd]);
130
131 auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
132 D: std::make_unique<DynamicThreadPoolTaskDispatcher>(),
133 S: SimpleRemoteEPC::Setup(),
134 TransportTCtorArgs&: FromExecutor[ReadEnd], TransportTCtorArgs&: ToExecutor[WriteEnd]);
135 if (!EPC)
136 return EPC.takeError();
137
138 return std::make_pair(x: std::move(*EPC), y: 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(name: Host.c_str(), service: PortStr.c_str(), req: &Hints, pai: &AI))
150 return make_error<StringError>(
151 Args: formatv(Fmt: "address resolution failed ({0})", Vals: gai_strerror(ecode: EC)),
152 Args: inconvertibleErrorCode());
153
154 // Cycle through the returned addrinfo structures and connect to the first
155 // 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(domain: AI->ai_family, type: AI->ai_socktype, protocol: AI->ai_protocol)) < 0)
162 continue;
163
164 // If connect works, we exit the loop with a working socket.
165 if (connect(fd: SockFD, addr: Server->ai_addr, len: Server->ai_addrlen) == 0)
166 break;
167
168 close(fd: SockFD);
169 }
170 freeaddrinfo(ai: 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>(Args: "invalid hostname",
175 Args: 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 Args: formatv(Fmt: "Failed to connect TCP socket '{0}': {1}", Vals: NetworkAddress,
185 Vals&: Details),
186 Args: inconvertibleErrorCode());
187 };
188
189 StringRef Host, PortStr;
190 std::tie(args&: Host, args&: PortStr) = NetworkAddress.split(Separator: ':');
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(Radix: 10, Result&: Port))
197 return CreateErr("port number is not a valid integer");
198
199 Expected<int> SockFD = connectTCPSocketImpl(Host: Host.str(), PortStr: PortStr.str());
200 if (!SockFD)
201 return CreateErr(toString(E: SockFD.takeError()));
202
203 return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
204 D: std::make_unique<DynamicThreadPoolTaskDispatcher>(),
205 S: SimpleRemoteEPC::Setup(), TransportTCtorArgs&: *SockFD);
206}
207
208#endif
209

source code of llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp