| //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "RemoteJITUtils.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h" | |
| #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" | |
| #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" | |
| #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/Path.h" | |
| #ifdef LLVM_ON_UNIX | |
| #include <netdb.h> | |
| #include <netinet/in.h> | |
| #include <sys/socket.h> | |
| #include <unistd.h> | |
| #endif // LLVM_ON_UNIX | |
| using namespace llvm; | |
| using namespace llvm::orc; | |
| Expected<std::unique_ptr<DefinitionGenerator>> | |
| loadDylib(ExecutionSession &ES, DylibManager &DylibMgr, StringRef RemotePath) { | |
| if (auto Handle = DylibMgr.loadDylib(RemotePath.data())) | |
| return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, DylibMgr, | |
| *Handle); | |
| else | |
| return Handle.takeError(); | |
| } | |
| static void findLocalExecutorHelper() {} | |
| std::string findLocalExecutor(const char *HostArgv0) { | |
| // This just needs to be some static symbol in the binary; C++ doesn't | |
| // allow taking the address of ::main however. | |
| uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper); | |
| void *VoidPtr = reinterpret_cast<void *>(UIntPtr); | |
| SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr)); | |
| sys::path::remove_filename(FullName); | |
| sys::path::append(FullName, "llvm-jitlink-executor"); | |
| return FullName.str().str(); | |
| } | |
| #ifndef LLVM_ON_UNIX | |
| // FIXME: Add support for Windows. | |
| Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>> | |
| launchLocalExecutor(StringRef ExecutablePath) { | |
| return make_error<StringError>( | |
| "Remote JITing not yet supported on non-unix platforms", | |
| inconvertibleErrorCode()); | |
| } | |
| // FIXME: Add support for Windows. | |
| Expected<std::unique_ptr<SimpleRemoteEPC>> | |
| connectTCPSocket(StringRef NetworkAddress) { | |
| return make_error<StringError>( | |
| "Remote JITing not yet supported on non-unix platforms", | |
| inconvertibleErrorCode()); | |
| } | |
| #else | |
| Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>> | |
| launchLocalExecutor(StringRef ExecutablePath) { | |
| if (!sys::fs::can_execute(ExecutablePath)) | |
| return make_error<StringError>( | |
| formatv("Specified executor invalid: {0}", ExecutablePath), | |
| inconvertibleErrorCode()); | |
| // Unix domain socket FDs. | |
| constexpr int ParentSocket = 0, ChildSocket = 1; | |
| int Sockets[2]; | |
| if (socketpair(AF_UNIX, SOCK_STREAM, 0, Sockets) == -1) | |
| return make_error<StringError>( | |
| Twine("Unable to create socket for executor: ") + strerror(errno), | |
| inconvertibleErrorCode()); | |
| pid_t ProcessID = fork(); | |
| if (ProcessID == 0) { | |
| // In the child... | |
| close(Sockets[ParentSocket]); | |
| std::string ExecPath = ExecutablePath.str(); | |
| std::string TestOutputFlag = "test-jitloadergdb"; | |
| std::string ConnSpec = | |
| "socket:adopt=" + std::to_string(Sockets[ChildSocket]); | |
| char *const Args[] = {ExecPath.data(), TestOutputFlag.data(), | |
| ConnSpec.data(), nullptr}; | |
| int RC = execvp(ExecutablePath.data(), Args); | |
| if (RC != 0) | |
| return make_error<StringError>( | |
| "Unable to launch out-of-process executor '" + ExecutablePath + "'\n", | |
| inconvertibleErrorCode()); | |
| llvm_unreachable("Fork won't return in success case"); | |
| } | |
| // else we're the parent... | |
| // Close the child ends of the pipes | |
| close(Sockets[ChildSocket]); | |
| auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( | |
| std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), | |
| Sockets[ParentSocket], Sockets[ParentSocket]); | |
| if (!EPC) | |
| return EPC.takeError(); | |
| return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID)); | |
| } | |
| static Expected<int> connectTCPSocketImpl(std::string Host, | |
| std::string PortStr) { | |
| addrinfo *AI; | |
| addrinfo Hints{}; | |
| Hints.ai_family = AF_INET; | |
| Hints.ai_socktype = SOCK_STREAM; | |
| Hints.ai_flags = AI_NUMERICSERV; | |
| if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI)) | |
| return make_error<StringError>( | |
| formatv("address resolution failed ({0})", gai_strerror(EC)), | |
| inconvertibleErrorCode()); | |
| // Cycle through the returned addrinfo structures and connect to the first | |
| // reachable endpoint. | |
| int SockFD; | |
| addrinfo *Server; | |
| for (Server = AI; Server != nullptr; Server = Server->ai_next) { | |
| // If socket fails, maybe it's because the address family is not supported. | |
| // Skip to the next addrinfo structure. | |
| if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) | |
| continue; | |
| // If connect works, we exit the loop with a working socket. | |
| if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) | |
| break; | |
| close(SockFD); | |
| } | |
| freeaddrinfo(AI); | |
| // Did we reach the end of the loop without connecting to a valid endpoint? | |
| if (Server == nullptr) | |
| return make_error<StringError>("invalid hostname", | |
| inconvertibleErrorCode()); | |
| return SockFD; | |
| } | |
| Expected<std::unique_ptr<SimpleRemoteEPC>> | |
| connectTCPSocket(StringRef NetworkAddress) { | |
| auto CreateErr = [NetworkAddress](StringRef Details) { | |
| return make_error<StringError>( | |
| formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress, | |
| Details), | |
| inconvertibleErrorCode()); | |
| }; | |
| StringRef Host, PortStr; | |
| std::tie(Host, PortStr) = NetworkAddress.split(':'); | |
| if (Host.empty()) | |
| return CreateErr("host name cannot be empty"); | |
| if (PortStr.empty()) | |
| return CreateErr("port cannot be empty"); | |
| int Port = 0; | |
| if (PortStr.getAsInteger(10, Port)) | |
| return CreateErr("port number is not a valid integer"); | |
| Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); | |
| if (!SockFD) | |
| return CreateErr(toString(SockFD.takeError())); | |
| return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( | |
| std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), *SockFD); | |
| } | |
| #endif |