blob: 7100c274b988b6907d898c56f353d719b0cca750 [file] [log] [blame]
//===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Simple out-of-process executor for llvm-jitlink.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <cstring>
#include <sstream>
#ifdef LLVM_ON_UNIX
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif
using namespace llvm;
using namespace llvm::orc;
ExitOnError ExitOnErr;
LLVM_ATTRIBUTE_USED void linkComponents() {
errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper;
}
void printErrorAndExit(Twine ErrMsg) {
#ifndef NDEBUG
const char *DebugOption = "[debug] ";
#else
const char *DebugOption = "";
#endif
errs() << "error: " << ErrMsg.str() << "\n\n"
<< "Usage:\n"
<< " llvm-jitlink-executor " << DebugOption
<< "filedescs=<infd>,<outfd> [args...]\n"
<< " llvm-jitlink-executor " << DebugOption
<< "listen=<host>:<port> [args...]\n";
exit(1);
}
int openListener(std::string Host, std::string PortStr) {
#ifndef LLVM_ON_UNIX
// FIXME: Add TCP support for Windows.
printErrorAndExit("listen option not supported");
return 0;
#else
addrinfo Hints{};
Hints.ai_family = AF_INET;
Hints.ai_socktype = SOCK_STREAM;
Hints.ai_flags = AI_PASSIVE;
addrinfo *AI;
if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) {
errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n";
exit(1);
}
// Create a socket from first addrinfo structure returned by getaddrinfo.
int SockFD;
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
errs() << "Error creating socket: " << std::strerror(errno) << "\n";
exit(1);
}
// Avoid "Address already in use" errors.
const int Yes = 1;
if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n";
exit(1);
}
// Bind the socket to the desired port.
if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) {
errs() << "Error on binding: " << std::strerror(errno) << "\n";
exit(1);
}
// Listen for incomming connections.
static constexpr int ConnectionQueueLen = 1;
listen(SockFD, ConnectionQueueLen);
#if defined(_AIX)
assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX");
socklen_t AddrLen = Lo_32(AI->ai_addrlen);
return accept(SockFD, AI->ai_addr, &AddrLen);
#else
return accept(SockFD, AI->ai_addr, &AI->ai_addrlen);
#endif
#endif // LLVM_ON_UNIX
}
int main(int argc, char *argv[]) {
#if LLVM_ENABLE_THREADS
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
unsigned FirstProgramArg = 1;
int InFD = 0;
int OutFD = 0;
if (argc < 2)
printErrorAndExit("insufficient arguments");
else {
StringRef ConnectArg = argv[FirstProgramArg++];
#ifndef NDEBUG
if (ConnectArg == "debug") {
DebugFlag = true;
ConnectArg = argv[FirstProgramArg++];
}
#endif
StringRef SpecifierType, Specifier;
std::tie(SpecifierType, Specifier) = ConnectArg.split('=');
if (SpecifierType == "filedescs") {
StringRef FD1Str, FD2Str;
std::tie(FD1Str, FD2Str) = Specifier.split(',');
if (FD1Str.getAsInteger(10, InFD))
printErrorAndExit(FD1Str + " is not a valid file descriptor");
if (FD2Str.getAsInteger(10, OutFD))
printErrorAndExit(FD2Str + " is not a valid file descriptor");
} else if (SpecifierType == "listen") {
StringRef Host, PortStr;
std::tie(Host, PortStr) = Specifier.split(':');
int Port = 0;
if (PortStr.getAsInteger(10, Port))
printErrorAndExit("port number '" + PortStr +
"' is not a valid integer");
InFD = OutFD = openListener(Host.str(), PortStr.str());
} else
printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
}
auto Server =
ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
[](SimpleRemoteEPCServer::Setup &S) -> Error {
S.setDispatcher(
std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
S.bootstrapSymbols() =
SimpleRemoteEPCServer::defaultBootstrapSymbols();
S.services().push_back(
std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
return Error::success();
},
InFD, OutFD));
ExitOnErr(Server->waitForDisconnect());
return 0;
#else
errs() << argv[0]
<< " error: this tool requires threads, but LLVM was "
"built with LLVM_ENABLE_THREADS=Off\n";
return 1;
#endif
}