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