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