11d0676b5SLang Hames //===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===// 21d0676b5SLang Hames // 31d0676b5SLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41d0676b5SLang Hames // See https://llvm.org/LICENSE.txt for license information. 51d0676b5SLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61d0676b5SLang Hames // 71d0676b5SLang Hames //===----------------------------------------------------------------------===// 81d0676b5SLang Hames // 91d0676b5SLang Hames // Simple out-of-process executor for llvm-jitlink. 101d0676b5SLang Hames // 111d0676b5SLang Hames //===----------------------------------------------------------------------===// 121d0676b5SLang Hames 131d0676b5SLang Hames #include "llvm/ADT/StringRef.h" 1489e6a288SDaniil Fukalov #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS 15f1aa49adSAnubhab Ghosh #include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h" 16ef238923SStefan Gränitz #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" 171d0676b5SLang Hames #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" 1878b083dbSLang Hames #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h" 19bb72f073SLang Hames #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h" 20*c87d198cSThomas Fransham #include "llvm/Support/Compiler.h" 21175c1a39SLang Hames #include "llvm/Support/Debug.h" 221d0676b5SLang Hames #include "llvm/Support/DynamicLibrary.h" 231d0676b5SLang Hames #include "llvm/Support/Error.h" 24f21cc55fSStefan Gränitz #include "llvm/Support/MathExtras.h" 251d0676b5SLang Hames #include "llvm/Support/raw_ostream.h" 26f21cc55fSStefan Gränitz #include <cstring> 271d0676b5SLang Hames #include <sstream> 281d0676b5SLang Hames 291d0676b5SLang Hames #ifdef LLVM_ON_UNIX 301d0676b5SLang Hames 31f21cc55fSStefan Gränitz #include <netdb.h> 321d0676b5SLang Hames #include <netinet/in.h> 331d0676b5SLang Hames #include <sys/socket.h> 341d0676b5SLang Hames 351d0676b5SLang Hames #endif 361d0676b5SLang Hames 371d0676b5SLang Hames using namespace llvm; 381d0676b5SLang Hames using namespace llvm::orc; 391d0676b5SLang Hames 401d0676b5SLang Hames ExitOnError ExitOnErr; 411d0676b5SLang Hames 421d0676b5SLang Hames LLVM_ATTRIBUTE_USED void linkComponents() { 431d0676b5SLang Hames errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper 44ef238923SStefan Gränitz << (void *)&llvm_orc_deregisterEHFrameSectionWrapper 45b5f76d83SLang Hames << (void *)&llvm_orc_registerJITLoaderGDBWrapper 46b5f76d83SLang Hames << (void *)&llvm_orc_registerJITLoaderGDBAllocAction; 471d0676b5SLang Hames } 481d0676b5SLang Hames 491d0676b5SLang Hames void printErrorAndExit(Twine ErrMsg) { 50175c1a39SLang Hames #ifndef NDEBUG 51175c1a39SLang Hames const char *DebugOption = "[debug] "; 52175c1a39SLang Hames #else 53175c1a39SLang Hames const char *DebugOption = ""; 54175c1a39SLang Hames #endif 55175c1a39SLang Hames 561d0676b5SLang Hames errs() << "error: " << ErrMsg.str() << "\n\n" 571d0676b5SLang Hames << "Usage:\n" 58175c1a39SLang Hames << " llvm-jitlink-executor " << DebugOption 59a93c17c9SStefan Gränitz << "[test-jitloadergdb] filedescs=<infd>,<outfd> [args...]\n" 60175c1a39SLang Hames << " llvm-jitlink-executor " << DebugOption 61a93c17c9SStefan Gränitz << "[test-jitloadergdb] listen=<host>:<port> [args...]\n"; 621d0676b5SLang Hames exit(1); 631d0676b5SLang Hames } 641d0676b5SLang Hames 65f21cc55fSStefan Gränitz int openListener(std::string Host, std::string PortStr) { 661d0676b5SLang Hames #ifndef LLVM_ON_UNIX 671d0676b5SLang Hames // FIXME: Add TCP support for Windows. 681d0676b5SLang Hames printErrorAndExit("listen option not supported"); 69f2980e88SLang Hames return 0; 701d0676b5SLang Hames #else 71f21cc55fSStefan Gränitz addrinfo Hints{}; 72f21cc55fSStefan Gränitz Hints.ai_family = AF_INET; 73f21cc55fSStefan Gränitz Hints.ai_socktype = SOCK_STREAM; 74f21cc55fSStefan Gränitz Hints.ai_flags = AI_PASSIVE; 751d0676b5SLang Hames 76f21cc55fSStefan Gränitz addrinfo *AI; 77f21cc55fSStefan Gränitz if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) { 78f21cc55fSStefan Gränitz errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n"; 79f21cc55fSStefan Gränitz exit(1); 80f21cc55fSStefan Gränitz } 81f21cc55fSStefan Gränitz 82f21cc55fSStefan Gränitz // Create a socket from first addrinfo structure returned by getaddrinfo. 83f21cc55fSStefan Gränitz int SockFD; 84f21cc55fSStefan Gränitz if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) { 85f21cc55fSStefan Gränitz errs() << "Error creating socket: " << std::strerror(errno) << "\n"; 86f21cc55fSStefan Gränitz exit(1); 87f21cc55fSStefan Gränitz } 88f21cc55fSStefan Gränitz 89f21cc55fSStefan Gränitz // Avoid "Address already in use" errors. 90f21cc55fSStefan Gränitz const int Yes = 1; 911d0676b5SLang Hames if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { 92f21cc55fSStefan Gränitz errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n"; 931d0676b5SLang Hames exit(1); 941d0676b5SLang Hames } 951d0676b5SLang Hames 96f21cc55fSStefan Gränitz // Bind the socket to the desired port. 97f21cc55fSStefan Gränitz if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) { 98f21cc55fSStefan Gränitz errs() << "Error on binding: " << std::strerror(errno) << "\n"; 99f21cc55fSStefan Gränitz exit(1); 100f21cc55fSStefan Gränitz } 101f21cc55fSStefan Gränitz 102f21cc55fSStefan Gränitz // Listen for incomming connections. 103f21cc55fSStefan Gränitz static constexpr int ConnectionQueueLen = 1; 104f21cc55fSStefan Gränitz listen(SockFD, ConnectionQueueLen); 105f21cc55fSStefan Gränitz 106f21cc55fSStefan Gränitz #if defined(_AIX) 107f21cc55fSStefan Gränitz assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX"); 108f21cc55fSStefan Gränitz socklen_t AddrLen = Lo_32(AI->ai_addrlen); 109f21cc55fSStefan Gränitz return accept(SockFD, AI->ai_addr, &AddrLen); 110f21cc55fSStefan Gränitz #else 111f21cc55fSStefan Gränitz return accept(SockFD, AI->ai_addr, &AI->ai_addrlen); 1121d0676b5SLang Hames #endif 113f21cc55fSStefan Gränitz 114f21cc55fSStefan Gränitz #endif // LLVM_ON_UNIX 1151d0676b5SLang Hames } 1161d0676b5SLang Hames 117626d0fa3SStefan Gränitz #if LLVM_ENABLE_THREADS 118626d0fa3SStefan Gränitz 11940236257SStefan Gränitz // JITLink debug support plugins put information about JITed code in this GDB 12040236257SStefan Gränitz // JIT Interface global from OrcTargetProcess. 121*c87d198cSThomas Fransham extern "C" LLVM_ABI struct jit_descriptor __jit_debug_descriptor; 122b7d5b0d0SStefan Gränitz 123b7d5b0d0SStefan Gränitz static void *findLastDebugDescriptorEntryPtr() { 124b7d5b0d0SStefan Gränitz struct jit_code_entry *Last = __jit_debug_descriptor.first_entry; 125b7d5b0d0SStefan Gränitz while (Last && Last->next_entry) 126b7d5b0d0SStefan Gränitz Last = Last->next_entry; 127b7d5b0d0SStefan Gränitz return Last; 128b7d5b0d0SStefan Gränitz } 129b7d5b0d0SStefan Gränitz 130626d0fa3SStefan Gränitz #endif 131626d0fa3SStefan Gränitz 1321d0676b5SLang Hames int main(int argc, char *argv[]) { 133bb72f073SLang Hames #if LLVM_ENABLE_THREADS 1341d0676b5SLang Hames 1351d0676b5SLang Hames ExitOnErr.setBanner(std::string(argv[0]) + ": "); 1361d0676b5SLang Hames 137175c1a39SLang Hames unsigned FirstProgramArg = 1; 1381d0676b5SLang Hames int InFD = 0; 1391d0676b5SLang Hames int OutFD = 0; 1401d0676b5SLang Hames 1411d0676b5SLang Hames if (argc < 2) 1421d0676b5SLang Hames printErrorAndExit("insufficient arguments"); 143a93c17c9SStefan Gränitz 144a93c17c9SStefan Gränitz StringRef NextArg = argv[FirstProgramArg++]; 145175c1a39SLang Hames #ifndef NDEBUG 146a93c17c9SStefan Gränitz if (NextArg == "debug") { 147175c1a39SLang Hames DebugFlag = true; 148a93c17c9SStefan Gränitz NextArg = argv[FirstProgramArg++]; 149175c1a39SLang Hames } 150175c1a39SLang Hames #endif 151175c1a39SLang Hames 152a93c17c9SStefan Gränitz std::vector<StringRef> TestOutputFlags; 153a93c17c9SStefan Gränitz while (NextArg.starts_with("test-")) { 154a93c17c9SStefan Gränitz TestOutputFlags.push_back(NextArg); 155a93c17c9SStefan Gränitz NextArg = argv[FirstProgramArg++]; 156b7d5b0d0SStefan Gränitz } 157b7d5b0d0SStefan Gränitz 158a93c17c9SStefan Gränitz if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb")) 159a93c17c9SStefan Gränitz fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n", 160a93c17c9SStefan Gränitz pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr())); 161a93c17c9SStefan Gränitz 1621d0676b5SLang Hames StringRef SpecifierType, Specifier; 163a93c17c9SStefan Gränitz std::tie(SpecifierType, Specifier) = NextArg.split('='); 1641d0676b5SLang Hames if (SpecifierType == "filedescs") { 1651d0676b5SLang Hames StringRef FD1Str, FD2Str; 1661d0676b5SLang Hames std::tie(FD1Str, FD2Str) = Specifier.split(','); 1671d0676b5SLang Hames if (FD1Str.getAsInteger(10, InFD)) 1681d0676b5SLang Hames printErrorAndExit(FD1Str + " is not a valid file descriptor"); 1691d0676b5SLang Hames if (FD2Str.getAsInteger(10, OutFD)) 1701d0676b5SLang Hames printErrorAndExit(FD2Str + " is not a valid file descriptor"); 1711d0676b5SLang Hames } else if (SpecifierType == "listen") { 1721d0676b5SLang Hames StringRef Host, PortStr; 1731d0676b5SLang Hames std::tie(Host, PortStr) = Specifier.split(':'); 1741d0676b5SLang Hames 1751d0676b5SLang Hames int Port = 0; 1761d0676b5SLang Hames if (PortStr.getAsInteger(10, Port)) 177a93c17c9SStefan Gränitz printErrorAndExit("port number '" + PortStr + "' is not a valid integer"); 1781d0676b5SLang Hames 179f21cc55fSStefan Gränitz InFD = OutFD = openListener(Host.str(), PortStr.str()); 1801d0676b5SLang Hames } else 1811d0676b5SLang Hames printErrorAndExit("invalid specifier type \"" + SpecifierType + "\""); 182b7d5b0d0SStefan Gränitz 183bb72f073SLang Hames auto Server = 184bb72f073SLang Hames ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>( 18578b083dbSLang Hames [](SimpleRemoteEPCServer::Setup &S) -> Error { 18678b083dbSLang Hames S.setDispatcher( 1878fe3d9dfSLang Hames std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>()); 18878b083dbSLang Hames S.bootstrapSymbols() = 18978b083dbSLang Hames SimpleRemoteEPCServer::defaultBootstrapSymbols(); 19078b083dbSLang Hames S.services().push_back( 19178b083dbSLang Hames std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>()); 192f1aa49adSAnubhab Ghosh S.services().push_back( 193f1aa49adSAnubhab Ghosh std::make_unique< 194f1aa49adSAnubhab Ghosh rt_bootstrap::ExecutorSharedMemoryMapperService>()); 19578b083dbSLang Hames return Error::success(); 19678b083dbSLang Hames }, 19778b083dbSLang Hames InFD, OutFD)); 1981d0676b5SLang Hames 199bb72f073SLang Hames ExitOnErr(Server->waitForDisconnect()); 200b7d5b0d0SStefan Gränitz 201b7d5b0d0SStefan Gränitz if (llvm::is_contained(TestOutputFlags, "test-jitloadergdb")) 202b7d5b0d0SStefan Gränitz fprintf(stderr, "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n", 203b7d5b0d0SStefan Gränitz pointerToJITTargetAddress(findLastDebugDescriptorEntryPtr())); 204b7d5b0d0SStefan Gränitz 2051d0676b5SLang Hames return 0; 206bb72f073SLang Hames 207bb72f073SLang Hames #else 208bb72f073SLang Hames errs() << argv[0] 209bb72f073SLang Hames << " error: this tool requires threads, but LLVM was " 210bb72f073SLang Hames "built with LLVM_ENABLE_THREADS=Off\n"; 211bb72f073SLang Hames return 1; 212bb72f073SLang Hames #endif 2131d0676b5SLang Hames } 214