1//===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===//
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// Simple out-of-process executor for llvm-jitlink.
10//
11//===----------------------------------------------------------------------===//
12
13#include "../ConnectionUtils.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS
16#include "llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h"
17#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
18#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
19#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
20#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
21#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
22#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
23#include "llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h"
24#include "llvm/Support/Compiler.h"
25#include "llvm/Support/Debug.h"
26#include "llvm/Support/DynamicLibrary.h"
27#include "llvm/Support/Error.h"
28#include "llvm/Support/MathExtras.h"
29#include "llvm/Support/raw_ostream.h"
30#include <cstring>
31#include <sstream>
32
33using namespace llvm;
34using namespace llvm::orc;
35
36ExitOnError ExitOnErr;
37
38LLVM_ATTRIBUTE_USED void linkComponents() {
39 errs() << (void *)&llvm_orc_registerEHFrameSectionAllocAction
40 << (void *)&llvm_orc_deregisterEHFrameSectionAllocAction
41 << (void *)&llvm_orc_registerJITLoaderGDBAllocAction;
42}
43
44void printErrorAndExit(Twine ErrMsg) {
45#ifndef NDEBUG
46 const char *DebugOption = "[debug] ";
47#else
48 const char *DebugOption = "";
49#endif
50
51 errs() << "error: " << ErrMsg.str() << "\n\n"
52 << "Usage:\n"
53 << " llvm-jitlink-executor " << DebugOption
54 << "[test-jitloadergdb] socket:adopt=<sockfd> [args...]\n"
55 << " llvm-jitlink-executor " << DebugOption
56 << "[test-jitloadergdb] tcp:connect=<host>:<port> [args...]\n"
57 << " llvm-jitlink-executor " << DebugOption
58 << "[test-jitloadergdb] tcp:listen=<host>:<port> [args...]\n";
59 exit(status: 1);
60}
61
62#if LLVM_ENABLE_THREADS
63
64// JITLink debug support plugins put information about JITed code in this GDB
65// JIT Interface global from OrcTargetProcess.
66extern "C" LLVM_ABI struct jit_descriptor __jit_debug_descriptor;
67
68static void *findLastDebugDescriptorEntryPtr() {
69 struct jit_code_entry *Last = __jit_debug_descriptor.first_entry;
70 while (Last && Last->next_entry)
71 Last = Last->next_entry;
72 return Last;
73}
74
75#endif
76
77Expected<std::unique_ptr<SimpleRemoteEPCServer>> createServerWithFD(int FD) {
78#if LLVM_ENABLE_THREADS
79 return SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
80 SetupFunction: [](SimpleRemoteEPCServer::Setup &S) -> Error {
81 S.setDispatcher(
82 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
83 S.bootstrapSymbols() = SimpleRemoteEPCServer::defaultBootstrapSymbols();
84 addDefaultBootstrapValuesForHostProcess(BootstrapMap&: S.bootstrapMap(),
85 BootstrapSymbols&: S.bootstrapSymbols());
86#ifdef __APPLE__
87 if (UnwindInfoManager::TryEnable())
88 UnwindInfoManager::addBootstrapSymbols(S.bootstrapSymbols());
89#endif // __APPLE__
90 S.services().push_back(
91 x: std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
92 S.services().push_back(
93 x: std::make_unique<
94 rt_bootstrap::ExecutorSharedMemoryMapperService>());
95 return Error::success();
96 },
97 TransportTCtorArgs&: FD, TransportTCtorArgs&: FD);
98#else
99 llvm_unreachable("Not available on LLVM_ENABLE_THREADS=Off builds");
100#endif // !LLVM_ENABLE_THREADS
101}
102
103Expected<std::unique_ptr<SimpleRemoteEPCServer>>
104connectWithSocket(const ConnectionSpec &CS) {
105 if (CS.getAction() != "adopt")
106 return make_error<StringError>(
107 Args: "In " + CS.str() +
108 ", the socket transport supports only the \"adopt\" action",
109 Args: inconvertibleErrorCode());
110
111 int FD;
112 if (CS.getDescriptor().getAsInteger(Radix: 10, Result&: FD))
113 return make_error<StringError>(
114 Args: "In " + CS.str() + ", " + CS.getDescriptor() + " is not an integer",
115 Args: inconvertibleErrorCode());
116
117 return createServerWithFD(FD);
118}
119
120Expected<std::unique_ptr<SimpleRemoteEPCServer>>
121connectWithTCPConnect(const ConnectionSpec &CS) {
122#ifndef LLVM_ON_UNIX
123 return make_error<StringError>("TCP connection not supported",
124 inconvertibleErrorCode());
125#else
126 auto [Host, PortStr] = CS.getDescriptor().split(Separator: ':');
127 if (Host.empty() || PortStr.empty())
128 return make_error<StringError>(Args: "In " + CS.str() +
129 ", expected <host>:<port>",
130 Args: inconvertibleErrorCode());
131
132 auto SockFD = connectTCPSocket(Host, PortStr);
133 if (!SockFD)
134 return make_error<StringError>(Args: "In " + CS.str() + ", " +
135 toString(E: SockFD.takeError()),
136 Args: inconvertibleErrorCode());
137
138 return createServerWithFD(FD: *SockFD);
139#endif // LLVM_ON_UNIX
140}
141
142Expected<std::unique_ptr<SimpleRemoteEPCServer>>
143connectWithTCPListen(const ConnectionSpec &CS) {
144#ifndef LLVM_ON_UNIX
145 return make_error<StringError>("TCP connection not supported",
146 inconvertibleErrorCode());
147#else
148 auto [Host, PortStr] = CS.getDescriptor().split(Separator: ':');
149
150 auto ListenFD = listenTCPSocket(Host, PortStr);
151 if (!ListenFD)
152 return make_error<StringError>(Args: "In " + CS.str() + ", " +
153 toString(E: ListenFD.takeError()),
154 Args: inconvertibleErrorCode());
155
156 auto SockFD = acceptTCPConnection(ListeningSockFD: *ListenFD);
157 if (!SockFD)
158 return make_error<StringError>(Args: "In " + CS.str() + ", " +
159 toString(E: SockFD.takeError()),
160 Args: inconvertibleErrorCode());
161
162 return createServerWithFD(FD: *SockFD);
163#endif // LLVM_ON_UNIX
164}
165
166Expected<std::unique_ptr<SimpleRemoteEPCServer>>
167connectWithTCP(const ConnectionSpec &CS) {
168 if (CS.getAction() == "connect")
169 return connectWithTCPConnect(CS);
170 if (CS.getAction() == "listen")
171 return connectWithTCPListen(CS);
172
173 return make_error<StringError>(Args: "In " + CS.str() + ", unrecognized action \"" +
174 CS.getAction() + "\"",
175 Args: inconvertibleErrorCode());
176}
177
178Expected<std::unique_ptr<SimpleRemoteEPCServer>>
179createServer(const ConnectionSpec &CS) {
180 if (CS.getTransport() == "socket")
181 return connectWithSocket(CS);
182 if (CS.getTransport() == "tcp")
183 return connectWithTCP(CS);
184
185 return make_error<StringError>(Args: "In " + CS.str() +
186 ", unrecognized transport \"" +
187 CS.getTransport() + "\"",
188 Args: inconvertibleErrorCode());
189}
190
191int main(int argc, char *argv[]) {
192#if LLVM_ENABLE_THREADS
193
194 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
195
196 unsigned FirstProgramArg = 1;
197
198 if (argc < 2)
199 printErrorAndExit(ErrMsg: "insufficient arguments");
200
201 StringRef NextArg = argv[FirstProgramArg++];
202#ifndef NDEBUG
203 if (NextArg == "debug") {
204 DebugFlag = true;
205 NextArg = argv[FirstProgramArg++];
206 }
207#endif
208
209 std::vector<StringRef> TestOutputFlags;
210 while (NextArg.starts_with(Prefix: "test-")) {
211 TestOutputFlags.push_back(x: NextArg);
212 NextArg = argv[FirstProgramArg++];
213 }
214
215 if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb"))
216 fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
217 pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr()));
218
219 auto ConnSpec = ExitOnErr(ConnectionSpec::parse(Spec: NextArg));
220 auto Server = ExitOnErr(createServer(CS: ConnSpec));
221
222 ExitOnErr(Server->waitForDisconnect());
223
224 if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb"))
225 fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
226 pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr()));
227
228 return 0;
229
230#else
231 errs() << argv[0]
232 << " error: this tool requires threads, but LLVM was "
233 "built with LLVM_ENABLE_THREADS=Off\n";
234 return 1;
235#endif
236}
237