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