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