//===-- 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/DebugObjectManagerPlugin.h" #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.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 #include #include #include #endif // LLVM_ON_UNIX using namespace llvm; using namespace llvm::orc; Expected> loadDylib(ExecutionSession &ES, StringRef RemotePath) { if (auto Handle = ES.getExecutorProcessControl().getDylibMgr().loadDylib( RemotePath.data())) return std::make_unique(ES, *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(&findLocalExecutorHelper); void *VoidPtr = reinterpret_cast(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, uint64_t>> launchLocalExecutor(StringRef ExecutablePath) { return make_error( "Remote JITing not yet supported on non-unix platforms", inconvertibleErrorCode()); } // FIXME: Add support for Windows. Expected> connectTCPSocket(StringRef NetworkAddress) { return make_error( "Remote JITing not yet supported on non-unix platforms", inconvertibleErrorCode()); } #else Expected, uint64_t>> launchLocalExecutor(StringRef ExecutablePath) { constexpr int ReadEnd = 0; constexpr int WriteEnd = 1; if (!sys::fs::can_execute(ExecutablePath)) return make_error( formatv("Specified executor invalid: {0}", ExecutablePath), inconvertibleErrorCode()); // Pipe FDs. int ToExecutor[2]; int FromExecutor[2]; // Create pipes to/from the executor.. if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0) return make_error("Unable to create pipe for executor", inconvertibleErrorCode()); pid_t ProcessID = fork(); if (ProcessID == 0) { // In the child... // Close the parent ends of the pipes close(ToExecutor[WriteEnd]); close(FromExecutor[ReadEnd]); // Execute the child process. std::unique_ptr ExecPath, FDSpecifier, TestOutputFlag; { ExecPath = std::make_unique(ExecutablePath.size() + 1); strcpy(ExecPath.get(), ExecutablePath.data()); const char *TestOutputFlagStr = "test-jitloadergdb"; TestOutputFlag = std::make_unique(strlen(TestOutputFlagStr) + 1); strcpy(TestOutputFlag.get(), TestOutputFlagStr); std::string FDSpecifierStr("filedescs="); FDSpecifierStr += utostr(ToExecutor[ReadEnd]); FDSpecifierStr += ','; FDSpecifierStr += utostr(FromExecutor[WriteEnd]); FDSpecifier = std::make_unique(FDSpecifierStr.size() + 1); strcpy(FDSpecifier.get(), FDSpecifierStr.c_str()); } char *const Args[] = {ExecPath.get(), TestOutputFlag.get(), FDSpecifier.get(), nullptr}; int RC = execvp(ExecPath.get(), Args); if (RC != 0) return make_error( "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(ToExecutor[ReadEnd]); close(FromExecutor[WriteEnd]); auto EPC = SimpleRemoteEPC::Create( std::make_unique(std::nullopt), SimpleRemoteEPC::Setup(), FromExecutor[ReadEnd], ToExecutor[WriteEnd]); if (!EPC) return EPC.takeError(); return std::make_pair(std::move(*EPC), static_cast(ProcessID)); } static Expected 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( 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("invalid hostname", inconvertibleErrorCode()); return SockFD; } Expected> connectTCPSocket(StringRef NetworkAddress) { auto CreateErr = [NetworkAddress](StringRef Details) { return make_error( 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 SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); if (!SockFD) return CreateErr(toString(SockFD.takeError())); return SimpleRemoteEPC::Create( std::make_unique(std::nullopt), SimpleRemoteEPC::Setup(), *SockFD); } #endif