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