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/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS 15 #include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h" 16 #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" 17 #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" 18 #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h" 19 #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h" 20 #include "llvm/Support/Compiler.h" 21 #include "llvm/Support/Debug.h" 22 #include "llvm/Support/DynamicLibrary.h" 23 #include "llvm/Support/Error.h" 24 #include "llvm/Support/MathExtras.h" 25 #include "llvm/Support/raw_ostream.h" 26 #include <cstring> 27 #include <sstream> 28 29 #ifdef LLVM_ON_UNIX 30 31 #include <netdb.h> 32 #include <netinet/in.h> 33 #include <sys/socket.h> 34 35 #endif 36 37 using namespace llvm; 38 using namespace llvm::orc; 39 40 ExitOnError ExitOnErr; 41 42 LLVM_ATTRIBUTE_USED void linkComponents() { 43 errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper 44 << (void *)&llvm_orc_deregisterEHFrameSectionWrapper 45 << (void *)&llvm_orc_registerJITLoaderGDBWrapper 46 << (void *)&llvm_orc_registerJITLoaderGDBAllocAction; 47 } 48 49 void printErrorAndExit(Twine ErrMsg) { 50 #ifndef NDEBUG 51 const char *DebugOption = "[debug] "; 52 #else 53 const char *DebugOption = ""; 54 #endif 55 56 errs() << "error: " << ErrMsg.str() << "\n\n" 57 << "Usage:\n" 58 << " llvm-jitlink-executor " << DebugOption 59 << "[test-jitloadergdb] filedescs=<infd>,<outfd> [args...]\n" 60 << " llvm-jitlink-executor " << DebugOption 61 << "[test-jitloadergdb] listen=<host>:<port> [args...]\n"; 62 exit(1); 63 } 64 65 int openListener(std::string Host, std::string PortStr) { 66 #ifndef LLVM_ON_UNIX 67 // FIXME: Add TCP support for Windows. 68 printErrorAndExit("listen option not supported"); 69 return 0; 70 #else 71 addrinfo Hints{}; 72 Hints.ai_family = AF_INET; 73 Hints.ai_socktype = SOCK_STREAM; 74 Hints.ai_flags = AI_PASSIVE; 75 76 addrinfo *AI; 77 if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) { 78 errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n"; 79 exit(1); 80 } 81 82 // Create a socket from first addrinfo structure returned by getaddrinfo. 83 int SockFD; 84 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) { 85 errs() << "Error creating socket: " << std::strerror(errno) << "\n"; 86 exit(1); 87 } 88 89 // Avoid "Address already in use" errors. 90 const int Yes = 1; 91 if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { 92 errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n"; 93 exit(1); 94 } 95 96 // Bind the socket to the desired port. 97 if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) { 98 errs() << "Error on binding: " << std::strerror(errno) << "\n"; 99 exit(1); 100 } 101 102 // Listen for incomming connections. 103 static constexpr int ConnectionQueueLen = 1; 104 listen(SockFD, ConnectionQueueLen); 105 106 #if defined(_AIX) 107 assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX"); 108 socklen_t AddrLen = Lo_32(AI->ai_addrlen); 109 return accept(SockFD, AI->ai_addr, &AddrLen); 110 #else 111 return accept(SockFD, AI->ai_addr, &AI->ai_addrlen); 112 #endif 113 114 #endif // LLVM_ON_UNIX 115 } 116 117 #if LLVM_ENABLE_THREADS 118 119 // JITLink debug support plugins put information about JITed code in this GDB 120 // JIT Interface global from OrcTargetProcess. 121 extern "C" LLVM_ABI struct jit_descriptor __jit_debug_descriptor; 122 123 static void *findLastDebugDescriptorEntryPtr() { 124 struct jit_code_entry *Last = __jit_debug_descriptor.first_entry; 125 while (Last && Last->next_entry) 126 Last = Last->next_entry; 127 return Last; 128 } 129 130 #endif 131 132 int main(int argc, char *argv[]) { 133 #if LLVM_ENABLE_THREADS 134 135 ExitOnErr.setBanner(std::string(argv[0]) + ": "); 136 137 unsigned FirstProgramArg = 1; 138 int InFD = 0; 139 int OutFD = 0; 140 141 if (argc < 2) 142 printErrorAndExit("insufficient arguments"); 143 144 StringRef NextArg = argv[FirstProgramArg++]; 145 #ifndef NDEBUG 146 if (NextArg == "debug") { 147 DebugFlag = true; 148 NextArg = argv[FirstProgramArg++]; 149 } 150 #endif 151 152 std::vector<StringRef> TestOutputFlags; 153 while (NextArg.starts_with("test-")) { 154 TestOutputFlags.push_back(NextArg); 155 NextArg = argv[FirstProgramArg++]; 156 } 157 158 if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb")) 159 fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n", 160 pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr())); 161 162 StringRef SpecifierType, Specifier; 163 std::tie(SpecifierType, Specifier) = NextArg.split('='); 164 if (SpecifierType == "filedescs") { 165 StringRef FD1Str, FD2Str; 166 std::tie(FD1Str, FD2Str) = Specifier.split(','); 167 if (FD1Str.getAsInteger(10, InFD)) 168 printErrorAndExit(FD1Str + " is not a valid file descriptor"); 169 if (FD2Str.getAsInteger(10, OutFD)) 170 printErrorAndExit(FD2Str + " is not a valid file descriptor"); 171 } else if (SpecifierType == "listen") { 172 StringRef Host, PortStr; 173 std::tie(Host, PortStr) = Specifier.split(':'); 174 175 int Port = 0; 176 if (PortStr.getAsInteger(10, Port)) 177 printErrorAndExit("port number '" + PortStr + "' is not a valid integer"); 178 179 InFD = OutFD = openListener(Host.str(), PortStr.str()); 180 } else 181 printErrorAndExit("invalid specifier type \"" + SpecifierType + "\""); 182 183 auto Server = 184 ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>( 185 [](SimpleRemoteEPCServer::Setup &S) -> Error { 186 S.setDispatcher( 187 std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>()); 188 S.bootstrapSymbols() = 189 SimpleRemoteEPCServer::defaultBootstrapSymbols(); 190 S.services().push_back( 191 std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>()); 192 S.services().push_back( 193 std::make_unique< 194 rt_bootstrap::ExecutorSharedMemoryMapperService>()); 195 return Error::success(); 196 }, 197 InFD, OutFD)); 198 199 ExitOnErr(Server->waitForDisconnect()); 200 201 if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb")) 202 fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n", 203 pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr())); 204 205 return 0; 206 207 #else 208 errs() << argv[0] 209 << " error: this tool requires threads, but LLVM was " 210 "built with LLVM_ENABLE_THREADS=Off\n"; 211 return 1; 212 #endif 213 } 214