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 "llvm/ADT/StringRef.h"
14#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS
15#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
16#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
17#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
18#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
19#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
20#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
21#include "llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h"
22#include "llvm/Support/Compiler.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/DynamicLibrary.h"
25#include "llvm/Support/Error.h"
26#include "llvm/Support/MathExtras.h"
27#include "llvm/Support/raw_ostream.h"
28#include <cstring>
29#include <sstream>
30
31#ifdef LLVM_ON_UNIX
32
33#include <netdb.h>
34#include <netinet/in.h>
35#include <sys/socket.h>
36
37#endif
38
39using namespace llvm;
40using namespace llvm::orc;
41
42ExitOnError ExitOnErr;
43
44LLVM_ATTRIBUTE_USED void linkComponents() {
45 errs() << (void *)&llvm_orc_registerEHFrameSectionAllocAction
46 << (void *)&llvm_orc_deregisterEHFrameSectionAllocAction
47 << (void *)&llvm_orc_registerJITLoaderGDBWrapper
48 << (void *)&llvm_orc_registerJITLoaderGDBAllocAction;
49}
50
51void printErrorAndExit(Twine ErrMsg) {
52#ifndef NDEBUG
53 const char *DebugOption = "[debug] ";
54#else
55 const char *DebugOption = "";
56#endif
57
58 errs() << "error: " << ErrMsg.str() << "\n\n"
59 << "Usage:\n"
60 << " llvm-jitlink-executor " << DebugOption
61 << "[test-jitloadergdb] filedescs=<infd>,<outfd> [args...]\n"
62 << " llvm-jitlink-executor " << DebugOption
63 << "[test-jitloadergdb] listen=<host>:<port> [args...]\n";
64 exit(status: 1);
65}
66
67int openListener(std::string Host, std::string PortStr) {
68#ifndef LLVM_ON_UNIX
69 // FIXME: Add TCP support for Windows.
70 printErrorAndExit("listen option not supported");
71 return 0;
72#else
73 addrinfo Hints{};
74 Hints.ai_family = AF_INET;
75 Hints.ai_socktype = SOCK_STREAM;
76 Hints.ai_flags = AI_PASSIVE;
77
78 addrinfo *AI;
79 if (int EC = getaddrinfo(name: nullptr, service: PortStr.c_str(), req: &Hints, pai: &AI)) {
80 errs() << "Error setting up bind address: " << gai_strerror(ecode: EC) << "\n";
81 exit(status: 1);
82 }
83
84 // Create a socket from first addrinfo structure returned by getaddrinfo.
85 int SockFD;
86 if ((SockFD = socket(domain: AI->ai_family, type: AI->ai_socktype, protocol: AI->ai_protocol)) < 0) {
87 errs() << "Error creating socket: " << std::strerror(errno) << "\n";
88 exit(status: 1);
89 }
90
91 // Avoid "Address already in use" errors.
92 const int Yes = 1;
93 if (setsockopt(fd: SockFD, SOL_SOCKET, SO_REUSEADDR, optval: &Yes, optlen: sizeof(int)) == -1) {
94 errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n";
95 exit(status: 1);
96 }
97
98 // Bind the socket to the desired port.
99 if (bind(fd: SockFD, addr: AI->ai_addr, len: AI->ai_addrlen) < 0) {
100 errs() << "Error on binding: " << std::strerror(errno) << "\n";
101 exit(status: 1);
102 }
103
104 // Listen for incomming connections.
105 static constexpr int ConnectionQueueLen = 1;
106 listen(fd: SockFD, n: ConnectionQueueLen);
107
108#if defined(_AIX)
109 assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX");
110 socklen_t AddrLen = Lo_32(AI->ai_addrlen);
111 return accept(SockFD, AI->ai_addr, &AddrLen);
112#else
113 return accept(fd: SockFD, addr: AI->ai_addr, addr_len: &AI->ai_addrlen);
114#endif
115
116#endif // LLVM_ON_UNIX
117}
118
119#if LLVM_ENABLE_THREADS
120
121// JITLink debug support plugins put information about JITed code in this GDB
122// JIT Interface global from OrcTargetProcess.
123extern "C" LLVM_ABI struct jit_descriptor __jit_debug_descriptor;
124
125static void *findLastDebugDescriptorEntryPtr() {
126 struct jit_code_entry *Last = __jit_debug_descriptor.first_entry;
127 while (Last && Last->next_entry)
128 Last = Last->next_entry;
129 return Last;
130}
131
132#endif
133
134int main(int argc, char *argv[]) {
135#if LLVM_ENABLE_THREADS
136
137 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
138
139 unsigned FirstProgramArg = 1;
140 int InFD = 0;
141 int OutFD = 0;
142
143 if (argc < 2)
144 printErrorAndExit(ErrMsg: "insufficient arguments");
145
146 StringRef NextArg = argv[FirstProgramArg++];
147#ifndef NDEBUG
148 if (NextArg == "debug") {
149 DebugFlag = true;
150 NextArg = argv[FirstProgramArg++];
151 }
152#endif
153
154 std::vector<StringRef> TestOutputFlags;
155 while (NextArg.starts_with(Prefix: "test-")) {
156 TestOutputFlags.push_back(x: NextArg);
157 NextArg = argv[FirstProgramArg++];
158 }
159
160 if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb"))
161 fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
162 pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr()));
163
164 StringRef SpecifierType, Specifier;
165 std::tie(args&: SpecifierType, args&: Specifier) = NextArg.split(Separator: '=');
166 if (SpecifierType == "filedescs") {
167 StringRef FD1Str, FD2Str;
168 std::tie(args&: FD1Str, args&: FD2Str) = Specifier.split(Separator: ',');
169 if (FD1Str.getAsInteger(Radix: 10, Result&: InFD))
170 printErrorAndExit(ErrMsg: FD1Str + " is not a valid file descriptor");
171 if (FD2Str.getAsInteger(Radix: 10, Result&: OutFD))
172 printErrorAndExit(ErrMsg: FD2Str + " is not a valid file descriptor");
173 } else if (SpecifierType == "listen") {
174 StringRef Host, PortStr;
175 std::tie(args&: Host, args&: PortStr) = Specifier.split(Separator: ':');
176
177 int Port = 0;
178 if (PortStr.getAsInteger(Radix: 10, Result&: Port))
179 printErrorAndExit(ErrMsg: "port number '" + PortStr + "' is not a valid integer");
180
181 InFD = OutFD = openListener(Host: Host.str(), PortStr: PortStr.str());
182 } else
183 printErrorAndExit(ErrMsg: "invalid specifier type \"" + SpecifierType + "\"");
184
185 auto Server =
186 ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
187 SetupFunction: [](SimpleRemoteEPCServer::Setup &S) -> Error {
188 S.setDispatcher(
189 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
190 S.bootstrapSymbols() =
191 SimpleRemoteEPCServer::defaultBootstrapSymbols();
192 addDefaultBootstrapValuesForHostProcess(BootstrapMap&: S.bootstrapMap(),
193 BootstrapSymbols&: S.bootstrapSymbols());
194#ifdef __APPLE__
195 if (UnwindInfoManager::TryEnable())
196 UnwindInfoManager::addBootstrapSymbols(S.bootstrapSymbols());
197#endif // __APPLE__
198 S.services().push_back(
199 x: std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
200 S.services().push_back(
201 x: std::make_unique<
202 rt_bootstrap::ExecutorSharedMemoryMapperService>());
203 return Error::success();
204 },
205 TransportTCtorArgs&: InFD, TransportTCtorArgs&: OutFD));
206
207 ExitOnErr(Server->waitForDisconnect());
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 return 0;
214
215#else
216 errs() << argv[0]
217 << " error: this tool requires threads, but LLVM was "
218 "built with LLVM_ENABLE_THREADS=Off\n";
219 return 1;
220#endif
221}
222