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] fd=<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>>
104connectWithFD(const ConnectionSpec &CS) {
105 int FD;
106 if (CS.getDescriptor().getAsInteger(Radix: 10, Result&: FD))
107 return make_error<StringError>(
108 Args: "In " + CS.str() + ", " + CS.getDescriptor() + " is not an integer",
109 Args: inconvertibleErrorCode());
110
111 return createServerWithFD(FD);
112}
113
114Expected<std::unique_ptr<SimpleRemoteEPCServer>>
115connectWithTCPConnect(const ConnectionSpec &CS) {
116#ifndef LLVM_ON_UNIX
117 return make_error<StringError>("TCP connection not supported",
118 inconvertibleErrorCode());
119#else
120 auto [Host, PortStr] = CS.getDescriptor().split(Separator: ':');
121 if (Host.empty() || PortStr.empty())
122 return make_error<StringError>(Args: "In " + CS.str() +
123 ", expected <host>:<port>",
124 Args: inconvertibleErrorCode());
125
126 auto SockFD = connectTCPSocket(Host, PortStr);
127 if (!SockFD)
128 return make_error<StringError>(Args: "In " + CS.str() + ", " +
129 toString(E: SockFD.takeError()),
130 Args: inconvertibleErrorCode());
131
132 return createServerWithFD(FD: *SockFD);
133#endif // LLVM_ON_UNIX
134}
135
136Expected<std::unique_ptr<SimpleRemoteEPCServer>>
137connectWithTCPListen(const ConnectionSpec &CS) {
138#ifndef LLVM_ON_UNIX
139 return make_error<StringError>("TCP connection not supported",
140 inconvertibleErrorCode());
141#else
142 auto [Host, PortStr] = CS.getDescriptor().split(Separator: ':');
143
144 auto ListenFD = listenTCPSocket(Host, PortStr);
145 if (!ListenFD)
146 return make_error<StringError>(Args: "In " + CS.str() + ", " +
147 toString(E: ListenFD.takeError()),
148 Args: inconvertibleErrorCode());
149
150 auto SockFD = acceptTCPConnection(ListeningSockFD: *ListenFD);
151 if (!SockFD)
152 return make_error<StringError>(Args: "In " + CS.str() + ", " +
153 toString(E: SockFD.takeError()),
154 Args: inconvertibleErrorCode());
155
156 return createServerWithFD(FD: *SockFD);
157#endif // LLVM_ON_UNIX
158}
159
160Expected<std::unique_ptr<SimpleRemoteEPCServer>>
161connectWithTCP(const ConnectionSpec &CS) {
162 if (CS.getAction() == "connect")
163 return connectWithTCPConnect(CS);
164 if (CS.getAction() == "listen")
165 return connectWithTCPListen(CS);
166
167 return make_error<StringError>(Args: "In " + CS.str() + ", unrecognized action \"" +
168 CS.getAction() + "\"",
169 Args: inconvertibleErrorCode());
170}
171
172Expected<std::unique_ptr<SimpleRemoteEPCServer>>
173createServer(const ConnectionSpec &CS) {
174 if (CS.getTransport() == "fd")
175 return connectWithFD(CS);
176 if (CS.getTransport() == "tcp")
177 return connectWithTCP(CS);
178
179 return make_error<StringError>(Args: "In " + CS.str() +
180 ", unrecognized transport \"" +
181 CS.getTransport() + "\"",
182 Args: inconvertibleErrorCode());
183}
184
185int main(int argc, char *argv[]) {
186#if LLVM_ENABLE_THREADS
187
188 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
189
190 unsigned FirstProgramArg = 1;
191
192 if (argc < 2)
193 printErrorAndExit(ErrMsg: "insufficient arguments");
194
195 StringRef NextArg = argv[FirstProgramArg++];
196#ifndef NDEBUG
197 if (NextArg == "debug") {
198 DebugFlag = true;
199 NextArg = argv[FirstProgramArg++];
200 }
201#endif
202
203 std::vector<StringRef> TestOutputFlags;
204 while (NextArg.starts_with(Prefix: "test-")) {
205 TestOutputFlags.push_back(x: NextArg);
206 NextArg = argv[FirstProgramArg++];
207 }
208
209 if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb"))
210 fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
211 pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr()));
212
213 auto ConnSpec = ExitOnErr(ConnectionSpec::parse(Spec: NextArg));
214 auto Server = ExitOnErr(createServer(CS: ConnSpec));
215
216 ExitOnErr(Server->waitForDisconnect());
217
218 if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb"))
219 fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
220 pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr()));
221
222 return 0;
223
224#else
225 errs() << argv[0]
226 << " error: this tool requires threads, but LLVM was "
227 "built with LLVM_ENABLE_THREADS=Off\n";
228 return 1;
229#endif
230}
231