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