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