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
35 using namespace llvm;
36 using namespace llvm::orc;
37
38 ExitOnError ExitOnErr;
39
linkComponents()40 LLVM_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
printErrorAndExit(Twine ErrMsg)47 void 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 << "filedescs=<infd>,<outfd> [args...]\n"
58 << " llvm-jitlink-executor " << DebugOption
59 << "listen=<host>:<port> [args...]\n";
60 exit(1);
61 }
62
openListener(std::string Host,std::string PortStr)63 int 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(nullptr, PortStr.c_str(), &Hints, &AI)) {
76 errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n";
77 exit(1);
78 }
79
80 // Create a socket from first addrinfo structure returned by getaddrinfo.
81 int SockFD;
82 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
83 errs() << "Error creating socket: " << std::strerror(errno) << "\n";
84 exit(1);
85 }
86
87 // Avoid "Address already in use" errors.
88 const int Yes = 1;
89 if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
90 errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n";
91 exit(1);
92 }
93
94 // Bind the socket to the desired port.
95 if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) {
96 errs() << "Error on binding: " << std::strerror(errno) << "\n";
97 exit(1);
98 }
99
100 // Listen for incomming connections.
101 static constexpr int ConnectionQueueLen = 1;
102 listen(SockFD, 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(SockFD, AI->ai_addr, &AI->ai_addrlen);
110 #endif
111
112 #endif // LLVM_ON_UNIX
113 }
114
main(int argc,char * argv[])115 int main(int argc, char *argv[]) {
116 #if LLVM_ENABLE_THREADS
117
118 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
119
120 unsigned FirstProgramArg = 1;
121 int InFD = 0;
122 int OutFD = 0;
123
124 if (argc < 2)
125 printErrorAndExit("insufficient arguments");
126 else {
127
128 StringRef ConnectArg = argv[FirstProgramArg++];
129 #ifndef NDEBUG
130 if (ConnectArg == "debug") {
131 DebugFlag = true;
132 ConnectArg = argv[FirstProgramArg++];
133 }
134 #endif
135
136 StringRef SpecifierType, Specifier;
137 std::tie(SpecifierType, Specifier) = ConnectArg.split('=');
138 if (SpecifierType == "filedescs") {
139 StringRef FD1Str, FD2Str;
140 std::tie(FD1Str, FD2Str) = Specifier.split(',');
141 if (FD1Str.getAsInteger(10, InFD))
142 printErrorAndExit(FD1Str + " is not a valid file descriptor");
143 if (FD2Str.getAsInteger(10, OutFD))
144 printErrorAndExit(FD2Str + " is not a valid file descriptor");
145 } else if (SpecifierType == "listen") {
146 StringRef Host, PortStr;
147 std::tie(Host, PortStr) = Specifier.split(':');
148
149 int Port = 0;
150 if (PortStr.getAsInteger(10, Port))
151 printErrorAndExit("port number '" + PortStr +
152 "' is not a valid integer");
153
154 InFD = OutFD = openListener(Host.str(), PortStr.str());
155 } else
156 printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
157 }
158
159 auto Server =
160 ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
161 [](SimpleRemoteEPCServer::Setup &S) -> Error {
162 S.setDispatcher(
163 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
164 S.bootstrapSymbols() =
165 SimpleRemoteEPCServer::defaultBootstrapSymbols();
166 S.services().push_back(
167 std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
168 S.services().push_back(
169 std::make_unique<
170 rt_bootstrap::ExecutorSharedMemoryMapperService>());
171 return Error::success();
172 },
173 InFD, OutFD));
174
175 ExitOnErr(Server->waitForDisconnect());
176 return 0;
177
178 #else
179 errs() << argv[0]
180 << " error: this tool requires threads, but LLVM was "
181 "built with LLVM_ENABLE_THREADS=Off\n";
182 return 1;
183 #endif
184 }
185