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(status: 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(name: nullptr, service: PortStr.c_str(), req: &Hints, pai: &AI)) { |
76 | errs() << "Error setting up bind address: " << gai_strerror(ecode: EC) << "\n" ; |
77 | exit(status: 1); |
78 | } |
79 | |
80 | // Create a socket from first addrinfo structure returned by getaddrinfo. |
81 | int SockFD; |
82 | if ((SockFD = socket(domain: AI->ai_family, type: AI->ai_socktype, protocol: AI->ai_protocol)) < 0) { |
83 | errs() << "Error creating socket: " << std::strerror(errno) << "\n" ; |
84 | exit(status: 1); |
85 | } |
86 | |
87 | // Avoid "Address already in use" errors. |
88 | const int Yes = 1; |
89 | if (setsockopt(fd: SockFD, SOL_SOCKET, SO_REUSEADDR, optval: &Yes, optlen: sizeof(int)) == -1) { |
90 | errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n" ; |
91 | exit(status: 1); |
92 | } |
93 | |
94 | // Bind the socket to the desired port. |
95 | if (bind(fd: SockFD, addr: AI->ai_addr, len: AI->ai_addrlen) < 0) { |
96 | errs() << "Error on binding: " << std::strerror(errno) << "\n" ; |
97 | exit(status: 1); |
98 | } |
99 | |
100 | // Listen for incomming connections. |
101 | static constexpr int ConnectionQueueLen = 1; |
102 | listen(fd: SockFD, n: 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(fd: SockFD, addr: AI->ai_addr, addr_len: &AI->ai_addrlen); |
110 | #endif |
111 | |
112 | #endif // LLVM_ON_UNIX |
113 | } |
114 | |
115 | #if LLVM_ENABLE_THREADS |
116 | |
117 | // JITLink debug support plugins put information about JITed code in this GDB |
118 | // JIT Interface global from OrcTargetProcess. |
119 | extern "C" struct jit_descriptor __jit_debug_descriptor; |
120 | |
121 | static void *findLastDebugDescriptorEntryPtr() { |
122 | struct jit_code_entry *Last = __jit_debug_descriptor.first_entry; |
123 | while (Last && Last->next_entry) |
124 | Last = Last->next_entry; |
125 | return Last; |
126 | } |
127 | |
128 | #endif |
129 | |
130 | int main(int argc, char *argv[]) { |
131 | #if LLVM_ENABLE_THREADS |
132 | |
133 | ExitOnErr.setBanner(std::string(argv[0]) + ": " ); |
134 | |
135 | unsigned FirstProgramArg = 1; |
136 | int InFD = 0; |
137 | int OutFD = 0; |
138 | |
139 | if (argc < 2) |
140 | printErrorAndExit(ErrMsg: "insufficient arguments" ); |
141 | |
142 | StringRef NextArg = argv[FirstProgramArg++]; |
143 | #ifndef NDEBUG |
144 | if (NextArg == "debug" ) { |
145 | DebugFlag = true; |
146 | NextArg = argv[FirstProgramArg++]; |
147 | } |
148 | #endif |
149 | |
150 | std::vector<StringRef> TestOutputFlags; |
151 | while (NextArg.starts_with(Prefix: "test-" )) { |
152 | TestOutputFlags.push_back(x: NextArg); |
153 | NextArg = argv[FirstProgramArg++]; |
154 | } |
155 | |
156 | if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb" )) |
157 | fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n" , |
158 | pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr())); |
159 | |
160 | StringRef SpecifierType, Specifier; |
161 | std::tie(args&: SpecifierType, args&: Specifier) = NextArg.split(Separator: '='); |
162 | if (SpecifierType == "filedescs" ) { |
163 | StringRef FD1Str, FD2Str; |
164 | std::tie(args&: FD1Str, args&: FD2Str) = Specifier.split(Separator: ','); |
165 | if (FD1Str.getAsInteger(Radix: 10, Result&: InFD)) |
166 | printErrorAndExit(ErrMsg: FD1Str + " is not a valid file descriptor" ); |
167 | if (FD2Str.getAsInteger(Radix: 10, Result&: OutFD)) |
168 | printErrorAndExit(ErrMsg: FD2Str + " is not a valid file descriptor" ); |
169 | } else if (SpecifierType == "listen" ) { |
170 | StringRef Host, PortStr; |
171 | std::tie(args&: Host, args&: PortStr) = Specifier.split(Separator: ':'); |
172 | |
173 | int Port = 0; |
174 | if (PortStr.getAsInteger(Radix: 10, Result&: Port)) |
175 | printErrorAndExit(ErrMsg: "port number '" + PortStr + "' is not a valid integer" ); |
176 | |
177 | InFD = OutFD = openListener(Host: Host.str(), PortStr: PortStr.str()); |
178 | } else |
179 | printErrorAndExit(ErrMsg: "invalid specifier type \"" + SpecifierType + "\"" ); |
180 | |
181 | auto Server = |
182 | ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>( |
183 | SetupFunction: [](SimpleRemoteEPCServer::Setup &S) -> Error { |
184 | S.setDispatcher( |
185 | std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>()); |
186 | S.bootstrapSymbols() = |
187 | SimpleRemoteEPCServer::defaultBootstrapSymbols(); |
188 | S.services().push_back( |
189 | x: std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>()); |
190 | S.services().push_back( |
191 | x: std::make_unique< |
192 | rt_bootstrap::ExecutorSharedMemoryMapperService>()); |
193 | return Error::success(); |
194 | }, |
195 | TransportTCtorArgs&: InFD, TransportTCtorArgs&: OutFD)); |
196 | |
197 | ExitOnErr(Server->waitForDisconnect()); |
198 | |
199 | if (llvm::is_contained(Range&: TestOutputFlags, Element: "test-jitloadergdb" )) |
200 | fprintf(stderr, format: "__jit_debug_descriptor.last_entry = 0x%016" PRIx64 "\n" , |
201 | pointerToJITTargetAddress(Ptr: findLastDebugDescriptorEntryPtr())); |
202 | |
203 | return 0; |
204 | |
205 | #else |
206 | errs() << argv[0] |
207 | << " error: this tool requires threads, but LLVM was " |
208 | "built with LLVM_ENABLE_THREADS=Off\n" ; |
209 | return 1; |
210 | #endif |
211 | } |
212 | |