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