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