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