xref: /llvm-project/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp (revision c87d198cd964f37343083848f8fdd58bb0b00156)
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