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