xref: /llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp (revision 40236257ea9d1e451daee5c0938bd23abd5d1450)
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 
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 
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 
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 
115 // JITLink debug support plugins put information about JITed code in this GDB
116 // JIT Interface global from OrcTargetProcess.
117 extern "C" struct jit_descriptor __jit_debug_descriptor;
118 
119 static void *findLastDebugDescriptorEntryPtr() {
120   struct jit_code_entry *Last = __jit_debug_descriptor.first_entry;
121   while (Last && Last->next_entry)
122     Last = Last->next_entry;
123   return Last;
124 }
125 
126 int main(int argc, char *argv[]) {
127 #if LLVM_ENABLE_THREADS
128 
129   ExitOnErr.setBanner(std::string(argv[0]) + ": ");
130 
131   unsigned FirstProgramArg = 1;
132   int InFD = 0;
133   int OutFD = 0;
134 
135   std::vector<StringRef> TestOutputFlags;
136 
137   if (argc < 2)
138     printErrorAndExit("insufficient arguments");
139   else {
140     StringRef ConnectArg = argv[FirstProgramArg++];
141 #ifndef NDEBUG
142     if (ConnectArg == "debug") {
143       DebugFlag = true;
144       ConnectArg = argv[FirstProgramArg++];
145     }
146 #endif
147 
148     while (ConnectArg.starts_with("test-")) {
149       TestOutputFlags.push_back(ConnectArg);
150       ConnectArg = argv[FirstProgramArg++];
151     }
152 
153     StringRef SpecifierType, Specifier;
154     std::tie(SpecifierType, Specifier) = ConnectArg.split('=');
155     if (SpecifierType == "filedescs") {
156       StringRef FD1Str, FD2Str;
157       std::tie(FD1Str, FD2Str) = Specifier.split(',');
158       if (FD1Str.getAsInteger(10, InFD))
159         printErrorAndExit(FD1Str + " is not a valid file descriptor");
160       if (FD2Str.getAsInteger(10, OutFD))
161         printErrorAndExit(FD2Str + " is not a valid file descriptor");
162     } else if (SpecifierType == "listen") {
163       StringRef Host, PortStr;
164       std::tie(Host, PortStr) = Specifier.split(':');
165 
166       int Port = 0;
167       if (PortStr.getAsInteger(10, Port))
168         printErrorAndExit("port number '" + PortStr +
169                           "' is not a valid integer");
170 
171       InFD = OutFD = openListener(Host.str(), PortStr.str());
172     } else
173       printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
174   }
175 
176   if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb"))
177     fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
178             pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr()));
179 
180   auto Server =
181       ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
182           [](SimpleRemoteEPCServer::Setup &S) -> Error {
183             S.setDispatcher(
184                 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
185             S.bootstrapSymbols() =
186                 SimpleRemoteEPCServer::defaultBootstrapSymbols();
187             S.services().push_back(
188                 std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
189             S.services().push_back(
190                 std::make_unique<
191                     rt_bootstrap::ExecutorSharedMemoryMapperService>());
192             return Error::success();
193           },
194           InFD, OutFD));
195 
196   ExitOnErr(Server->waitForDisconnect());
197 
198   if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb"))
199     fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n",
200             pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr()));
201 
202   return 0;
203 
204 #else
205   errs() << argv[0]
206          << " error: this tool requires threads, but LLVM was "
207             "built with LLVM_ENABLE_THREADS=Off\n";
208   return 1;
209 #endif
210 }
211