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/RegisterEHFrames.h" 17 #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h" 18 #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.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 errs() << "error: " << ErrMsg.str() << "\n\n" 47 << "Usage:\n" 48 << " llvm-jitlink-executor filedescs=<infd>,<outfd> [args...]\n" 49 << " llvm-jitlink-executor listen=<host>:<port> [args...]\n"; 50 exit(1); 51 } 52 53 int openListener(std::string Host, std::string PortStr) { 54 #ifndef LLVM_ON_UNIX 55 // FIXME: Add TCP support for Windows. 56 printErrorAndExit("listen option not supported"); 57 return 0; 58 #else 59 addrinfo Hints{}; 60 Hints.ai_family = AF_INET; 61 Hints.ai_socktype = SOCK_STREAM; 62 Hints.ai_flags = AI_PASSIVE; 63 64 addrinfo *AI; 65 if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) { 66 errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n"; 67 exit(1); 68 } 69 70 // Create a socket from first addrinfo structure returned by getaddrinfo. 71 int SockFD; 72 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) { 73 errs() << "Error creating socket: " << std::strerror(errno) << "\n"; 74 exit(1); 75 } 76 77 // Avoid "Address already in use" errors. 78 const int Yes = 1; 79 if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { 80 errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n"; 81 exit(1); 82 } 83 84 // Bind the socket to the desired port. 85 if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) { 86 errs() << "Error on binding: " << std::strerror(errno) << "\n"; 87 exit(1); 88 } 89 90 // Listen for incomming connections. 91 static constexpr int ConnectionQueueLen = 1; 92 listen(SockFD, ConnectionQueueLen); 93 94 #if defined(_AIX) 95 assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX"); 96 socklen_t AddrLen = Lo_32(AI->ai_addrlen); 97 return accept(SockFD, AI->ai_addr, &AddrLen); 98 #else 99 return accept(SockFD, AI->ai_addr, &AI->ai_addrlen); 100 #endif 101 102 #endif // LLVM_ON_UNIX 103 } 104 105 int main(int argc, char *argv[]) { 106 #if LLVM_ENABLE_THREADS 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 } else 137 printErrorAndExit("invalid specifier type \"" + SpecifierType + "\""); 138 } 139 140 auto Server = 141 ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>( 142 [](SimpleRemoteEPCServer::Setup &S) -> Error { 143 S.setDispatcher( 144 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>()); 145 S.bootstrapSymbols() = 146 SimpleRemoteEPCServer::defaultBootstrapSymbols(); 147 S.services().push_back( 148 std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>()); 149 return Error::success(); 150 }, 151 InFD, OutFD)); 152 153 ExitOnErr(Server->waitForDisconnect()); 154 return 0; 155 156 #else 157 errs() << argv[0] 158 << " error: this tool requires threads, but LLVM was " 159 "built with LLVM_ENABLE_THREADS=Off\n"; 160 return 1; 161 #endif 162 } 163