1258f055eSStefan Gränitz //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
2258f055eSStefan Gränitz //
3258f055eSStefan Gränitz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4258f055eSStefan Gränitz // See https://llvm.org/LICENSE.txt for license information.
5258f055eSStefan Gränitz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6258f055eSStefan Gränitz //
7258f055eSStefan Gränitz //===----------------------------------------------------------------------===//
8258f055eSStefan Gränitz 
9258f055eSStefan Gränitz #include "RemoteJITUtils.h"
10258f055eSStefan Gränitz 
11258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
12*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
13*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
14*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h"
15258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
16258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
17258f055eSStefan Gränitz #include "llvm/Support/FileSystem.h"
18258f055eSStefan Gränitz #include "llvm/Support/Path.h"
19258f055eSStefan Gränitz #include "llvm/Support/ToolOutputFile.h"
20258f055eSStefan Gränitz 
21258f055eSStefan Gränitz #ifdef LLVM_ON_UNIX
22258f055eSStefan Gränitz #include <netdb.h>
23258f055eSStefan Gränitz #include <netinet/in.h>
24258f055eSStefan Gränitz #include <sys/socket.h>
25258f055eSStefan Gränitz #include <unistd.h>
26258f055eSStefan Gränitz #endif // LLVM_ON_UNIX
27258f055eSStefan Gränitz 
28258f055eSStefan Gränitz using namespace llvm;
29258f055eSStefan Gränitz using namespace llvm::orc;
30258f055eSStefan Gränitz 
31258f055eSStefan Gränitz namespace llvm {
32258f055eSStefan Gränitz namespace orc {
33258f055eSStefan Gränitz 
34*662c5544SLang Hames class RemoteExecutorProcessControl
35*662c5544SLang Hames     : public OrcRPCExecutorProcessControlBase<
36258f055eSStefan Gränitz           shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> {
37258f055eSStefan Gränitz public:
38258f055eSStefan Gränitz   using RPCChannel = JITLinkExecutor::RPCChannel;
39258f055eSStefan Gränitz   using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>;
40258f055eSStefan Gränitz 
41258f055eSStefan Gränitz private:
42*662c5544SLang Hames   using ThisT = RemoteExecutorProcessControl;
43*662c5544SLang Hames   using BaseT = OrcRPCExecutorProcessControlBase<RPCEndpoint>;
44*662c5544SLang Hames   using MemoryAccess = OrcRPCEPCMemoryAccess<ThisT>;
45*662c5544SLang Hames   using MemoryManager = OrcRPCEPCJITLinkMemoryManager<ThisT>;
46258f055eSStefan Gränitz 
47258f055eSStefan Gränitz public:
48*662c5544SLang Hames   using BaseT::initializeORCRPCEPCBase;
49258f055eSStefan Gränitz 
50*662c5544SLang Hames   RemoteExecutorProcessControl(ExecutionSession &ES,
51258f055eSStefan Gränitz                                std::unique_ptr<RPCChannel> Channel,
52258f055eSStefan Gränitz                                std::unique_ptr<RPCEndpoint> Endpoint);
53258f055eSStefan Gränitz 
54258f055eSStefan Gränitz   void initializeMemoryManagement();
55258f055eSStefan Gränitz   Error disconnect() override;
56258f055eSStefan Gränitz 
57258f055eSStefan Gränitz private:
58258f055eSStefan Gränitz   std::unique_ptr<RPCChannel> Channel;
59258f055eSStefan Gränitz   std::unique_ptr<RPCEndpoint> Endpoint;
60258f055eSStefan Gränitz   std::unique_ptr<MemoryAccess> OwnedMemAccess;
61258f055eSStefan Gränitz   std::unique_ptr<MemoryManager> OwnedMemMgr;
62258f055eSStefan Gränitz   std::atomic<bool> Finished{false};
63258f055eSStefan Gränitz   std::thread ListenerThread;
64258f055eSStefan Gränitz };
65258f055eSStefan Gränitz 
66*662c5544SLang Hames RemoteExecutorProcessControl::RemoteExecutorProcessControl(
67258f055eSStefan Gränitz     ExecutionSession &ES, std::unique_ptr<RPCChannel> Channel,
68258f055eSStefan Gränitz     std::unique_ptr<RPCEndpoint> Endpoint)
69258f055eSStefan Gränitz     : BaseT(ES.getSymbolStringPool(), *Endpoint,
70258f055eSStefan Gränitz             [&ES](Error Err) { ES.reportError(std::move(Err)); }),
71258f055eSStefan Gränitz       Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) {
72258f055eSStefan Gränitz 
73258f055eSStefan Gränitz   ListenerThread = std::thread([&]() {
74258f055eSStefan Gränitz     while (!Finished) {
75258f055eSStefan Gränitz       if (auto Err = this->Endpoint->handleOne()) {
76258f055eSStefan Gränitz         reportError(std::move(Err));
77258f055eSStefan Gränitz         return;
78258f055eSStefan Gränitz       }
79258f055eSStefan Gränitz     }
80258f055eSStefan Gränitz   });
81258f055eSStefan Gränitz }
82258f055eSStefan Gränitz 
83*662c5544SLang Hames void RemoteExecutorProcessControl::initializeMemoryManagement() {
84258f055eSStefan Gränitz   OwnedMemAccess = std::make_unique<MemoryAccess>(*this);
85258f055eSStefan Gränitz   OwnedMemMgr = std::make_unique<MemoryManager>(*this);
86258f055eSStefan Gränitz 
87258f055eSStefan Gränitz   // Base class needs non-owning access.
88258f055eSStefan Gränitz   MemAccess = OwnedMemAccess.get();
89258f055eSStefan Gränitz   MemMgr = OwnedMemMgr.get();
90258f055eSStefan Gränitz }
91258f055eSStefan Gränitz 
92*662c5544SLang Hames Error RemoteExecutorProcessControl::disconnect() {
93258f055eSStefan Gränitz   std::promise<MSVCPError> P;
94258f055eSStefan Gränitz   auto F = P.get_future();
95258f055eSStefan Gränitz   auto Err = closeConnection([&](Error Err) -> Error {
96258f055eSStefan Gränitz     P.set_value(std::move(Err));
97258f055eSStefan Gränitz     Finished = true;
98258f055eSStefan Gränitz     return Error::success();
99258f055eSStefan Gränitz   });
100258f055eSStefan Gränitz   ListenerThread.join();
101258f055eSStefan Gränitz   return joinErrors(std::move(Err), F.get());
102258f055eSStefan Gränitz }
103258f055eSStefan Gränitz 
104258f055eSStefan Gränitz } // namespace orc
105258f055eSStefan Gränitz } // namespace llvm
106258f055eSStefan Gränitz 
107258f055eSStefan Gränitz JITLinkExecutor::JITLinkExecutor() = default;
108258f055eSStefan Gränitz JITLinkExecutor::~JITLinkExecutor() = default;
109258f055eSStefan Gränitz 
110258f055eSStefan Gränitz Expected<std::unique_ptr<ObjectLayer>>
111258f055eSStefan Gränitz JITLinkExecutor::operator()(ExecutionSession &ES, const Triple &TT) {
112*662c5544SLang Hames   return std::make_unique<ObjectLinkingLayer>(ES, EPC->getMemMgr());
113258f055eSStefan Gränitz }
114258f055eSStefan Gränitz 
115258f055eSStefan Gränitz Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) {
116*662c5544SLang Hames   auto Registrar = createJITLoaderGDBRegistrar(*EPC);
117258f055eSStefan Gränitz   if (!Registrar)
118258f055eSStefan Gränitz     return Registrar.takeError();
119258f055eSStefan Gränitz 
120258f055eSStefan Gränitz   cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(
121258f055eSStefan Gränitz       std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),
122258f055eSStefan Gränitz                                                  std::move(*Registrar)));
123258f055eSStefan Gränitz 
124258f055eSStefan Gränitz   return Error::success();
125258f055eSStefan Gränitz }
126258f055eSStefan Gränitz 
127258f055eSStefan Gränitz Expected<std::unique_ptr<DefinitionGenerator>>
128258f055eSStefan Gränitz JITLinkExecutor::loadDylib(StringRef RemotePath) {
129*662c5544SLang Hames   if (auto Handle = EPC->loadDylib(RemotePath.data()))
130*662c5544SLang Hames     return std::make_unique<EPCDynamicLibrarySearchGenerator>(*EPC, *Handle);
131258f055eSStefan Gränitz   else
132258f055eSStefan Gränitz     return Handle.takeError();
133258f055eSStefan Gränitz }
134258f055eSStefan Gränitz 
135258f055eSStefan Gränitz Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,
136258f055eSStefan Gränitz                                          ArrayRef<std::string> Args) {
137*662c5544SLang Hames   return EPC->runAsMain(MainSym.getAddress(), Args);
138258f055eSStefan Gränitz }
139258f055eSStefan Gränitz 
140*662c5544SLang Hames Error JITLinkExecutor::disconnect() { return EPC->disconnect(); }
141258f055eSStefan Gränitz 
142258f055eSStefan Gränitz static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName) {
143258f055eSStefan Gränitz   // This just needs to be some symbol in the binary; C++ doesn't
144258f055eSStefan Gränitz   // allow taking the address of ::main however.
145258f055eSStefan Gränitz   void *P = (void *)(intptr_t)defaultPath;
146258f055eSStefan Gränitz   SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, P));
147258f055eSStefan Gränitz   sys::path::remove_filename(FullName);
148258f055eSStefan Gränitz   sys::path::append(FullName, ExecutorName);
149258f055eSStefan Gränitz   return FullName.str().str();
150258f055eSStefan Gränitz }
151258f055eSStefan Gränitz 
152258f055eSStefan Gränitz Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
153258f055eSStefan Gränitz JITLinkExecutor::FindLocal(const char *HostArgv) {
154258f055eSStefan Gränitz   std::string BestGuess = defaultPath(HostArgv, "llvm-jitlink-executor");
155258f055eSStefan Gränitz   auto Executor = CreateLocal(BestGuess);
156258f055eSStefan Gränitz   if (!Executor) {
157258f055eSStefan Gränitz     consumeError(Executor.takeError());
158258f055eSStefan Gränitz     return make_error<StringError>(
159258f055eSStefan Gränitz         formatv("Unable to find usable executor: {0}", BestGuess),
160258f055eSStefan Gränitz         inconvertibleErrorCode());
161258f055eSStefan Gränitz   }
162258f055eSStefan Gränitz   return Executor;
163258f055eSStefan Gränitz }
164258f055eSStefan Gränitz 
165258f055eSStefan Gränitz Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
166258f055eSStefan Gränitz JITLinkExecutor::CreateLocal(std::string ExecutablePath) {
167258f055eSStefan Gränitz   if (!sys::fs::can_execute(ExecutablePath))
168258f055eSStefan Gränitz     return make_error<StringError>(
169258f055eSStefan Gränitz         formatv("Specified executor invalid: {0}", ExecutablePath),
170258f055eSStefan Gränitz         inconvertibleErrorCode());
171258f055eSStefan Gränitz   return std::unique_ptr<ChildProcessJITLinkExecutor>(
172258f055eSStefan Gränitz       new ChildProcessJITLinkExecutor(std::move(ExecutablePath)));
173258f055eSStefan Gränitz }
174258f055eSStefan Gränitz 
175258f055eSStefan Gränitz TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(
176*662c5544SLang Hames     std::unique_ptr<RemoteExecutorProcessControl> EPC) {
177*662c5544SLang Hames   this->EPC = std::move(EPC);
178258f055eSStefan Gränitz }
179258f055eSStefan Gränitz 
180258f055eSStefan Gränitz #ifndef LLVM_ON_UNIX
181258f055eSStefan Gränitz 
182258f055eSStefan Gränitz // FIXME: Add support for Windows.
183258f055eSStefan Gränitz Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
184258f055eSStefan Gränitz   return make_error<StringError>(
185258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
186258f055eSStefan Gränitz       inconvertibleErrorCode());
187258f055eSStefan Gränitz }
188258f055eSStefan Gränitz 
189258f055eSStefan Gränitz // FIXME: Add support for Windows.
190258f055eSStefan Gränitz Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
191258f055eSStefan Gränitz JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
192258f055eSStefan Gränitz                                   ExecutionSession &ES) {
193258f055eSStefan Gränitz   return make_error<StringError>(
194258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
195258f055eSStefan Gränitz       inconvertibleErrorCode());
196258f055eSStefan Gränitz }
197258f055eSStefan Gränitz 
198258f055eSStefan Gränitz #else
199258f055eSStefan Gränitz 
200258f055eSStefan Gränitz Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
201258f055eSStefan Gränitz   constexpr int ReadEnd = 0;
202258f055eSStefan Gränitz   constexpr int WriteEnd = 1;
203258f055eSStefan Gränitz 
204258f055eSStefan Gränitz   // Pipe FDs.
205258f055eSStefan Gränitz   int ToExecutor[2];
206258f055eSStefan Gränitz   int FromExecutor[2];
207258f055eSStefan Gränitz 
208258f055eSStefan Gränitz   // Create pipes to/from the executor..
209258f055eSStefan Gränitz   if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
210258f055eSStefan Gränitz     return make_error<StringError>("Unable to create pipe for executor",
211258f055eSStefan Gränitz                                    inconvertibleErrorCode());
212258f055eSStefan Gränitz 
213258f055eSStefan Gränitz   ProcessID = fork();
214258f055eSStefan Gränitz   if (ProcessID == 0) {
215258f055eSStefan Gränitz     // In the child...
216258f055eSStefan Gränitz 
217258f055eSStefan Gränitz     // Close the parent ends of the pipes
218258f055eSStefan Gränitz     close(ToExecutor[WriteEnd]);
219258f055eSStefan Gränitz     close(FromExecutor[ReadEnd]);
220258f055eSStefan Gränitz 
221258f055eSStefan Gränitz     // Execute the child process.
222258f055eSStefan Gränitz     std::unique_ptr<char[]> ExecPath, FDSpecifier;
223258f055eSStefan Gränitz     {
224258f055eSStefan Gränitz       ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
225258f055eSStefan Gränitz       strcpy(ExecPath.get(), ExecutablePath.data());
226258f055eSStefan Gränitz 
227258f055eSStefan Gränitz       std::string FDSpecifierStr("filedescs=");
228258f055eSStefan Gränitz       FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
229258f055eSStefan Gränitz       FDSpecifierStr += ',';
230258f055eSStefan Gränitz       FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
231258f055eSStefan Gränitz       FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
232258f055eSStefan Gränitz       strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
233258f055eSStefan Gränitz     }
234258f055eSStefan Gränitz 
235258f055eSStefan Gränitz     char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr};
236258f055eSStefan Gränitz     int RC = execvp(ExecPath.get(), Args);
237258f055eSStefan Gränitz     if (RC != 0)
238258f055eSStefan Gränitz       return make_error<StringError>(
239258f055eSStefan Gränitz           "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
240258f055eSStefan Gränitz           inconvertibleErrorCode());
241258f055eSStefan Gränitz 
242258f055eSStefan Gränitz     llvm_unreachable("Fork won't return in success case");
243258f055eSStefan Gränitz   }
244258f055eSStefan Gränitz   // else we're the parent...
245258f055eSStefan Gränitz 
246258f055eSStefan Gränitz   // Close the child ends of the pipes
247258f055eSStefan Gränitz   close(ToExecutor[ReadEnd]);
248258f055eSStefan Gränitz   close(FromExecutor[WriteEnd]);
249258f055eSStefan Gränitz 
250258f055eSStefan Gränitz   auto Channel =
251258f055eSStefan Gränitz       std::make_unique<RPCChannel>(FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
252*662c5544SLang Hames   auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
253*662c5544SLang Hames       *Channel, true);
254258f055eSStefan Gränitz 
255*662c5544SLang Hames   EPC = std::make_unique<RemoteExecutorProcessControl>(ES, std::move(Channel),
256258f055eSStefan Gränitz                                                        std::move(Endpoint));
257258f055eSStefan Gränitz 
258*662c5544SLang Hames   if (auto Err = EPC->initializeORCRPCEPCBase())
259*662c5544SLang Hames     return joinErrors(std::move(Err), EPC->disconnect());
260258f055eSStefan Gränitz 
261*662c5544SLang Hames   EPC->initializeMemoryManagement();
262258f055eSStefan Gränitz 
263258f055eSStefan Gränitz   shared::registerStringError<RPCChannel>();
264258f055eSStefan Gränitz   return Error::success();
265258f055eSStefan Gränitz }
266258f055eSStefan Gränitz 
267258f055eSStefan Gränitz static Expected<int> connectTCPSocketImpl(std::string Host,
268258f055eSStefan Gränitz                                           std::string PortStr) {
269258f055eSStefan Gränitz   addrinfo *AI;
270258f055eSStefan Gränitz   addrinfo Hints{};
271258f055eSStefan Gränitz   Hints.ai_family = AF_INET;
272258f055eSStefan Gränitz   Hints.ai_socktype = SOCK_STREAM;
273258f055eSStefan Gränitz   Hints.ai_flags = AI_NUMERICSERV;
274258f055eSStefan Gränitz 
275258f055eSStefan Gränitz   if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
276258f055eSStefan Gränitz     return make_error<StringError>(
277258f055eSStefan Gränitz         formatv("address resolution failed ({0})", gai_strerror(EC)),
278258f055eSStefan Gränitz         inconvertibleErrorCode());
279258f055eSStefan Gränitz 
280258f055eSStefan Gränitz   // Cycle through the returned addrinfo structures and connect to the first
281258f055eSStefan Gränitz   // reachable endpoint.
282258f055eSStefan Gränitz   int SockFD;
283258f055eSStefan Gränitz   addrinfo *Server;
284258f055eSStefan Gränitz   for (Server = AI; Server != nullptr; Server = Server->ai_next) {
285258f055eSStefan Gränitz     // If socket fails, maybe it's because the address family is not supported.
286258f055eSStefan Gränitz     // Skip to the next addrinfo structure.
287258f055eSStefan Gränitz     if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
288258f055eSStefan Gränitz       continue;
289258f055eSStefan Gränitz 
290258f055eSStefan Gränitz     // If connect works, we exit the loop with a working socket.
291258f055eSStefan Gränitz     if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
292258f055eSStefan Gränitz       break;
293258f055eSStefan Gränitz 
294258f055eSStefan Gränitz     close(SockFD);
295258f055eSStefan Gränitz   }
296258f055eSStefan Gränitz   freeaddrinfo(AI);
297258f055eSStefan Gränitz 
298258f055eSStefan Gränitz   // Did we reach the end of the loop without connecting to a valid endpoint?
299258f055eSStefan Gränitz   if (Server == nullptr)
300258f055eSStefan Gränitz     return make_error<StringError>("invalid hostname",
301258f055eSStefan Gränitz                                    inconvertibleErrorCode());
302258f055eSStefan Gränitz 
303258f055eSStefan Gränitz   return SockFD;
304258f055eSStefan Gränitz }
305258f055eSStefan Gränitz 
306258f055eSStefan Gränitz Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
307258f055eSStefan Gränitz JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
308258f055eSStefan Gränitz                                   ExecutionSession &ES) {
309258f055eSStefan Gränitz   auto CreateErr = [NetworkAddress](StringRef Details) {
310258f055eSStefan Gränitz     return make_error<StringError>(
311258f055eSStefan Gränitz         formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
312258f055eSStefan Gränitz                 Details),
313258f055eSStefan Gränitz         inconvertibleErrorCode());
314258f055eSStefan Gränitz   };
315258f055eSStefan Gränitz 
316258f055eSStefan Gränitz   StringRef Host, PortStr;
317258f055eSStefan Gränitz   std::tie(Host, PortStr) = NetworkAddress.split(':');
318258f055eSStefan Gränitz   if (Host.empty())
319258f055eSStefan Gränitz     return CreateErr("host name cannot be empty");
320258f055eSStefan Gränitz   if (PortStr.empty())
321258f055eSStefan Gränitz     return CreateErr("port cannot be empty");
322258f055eSStefan Gränitz   int Port = 0;
323258f055eSStefan Gränitz   if (PortStr.getAsInteger(10, Port))
324258f055eSStefan Gränitz     return CreateErr("port number is not a valid integer");
325258f055eSStefan Gränitz 
326258f055eSStefan Gränitz   Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
327258f055eSStefan Gränitz   if (!SockFD)
328258f055eSStefan Gränitz     return CreateErr(toString(SockFD.takeError()));
329258f055eSStefan Gränitz 
330258f055eSStefan Gränitz   auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD);
331*662c5544SLang Hames   auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
332*662c5544SLang Hames       *Channel, true);
333258f055eSStefan Gränitz 
334*662c5544SLang Hames   auto EPC = std::make_unique<RemoteExecutorProcessControl>(
335258f055eSStefan Gränitz       ES, std::move(Channel), std::move(Endpoint));
336258f055eSStefan Gränitz 
337*662c5544SLang Hames   if (auto Err = EPC->initializeORCRPCEPCBase())
338*662c5544SLang Hames     return joinErrors(std::move(Err), EPC->disconnect());
339258f055eSStefan Gränitz 
340*662c5544SLang Hames   EPC->initializeMemoryManagement();
341258f055eSStefan Gränitz   shared::registerStringError<RPCChannel>();
342258f055eSStefan Gränitz 
343258f055eSStefan Gränitz   return std::unique_ptr<TCPSocketJITLinkExecutor>(
344*662c5544SLang Hames       new TCPSocketJITLinkExecutor(std::move(EPC)));
345258f055eSStefan Gränitz }
346258f055eSStefan Gränitz 
347258f055eSStefan Gränitz #endif
348