diff --git a/llvm/examples/OrcV2Examples/LLJITWithTargetProcessControl/LLJITWithTargetProcessControl.cpp b/llvm/examples/OrcV2Examples/LLJITWithTargetProcessControl/LLJITWithTargetProcessControl.cpp --- a/llvm/examples/OrcV2Examples/LLJITWithTargetProcessControl/LLJITWithTargetProcessControl.cpp +++ b/llvm/examples/OrcV2Examples/LLJITWithTargetProcessControl/LLJITWithTargetProcessControl.cpp @@ -134,7 +134,8 @@ ExitOnErr.setBanner(std::string(argv[0]) + ": "); // (1) Create LLJIT instance. - auto TPC = ExitOnErr(SelfTargetProcessControl::Create()); + auto SSP = std::make_shared(); + auto TPC = ExitOnErr(SelfTargetProcessControl::Create(std::move(SSP))); auto J = ExitOnErr(LLJITBuilder().setTargetProcessControl(*TPC).create()); // (2) Install transform to print modules as they are compiled: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h b/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h @@ -21,14 +21,6 @@ namespace llvm { namespace jitlink { -/// Registers all FDEs in the given eh-frame section with the current process. -Error registerEHFrameSection(const void *EHFrameSectionAddr, - size_t EHFrameSectionSize); - -/// Deregisters all FDEs in the given eh-frame section with the current process. -Error deregisterEHFrameSection(const void *EHFrameSectionAddr, - size_t EHFrameSectionSize); - /// Supports registration/deregistration of EH-frames in a target process. class EHFrameRegistrar { public: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -801,6 +801,17 @@ /// Returns the endianness of content in this graph. support::endianness getEndianness() const { return Endianness; } + /// Allocate a copy of the given String using the LinkGraph's allocator. + /// This can be useful when renaming symbols or adding new content to the + /// graph. + StringRef allocateString(Twine Source) { + SmallString<256> TmpBuffer; + auto SourceStr = Source.toStringRef(TmpBuffer); + auto *AllocatedBuffer = Allocator.Allocate(SourceStr.size()); + llvm::copy(SourceStr, AllocatedBuffer); + return StringRef(AllocatedBuffer, SourceStr.size()); + } + /// Create a section with the given name, protection flags, and alignment. Section &createSection(StringRef Name, sys::Memory::ProtectionFlags Prot) { std::unique_ptr
Sec(new Section(Name, Prot, Sections.size())); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h @@ -41,17 +41,6 @@ class ObjectLayer; -/// Run a main function, returning the result. -/// -/// If the optional ProgramName argument is given then it will be inserted -/// before the strings in Args as the first argument to the called function. -/// -/// It is legal to have an empty argument list and no program name, however -/// many main functions will expect a name argument at least, and will fail -/// if none is provided. -int runAsMain(int (*Main)(int, char *[]), ArrayRef Args, - Optional ProgramName = None); - /// This iterator provides a convenient way to iterate over the elements /// of an llvm.global_ctors/llvm.global_dtors instance. /// diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h @@ -0,0 +1,411 @@ +//===--- OrcRPCTargetProcessControl.h - Remote target control ---*- 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 +// +//===----------------------------------------------------------------------===// +// +// Utilities for interacting with target processes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H +#define LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H + +#include "llvm/ExecutionEngine/Orc/RPC/RPCUtils.h" +#include "llvm/ExecutionEngine/Orc/RPC/RawByteChannel.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h" +#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" +#include "llvm/Support/MSVCErrorWorkarounds.h" + +namespace llvm { +namespace orc { + +/// JITLinkMemoryManager implementation for a process connected via an ORC RPC +/// endpoint. +template +class OrcRPCTPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager { +private: + struct HostAlloc { + std::unique_ptr Mem; + uint64_t Size; + }; + + struct TargetAlloc { + JITTargetAddress Address = 0; + uint64_t AllocatedSize = 0; + }; + + using HostAllocMap = DenseMap; + using TargetAllocMap = DenseMap; + +public: + class OrcRPCAllocation : public Allocation { + public: + OrcRPCAllocation(OrcRPCTPCJITLinkMemoryManager &Parent, + HostAllocMap HostAllocs, TargetAllocMap TargetAllocs) + : Parent(Parent), HostAllocs(std::move(HostAllocs)), + TargetAllocs(std::move(TargetAllocs)) { + assert(HostAllocs.size() == TargetAllocs.size() && + "HostAllocs size should match TargetAllocs"); + } + + ~OrcRPCAllocation() override { + assert(TargetAllocs.empty() && "failed to deallocate"); + } + + MutableArrayRef getWorkingMemory(ProtectionFlags Seg) override { + auto I = HostAllocs.find(Seg); + assert(I != HostAllocs.end() && "No host allocation for segment"); + auto &HA = I->second; + return {HA.Mem.get(), HA.Size}; + } + + JITTargetAddress getTargetMemory(ProtectionFlags Seg) override { + auto I = TargetAllocs.find(Seg); + assert(I != TargetAllocs.end() && "No target allocation for segment"); + return I->second.Address; + } + + void finalizeAsync(FinalizeContinuation OnFinalize) override { + + std::vector BufferWrites; + orcrpctpc::ReleaseOrFinalizeMemRequest FMR; + + for (auto &KV : HostAllocs) { + assert(TargetAllocs.count(KV.first) && + "No target allocation for buffer"); + auto &HA = KV.second; + auto &TA = TargetAllocs[KV.first]; + BufferWrites.push_back({TA.Address, StringRef(HA.Mem.get(), HA.Size)}); + FMR.push_back({orcrpctpc::toWireProtectionFlags( + static_cast(KV.first)), + TA.Address, TA.AllocatedSize}); + } + + DEBUG_WITH_TYPE("orc", { + dbgs() << "finalizeAsync " << (void *)this << ":\n"; + auto FMRI = FMR.begin(); + for (auto &B : BufferWrites) { + auto Prot = FMRI->Prot; + ++FMRI; + dbgs() << " Writing " << formatv("{0:x16}", B.Buffer.size()) + << " bytes to " << ((Prot & orcrpctpc::WPF_Read) ? 'R' : '-') + << ((Prot & orcrpctpc::WPF_Write) ? 'W' : '-') + << ((Prot & orcrpctpc::WPF_Exec) ? 'X' : '-') + << " segment: local " << (void *)B.Buffer.data() + << " -> target " << formatv("{0:x16}", B.Address) << "\n"; + } + }); + if (auto Err = + Parent.Parent.getMemoryAccess().writeBuffers(BufferWrites)) { + OnFinalize(std::move(Err)); + return; + } + + DEBUG_WITH_TYPE("orc", dbgs() << " Applying permissions...\n"); + if (auto Err = + Parent.getEndpoint().template callAsync( + [OF = std::move(OnFinalize)](Error Err2) { + // FIXME: Dispatch to work queue. + std::thread([OF = std::move(OF), + Err3 = std::move(Err2)]() mutable { + DEBUG_WITH_TYPE( + "orc", { dbgs() << " finalizeAsync complete\n"; }); + OF(std::move(Err3)); + }).detach(); + return Error::success(); + }, + FMR)) { + DEBUG_WITH_TYPE("orc", dbgs() << " failed.\n"); + Parent.getEndpoint().abandonPendingResponses(); + Parent.reportError(std::move(Err)); + } + DEBUG_WITH_TYPE("orc", { + dbgs() << "Leaving finalizeAsync (finalization may continue in " + "background)\n"; + }); + } + + Error deallocate() override { + orcrpctpc::ReleaseOrFinalizeMemRequest RMR; + for (auto &KV : TargetAllocs) + RMR.push_back({orcrpctpc::toWireProtectionFlags( + static_cast(KV.first)), + KV.second.Address, KV.second.AllocatedSize}); + TargetAllocs.clear(); + + return Parent.getEndpoint().template callB(RMR); + } + + private: + OrcRPCTPCJITLinkMemoryManager &Parent; + HostAllocMap HostAllocs; + TargetAllocMap TargetAllocs; + }; + + OrcRPCTPCJITLinkMemoryManager(OrcRPCTPCImplT &Parent) : Parent(Parent) {} + + Expected> + allocate(const SegmentsRequestMap &Request) override { + orcrpctpc::ReserveMemRequest RMR; + HostAllocMap HostAllocs; + + for (auto &KV : Request) { + RMR.push_back({orcrpctpc::toWireProtectionFlags( + static_cast(KV.first)), + KV.second.getContentSize() + KV.second.getZeroFillSize(), + KV.second.getAlignment()}); + HostAllocs[KV.first] = { + std::make_unique(KV.second.getContentSize()), + KV.second.getContentSize()}; + } + + DEBUG_WITH_TYPE("orc", { + dbgs() << "Orc remote memmgr got request:\n"; + for (auto &KV : Request) + dbgs() << " permissions: " + << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-') + << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-') + << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-') + << ", content size: " + << formatv("{0:x16}", KV.second.getContentSize()) + << " + zero-fill-size: " + << formatv("{0:x16}", KV.second.getZeroFillSize()) + << ", align: " << KV.second.getAlignment() << "\n"; + }); + + // FIXME: LLVM RPC needs to be fixed to support alt + // serialization/deserialization on return types. For now just + // translate from std::map to DenseMap manually. + auto TmpTargetAllocs = + Parent.getEndpoint().template callB(RMR); + if (!TmpTargetAllocs) + return TmpTargetAllocs.takeError(); + + if (TmpTargetAllocs->size() != RMR.size()) + return make_error( + "Number of target allocations does not match request", + inconvertibleErrorCode()); + + TargetAllocMap TargetAllocs; + for (auto &E : *TmpTargetAllocs) + TargetAllocs[orcrpctpc::fromWireProtectionFlags(E.Prot)] = { + E.Address, E.AllocatedSize}; + + DEBUG_WITH_TYPE("orc", { + auto HAI = HostAllocs.begin(); + for (auto &KV : TargetAllocs) + dbgs() << " permissions: " + << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-') + << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-') + << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-') + << " assigned local " << (void *)HAI->second.Mem.get() + << ", target " << formatv("{0:x16}", KV.second.Address) << "\n"; + }); + + return std::make_unique(*this, std::move(HostAllocs), + std::move(TargetAllocs)); + } + +private: + void reportError(Error Err) { Parent.reportError(std::move(Err)); } + + decltype(std::declval().getEndpoint()) getEndpoint() { + return Parent.getEndpoint(); + } + + OrcRPCTPCImplT &Parent; +}; + +/// TargetProcessControl::MemoryAccess implementation for a process connected +/// via an ORC RPC endpoint. +template +class OrcRPCTPCMemoryAccess : public TargetProcessControl::MemoryAccess { +public: + OrcRPCTPCMemoryAccess(OrcRPCTPCImplT &Parent) : Parent(Parent) {} + + void writeUInt8s(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + writeViaRPC(Ws, std::move(OnWriteComplete)); + } + + void writeUInt16s(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + writeViaRPC(Ws, std::move(OnWriteComplete)); + } + + void writeUInt32s(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + writeViaRPC(Ws, std::move(OnWriteComplete)); + } + + void writeUInt64s(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + writeViaRPC(Ws, std::move(OnWriteComplete)); + } + + void writeBuffers(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + writeViaRPC(Ws, std::move(OnWriteComplete)); + } + +private: + template + void writeViaRPC(ArrayRef Ws, WriteResultFn OnWriteComplete) { + if (auto Err = Parent.getEndpoint().template callAsync( + [OWC = std::move(OnWriteComplete)](Error Err2) mutable -> Error { + OWC(std::move(Err2)); + return Error::success(); + }, + Ws)) { + Parent.reportError(std::move(Err)); + Parent.getEndpoint().abandonPendingResponses(); + } + } + + OrcRPCTPCImplT &Parent; +}; + +// TargetProcessControl for a process connected via an ORC RPC Endpoint. +template +class OrcRPCTargetProcessControlBase : public TargetProcessControl { +public: + using ErrorReporter = unique_function; + + using OnCloseConnectionFunction = unique_function; + + OrcRPCTargetProcessControlBase(std::shared_ptr SSP, + RPCEndpointT &EP, ErrorReporter ReportError) + : TargetProcessControl(std::move(SSP)), + ReportError(std::move(ReportError)), EP(EP) {} + + void reportError(Error Err) { ReportError(std::move(Err)); } + + RPCEndpointT &getEndpoint() { return EP; } + + Expected loadDylib(const char *DylibPath) override { + DEBUG_WITH_TYPE("orc", { + dbgs() << "Loading dylib \"" << (DylibPath ? DylibPath : "") << "\" "; + if (!DylibPath) + dbgs() << "(process symbols)"; + dbgs() << "\n"; + }); + if (!DylibPath) + DylibPath = ""; + auto H = EP.template callB(DylibPath); + DEBUG_WITH_TYPE("orc", { + if (H) + dbgs() << " got handle " << formatv("{0:x16}", *H) << "\n"; + else + dbgs() << " error, unable to load\n"; + }); + return H; + } + + Expected> + lookupSymbols(ArrayRef Request) override { + std::vector RR; + for (auto &E : Request) { + RR.push_back({}); + RR.back().first = E.Handle; + for (auto &KV : E.Symbols) + RR.back().second.push_back( + {(*KV.first).str(), + KV.second == SymbolLookupFlags::WeaklyReferencedSymbol}); + } + DEBUG_WITH_TYPE("orc", { + dbgs() << "Compound lookup:\n"; + for (auto &R : Request) { + dbgs() << " In " << formatv("{0:x16}", R.Handle) << ": {"; + bool First = true; + for (auto &KV : R.Symbols) { + dbgs() << (First ? "" : ",") << " " << *KV.first; + First = false; + } + dbgs() << " }\n"; + } + }); + return EP.template callB(RR); + } + + Expected runAsMain(JITTargetAddress MainFnAddr, + ArrayRef Args) override { + DEBUG_WITH_TYPE("orc", { + dbgs() << "Running as main: " << formatv("{0:x16}", MainFnAddr) + << ", args = ["; + for (unsigned I = 0; I != Args.size(); ++I) + dbgs() << (I ? "," : "") << " \"" << Args[I] << "\""; + dbgs() << "]\n"; + }); + auto Result = EP.template callB(MainFnAddr, Args); + DEBUG_WITH_TYPE("orc", { + dbgs() << " call to " << formatv("{0:x16}", MainFnAddr); + if (Result) + dbgs() << " returned result " << *Result << "\n"; + else + dbgs() << " failed\n"; + }); + return Result; + } + + Expected + runWrapper(JITTargetAddress WrapperFnAddr, + ArrayRef ArgBuffer) override { + DEBUG_WITH_TYPE("orc", { + dbgs() << "Running as wrapper function " + << formatv("{0:x16}", WrapperFnAddr) << " with " + << formatv("{0:x16}", ArgBuffer.size()) << " argument buffer\n"; + }); + auto Result = + EP.template callB(WrapperFnAddr, ArgBuffer); + // dbgs() << "Returned from runWrapper...\n"; + return Result; + } + + Error closeConnection(OnCloseConnectionFunction OnCloseConnection) { + DEBUG_WITH_TYPE("orc", dbgs() << "Closing connection to remote\n"); + return EP.template callAsync( + std::move(OnCloseConnection)); + } + + Error closeConnectionAndWait() { + std::promise P; + auto F = P.get_future(); + if (auto Err = closeConnection([&](Error Err2) -> Error { + P.set_value(std::move(Err2)); + return Error::success(); + })) { + EP.abandonAllPendingResponses(); + return joinErrors(std::move(Err), F.get()); + } + return F.get(); + } + +protected: + /// Subclasses must call this during construction to initialize the + /// TargetTriple and PageSize members. + Error initializeORCRPCTPCBase() { + if (auto TripleOrErr = EP.template callB()) + TargetTriple = Triple(*TripleOrErr); + else + return TripleOrErr.takeError(); + + if (auto PageSizeOrErr = EP.template callB()) + PageSize = *PageSizeOrErr; + else + return PageSizeOrErr.takeError(); + + return Error::success(); + } + +private: + ErrorReporter ReportError; + RPCEndpointT &EP; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h b/llvm/include/llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h --- a/llvm/include/llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h @@ -15,6 +15,9 @@ #include "llvm/ExecutionEngine/Orc/RPC/RawByteChannel.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #else @@ -31,11 +34,13 @@ FDRawByteChannel(int InFD, int OutFD) : InFD(InFD), OutFD(OutFD) {} llvm::Error readBytes(char *Dst, unsigned Size) override { + // dbgs() << "Reading " << Size << " bytes: ["; assert(Dst && "Attempt to read into null."); ssize_t Completed = 0; while (Completed < static_cast(Size)) { ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed); if (Read <= 0) { + // dbgs() << " <<<\n"; auto ErrNo = errno; if (ErrNo == EAGAIN || ErrNo == EINTR) continue; @@ -43,17 +48,22 @@ return llvm::errorCodeToError( std::error_code(errno, std::generic_category())); } + // for (size_t I = 0; I != Read; ++I) + // dbgs() << " " << formatv("{0:x2}", Dst[Completed + I]); Completed += Read; } + // dbgs() << " ]\n"; return llvm::Error::success(); } llvm::Error appendBytes(const char *Src, unsigned Size) override { + // dbgs() << "Appending " << Size << " bytes: ["; assert(Src && "Attempt to append from null."); ssize_t Completed = 0; while (Completed < static_cast(Size)) { ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed); if (Written < 0) { + // dbgs() << " <<<\n"; auto ErrNo = errno; if (ErrNo == EAGAIN || ErrNo == EINTR) continue; @@ -61,8 +71,11 @@ return llvm::errorCodeToError( std::error_code(errno, std::generic_category())); } + // for (size_t I = 0; I != Written; ++I) + // dbgs() << " " << formatv("{0:x2}", Src[Completed + I]); Completed += Written; } + // dbgs() << " ]\n"; return llvm::Error::success(); } diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RPC/RPCSerialization.h b/llvm/include/llvm/ExecutionEngine/Orc/RPC/RPCSerialization.h --- a/llvm/include/llvm/ExecutionEngine/Orc/RPC/RPCSerialization.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/RPC/RPCSerialization.h @@ -9,6 +9,8 @@ #ifndef LLVM_EXECUTIONENGINE_ORC_RPC_RPCSERIALIZATION_H #define LLVM_EXECUTIONENGINE_ORC_RPC_RPCSERIALIZATION_H +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/Support/thread.h" #include @@ -166,6 +168,19 @@ } }; +template class RPCTypeName> { +public: + static const char *getName() { + static std::string Name = [] { + std::string Name; + raw_string_ostream(Name) + << "Optional<" << RPCTypeName::getName() << ">"; + return Name; + }(); + return Name.data(); + } +}; + template class RPCTypeName> { public: @@ -574,6 +589,31 @@ } }; +template +class SerializationTraits> { +public: + /// Serialize an Optional. + static Error serialize(ChannelT &C, const Optional &O) { + if (auto Err = serializeSeq(C, O != None)) + return Err; + if (O) + if (auto Err = serializeSeq(C, *O)) + return Err; + return Error::success(); + } + + /// Deserialize an Optional. + static Error deserialize(ChannelT &C, Optional &O) { + bool HasValue = false; + if (auto Err = deserializeSeq(C, HasValue)) + return Err; + if (HasValue) + if (auto Err = deserializeSeq(C, *O)) + return Err; + return Error::success(); + }; +}; + /// SerializationTraits default specialization for std::vector. template class SerializationTraits> { @@ -609,6 +649,22 @@ } }; +/// Enable vector serialization from an ArrayRef. +template +class SerializationTraits, ArrayRef> { +public: + static Error serialize(ChannelT &C, ArrayRef V) { + if (auto Err = serializeSeq(C, static_cast(V.size()))) + return Err; + + for (const auto &E : V) + if (auto Err = serializeSeq(C, E)) + return Err; + + return Error::success(); + } +}; + template class SerializationTraits, std::set> { public: @@ -695,6 +751,55 @@ } }; +template +class SerializationTraits, DenseMap> { +public: + /// Serialize a std::map from DenseMap. + static Error serialize(ChannelT &C, const DenseMap &M) { + if (auto Err = serializeSeq(C, static_cast(M.size()))) + return Err; + + for (auto &E : M) { + if (auto Err = + SerializationTraits::serialize(C, E.first)) + return Err; + + if (auto Err = + SerializationTraits::serialize(C, E.second)) + return Err; + } + + return Error::success(); + } + + /// Serialize a std::map from DenseMap. + static Error deserialize(ChannelT &C, DenseMap &M) { + assert(M.empty() && "Expected default-constructed map to deserialize into"); + + uint64_t Count = 0; + if (auto Err = deserializeSeq(C, Count)) + return Err; + + while (Count-- != 0) { + std::pair Val; + if (auto Err = + SerializationTraits::deserialize(C, Val.first)) + return Err; + + if (auto Err = + SerializationTraits::deserialize(C, Val.second)) + return Err; + + auto Added = M.insert(Val).second; + if (!Added) + return make_error("Duplicate element in deserialized map", + orcError(OrcErrorCode::UnknownORCError)); + } + + return Error::success(); + } +}; + } // end namespace rpc } // end namespace orc } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h @@ -0,0 +1,174 @@ +//===--- TargetProcessControlTypes.h -- Shared Core/TPC types ---*- 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 +// +//===----------------------------------------------------------------------===// +// +// TargetProcessControl types that are used by both the Orc and +// OrcTargetProcess libraries. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_TARGETPROCESSCONTROLTYPES_H +#define LLVM_EXECUTIONENGINE_ORC_SHARED_TARGETPROCESSCONTROLTYPES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/Core.h" + +#include + +namespace llvm { +namespace orc { +namespace tpctypes { + +template struct UIntWrite { + UIntWrite() = default; + UIntWrite(JITTargetAddress Address, T Value) + : Address(Address), Value(Value) {} + + JITTargetAddress Address = 0; + T Value = 0; +}; + +/// Describes a write to a uint8_t. +using UInt8Write = UIntWrite; + +/// Describes a write to a uint16_t. +using UInt16Write = UIntWrite; + +/// Describes a write to a uint32_t. +using UInt32Write = UIntWrite; + +/// Describes a write to a uint64_t. +using UInt64Write = UIntWrite; + +/// Describes a write to a buffer. +/// For use with TargetProcessControl::MemoryAccess objects. +struct BufferWrite { + BufferWrite() = default; + BufferWrite(JITTargetAddress Address, StringRef Buffer) + : Address(Address), Buffer(Buffer) {} + + JITTargetAddress Address = 0; + StringRef Buffer; +}; + +/// A handle used to represent a loaded dylib in the target process. +using DylibHandle = JITTargetAddress; + +/// A pair of a dylib and a set of symbols to be looked up. +struct LookupRequest { + LookupRequest(DylibHandle Handle, const SymbolLookupSet &Symbols) + : Handle(Handle), Symbols(Symbols) {} + DylibHandle Handle; + const SymbolLookupSet &Symbols; +}; + +using LookupResult = std::vector; + +/// Either a uint8_t array or a uint8_t*. +union CWrapperFunctionResultData { + uint8_t Value[8]; + uint8_t *ValuePtr; +}; + +/// C ABI compatible wrapper function result. +/// +/// This can be safely returned from extern "C" functions, but should be used +/// to construct a WrapperFunctionResult for safety. +struct CWrapperFunctionResult { + uint64_t Size; + CWrapperFunctionResultData Data; + void (*Destroy)(CWrapperFunctionResultData Data, uint64_t Size); +}; + +/// C++ wrapper function result: Same as CWrapperFunctionResult but +/// auto-releases memory. +class WrapperFunctionResult { +public: + /// Create a default WrapperFunctionResult. + WrapperFunctionResult() { zeroInit(R); } + + /// Create a WrapperFunctionResult from a CWrapperFunctionResult. This + /// instance takes ownership of the result object and will automatically + /// call the Destroy member upon destruction. + WrapperFunctionResult(CWrapperFunctionResult R) : R(R) {} + + WrapperFunctionResult(const WrapperFunctionResult &) = delete; + WrapperFunctionResult &operator=(const WrapperFunctionResult &) = delete; + + WrapperFunctionResult(WrapperFunctionResult &&Other) { + zeroInit(R); + std::swap(R, Other.R); + } + + WrapperFunctionResult &operator=(WrapperFunctionResult &&Other) { + CWrapperFunctionResult Tmp; + zeroInit(Tmp); + std::swap(Tmp, Other.R); + std::swap(R, Tmp); + return *this; + } + + ~WrapperFunctionResult() { + if (R.Destroy) + R.Destroy(R.Data, R.Size); + } + + /// Relinquish ownership of and return the CWrapperFunctionResult. + CWrapperFunctionResult release() { + CWrapperFunctionResult Tmp; + zeroInit(Tmp); + std::swap(R, Tmp); + return Tmp; + } + + /// Get an ArrayRef covering the data in the result. + ArrayRef getData() const { + if (R.Size <= 8) + return ArrayRef(R.Data.Value, R.Size); + return ArrayRef(R.Data.ValuePtr, R.Size); + } + + /// Create a WrapperFunctionResult from the given integer, provided its + /// size is no greater than 64 bits. + template ::value && + sizeof(T) <= sizeof(uint64_t)>> + static WrapperFunctionResult from(T Value) { + CWrapperFunctionResult R; + R.Size = sizeof(T); + memcpy(&R.Data.Value, Value, R.Size); + R.Destroy = nullptr; + return R; + } + + /// Create a WrapperFunctionResult from the given string. + static WrapperFunctionResult from(StringRef S); + + /// Always free Data.ValuePtr by calling free on it. + static void destroyWithFree(CWrapperFunctionResultData Data, uint64_t Size); + + /// Always free Data.ValuePtr by calling delete[] on it. + static void destroyWithDeleteArray(CWrapperFunctionResultData Data, + uint64_t Size); + +private: + void zeroInit(CWrapperFunctionResult &R) { + R.Size = 0; + R.Data.ValuePtr = nullptr; + R.Destroy = nullptr; + } + + CWrapperFunctionResult R; +}; + +} // end namespace tpctypes +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_TARGETPROCESSCONTROLTYPES_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h b/llvm/include/llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h --- a/llvm/include/llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h @@ -31,7 +31,7 @@ /// will be searched for. If the predicate is not given then all symbols will /// be searched for. TPCDynamicLibrarySearchGenerator(TargetProcessControl &TPC, - TargetProcessControl::DylibHandle H, + tpctypes::DylibHandle H, SymbolPredicate Allow = SymbolPredicate()) : TPC(TPC), H(H), Allow(std::move(Allow)) {} @@ -56,7 +56,7 @@ private: TargetProcessControl &TPC; - TargetProcessControl::DylibHandle H; + tpctypes::DylibHandle H; SymbolPredicate Allow; }; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h b/llvm/include/llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h @@ -0,0 +1,54 @@ +//===-- TPCEHFrameRegistrar.h - TPC based eh-frame registration -*- 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 +// +//===----------------------------------------------------------------------===// +// +// TargetProcessControl based eh-frame registration. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TPCEHFRAMEREGISTRAR_H +#define LLVM_EXECUTIONENGINE_ORC_TPCEHFRAMEREGISTRAR_H + +#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" +#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" + +namespace llvm { +namespace orc { + +/// Register/Deregisters EH frames in a remote process via a +/// TargetProcessControl instance. +class TPCEHFrameRegistrar : public jitlink::EHFrameRegistrar { +public: + /// Create from a TargetProcessControl instance alone. This will use + /// the TPC's lookupSymbols method to find the registration/deregistration + /// funciton addresses by name. + static Expected> + Create(TargetProcessControl &TPC); + + /// Create a TPCEHFrameRegistrar with the given TargetProcessControl + /// object and registration/deregistration function addresses. + TPCEHFrameRegistrar(TargetProcessControl &TPC, + JITTargetAddress RegisterEHFrameWrapperFnAddr, + JITTargetAddress DeregisterEHFRameWrapperFnAddr) + : TPC(TPC), RegisterEHFrameWrapperFnAddr(RegisterEHFrameWrapperFnAddr), + DeregisterEHFrameWrapperFnAddr(DeregisterEHFRameWrapperFnAddr) {} + + Error registerEHFrames(JITTargetAddress EHFrameSectionAddr, + size_t EHFrameSectionSize) override; + Error deregisterEHFrames(JITTargetAddress EHFrameSectionAddr, + size_t EHFrameSectionSize) override; + +private: + TargetProcessControl &TPC; + JITTargetAddress RegisterEHFrameWrapperFnAddr; + JITTargetAddress DeregisterEHFrameWrapperFnAddr; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_TPCEHFRAMEREGISTRAR_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h @@ -0,0 +1,614 @@ +//===-- OrcRPCTPCServer.h -- OrcRPCTargetProcessControl Server --*- 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 +// +//===----------------------------------------------------------------------===// +// +// OrcRPCTargetProcessControl server class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H +#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ExecutionEngine/Orc/RPC/RPCUtils.h" +#include "llvm/ExecutionEngine/Orc/RPC/RawByteChannel.h" +#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Process.h" + +#include + +namespace llvm { +namespace orc { + +namespace orcrpctpc { + +enum WireProtectionFlags : uint8_t { + WPF_None = 0, + WPF_Read = 1U << 0, + WPF_Write = 1U << 1, + WPF_Exec = 1U << 2, + LLVM_MARK_AS_BITMASK_ENUM(WPF_Exec) +}; + +/// Convert from sys::Memory::ProtectionFlags +inline WireProtectionFlags +toWireProtectionFlags(sys::Memory::ProtectionFlags PF) { + WireProtectionFlags WPF = WPF_None; + if (PF & sys::Memory::MF_READ) + WPF |= WPF_Read; + if (PF & sys::Memory::MF_WRITE) + WPF |= WPF_Write; + if (PF & sys::Memory::MF_EXEC) + WPF |= WPF_Exec; + return WPF; +} + +inline sys::Memory::ProtectionFlags +fromWireProtectionFlags(WireProtectionFlags WPF) { + int PF = 0; + if (WPF & WPF_Read) + PF |= sys::Memory::MF_READ; + if (WPF & WPF_Write) + PF |= sys::Memory::MF_WRITE; + if (WPF & WPF_Exec) + PF |= sys::Memory::MF_EXEC; + return static_cast(PF); +} + +struct ReserveMemRequestElement { + WireProtectionFlags Prot = WPF_None; + uint64_t Size = 0; + uint64_t Alignment = 0; +}; + +using ReserveMemRequest = std::vector; + +struct ReserveMemResultElement { + WireProtectionFlags Prot = WPF_None; + JITTargetAddress Address = 0; + uint64_t AllocatedSize = 0; +}; + +using ReserveMemResult = std::vector; + +struct ReleaseOrFinalizeMemRequestElement { + WireProtectionFlags Prot = WPF_None; + JITTargetAddress Address = 0; + uint64_t Size = 0; +}; + +using ReleaseOrFinalizeMemRequest = + std::vector; + +} // end namespace orcrpctpc + +namespace rpc { + +template <> class RPCTypeName { +public: + static const char *getName() { return "UInt8Write"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "UInt16Write"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "UInt32Write"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "UInt64Write"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "BufferWrite"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "ReserveMemRequestElement"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "ReserveMemResultElement"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; } +}; + +template <> class RPCTypeName { +public: + static const char *getName() { return "WrapperFunctionResult"; } +}; + +template +class SerializationTraits< + ChannelT, WriteT, WriteT, + std::enable_if_t::value || + std::is_same::value || + std::is_same::value || + std::is_same::value>> { +public: + static Error serialize(ChannelT &C, const WriteT &W) { + return serializeSeq(C, W.Address, W.Value); + } + static Error deserialize(ChannelT &C, WriteT &W) { + return deserializeSeq(C, W.Address, W.Value); + } +}; + +template +class SerializationTraits< + ChannelT, tpctypes::BufferWrite, tpctypes::BufferWrite, + std::enable_if_t::value>> { +public: + static Error serialize(ChannelT &C, const tpctypes::BufferWrite &W) { + uint64_t Size = W.Buffer.size(); + if (auto Err = serializeSeq(C, W.Address, Size)) + return Err; + + return C.appendBytes(W.Buffer.data(), Size); + } + static Error deserialize(ChannelT &C, tpctypes::BufferWrite &W) { + JITTargetAddress Address; + uint64_t Size; + + if (auto Err = deserializeSeq(C, Address, Size)) + return Err; + + char *Buffer = jitTargetAddressToPointer(Address); + + if (auto Err = C.readBytes(Buffer, Size)) + return Err; + + W = {Address, StringRef(Buffer, Size)}; + return Error::success(); + } +}; + +template +class SerializationTraits { +public: + static Error serialize(ChannelT &C, + const orcrpctpc::ReserveMemRequestElement &E) { + return serializeSeq(C, static_cast(E.Prot), E.Size, E.Alignment); + } + + static Error deserialize(ChannelT &C, + orcrpctpc::ReserveMemRequestElement &E) { + return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Size, + E.Alignment); + } +}; + +template +class SerializationTraits { +public: + static Error serialize(ChannelT &C, + const orcrpctpc::ReserveMemResultElement &E) { + return serializeSeq(C, static_cast(E.Prot), E.Address, + E.AllocatedSize); + } + + static Error deserialize(ChannelT &C, orcrpctpc::ReserveMemResultElement &E) { + return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Address, + E.AllocatedSize); + } +}; + +template +class SerializationTraits { +public: + static Error + serialize(ChannelT &C, + const orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) { + return serializeSeq(C, static_cast(E.Prot), E.Address, E.Size); + } + + static Error deserialize(ChannelT &C, + orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) { + return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Address, + E.Size); + } +}; + +template +class SerializationTraits< + ChannelT, tpctypes::WrapperFunctionResult, tpctypes::WrapperFunctionResult, + std::enable_if_t::value>> { +public: + static Error serialize(ChannelT &C, + const tpctypes::WrapperFunctionResult &E) { + auto Data = E.getData(); + if (auto Err = serializeSeq(C, static_cast(Data.size()))) + return Err; + if (Data.size() == 0) + return Error::success(); + return C.appendBytes(reinterpret_cast(Data.data()), + Data.size()); + } + + static Error deserialize(ChannelT &C, tpctypes::WrapperFunctionResult &E) { + tpctypes::CWrapperFunctionResult R; + + R.Size = 0; + R.Data.ValuePtr = nullptr; + R.Destroy = nullptr; + + if (auto Err = deserializeSeq(C, R.Size)) + return Err; + if (R.Size == 0) + return Error::success(); + R.Data.ValuePtr = new uint8_t[R.Size]; + if (auto Err = + C.readBytes(reinterpret_cast(R.Data.ValuePtr), R.Size)) { + R.Destroy = tpctypes::WrapperFunctionResult::destroyWithDeleteArray; + return Err; + } + + E = tpctypes::WrapperFunctionResult(R); + return Error::success(); + } +}; + +} // end namespace rpc + +namespace orcrpctpc { + +using RemoteSymbolLookupSet = std::vector>; +using RemoteLookupRequest = + std::pair; + +class GetTargetTriple : public rpc::Function { +public: + static const char *getName() { return "GetTargetTriple"; } +}; + +class GetPageSize : public rpc::Function { +public: + static const char *getName() { return "GetPageSize"; } +}; + +class ReserveMem : public rpc::Function( + ReserveMemRequest)> { +public: + static const char *getName() { return "ReserveMem"; } +}; + +class FinalizeMem + : public rpc::Function { +public: + static const char *getName() { return "FinalizeMem"; } +}; + +class ReleaseMem + : public rpc::Function { +public: + static const char *getName() { return "ReleaseMem"; } +}; + +class WriteUInt8s + : public rpc::Function)> { +public: + static const char *getName() { return "WriteUInt8s"; } +}; + +class WriteUInt16s + : public rpc::Function)> { +public: + static const char *getName() { return "WriteUInt16s"; } +}; + +class WriteUInt32s + : public rpc::Function)> { +public: + static const char *getName() { return "WriteUInt32s"; } +}; + +class WriteUInt64s + : public rpc::Function)> { +public: + static const char *getName() { return "WriteUInt64s"; } +}; + +class WriteBuffers + : public rpc::Function)> { +public: + static const char *getName() { return "WriteBuffers"; } +}; + +class LoadDylib + : public rpc::Function( + std::string DylibPath)> { +public: + static const char *getName() { return "LoadDylib"; } +}; + +class LookupSymbols + : public rpc::Function>( + std::vector)> { +public: + static const char *getName() { return "LookupSymbols"; } +}; + +class RunMain + : public rpc::Function Args)> { +public: + static const char *getName() { return "RunMain"; } +}; + +class RunWrapper + : public rpc::Function)> { +public: + static const char *getName() { return "RunWrapper"; } +}; + +class CloseConnection : public rpc::Function { +public: + static const char *getName() { return "CloseConnection"; } +}; + +} // end namespace orcrpctpc + +/// TargetProcessControl for a process connected via an ORC RPC Endpoint. +template class OrcRPCTPCServer { +public: + /// Create an OrcRPCTPCServer from the given endpoint. + OrcRPCTPCServer(RPCEndpointT &EP) : EP(EP) { + using ThisT = OrcRPCTPCServer; + + TripleStr = sys::getProcessTriple(); + PageSize = sys::Process::getPageSizeEstimate(); + + EP.template addHandler(*this, + &ThisT::getTargetTriple); + EP.template addHandler(*this, &ThisT::getPageSize); + + EP.template addHandler(*this, &ThisT::reserveMemory); + EP.template addHandler(*this, + &ThisT::finalizeMemory); + EP.template addHandler(*this, &ThisT::releaseMemory); + + EP.template addHandler( + handleWriteUInt); + EP.template addHandler( + handleWriteUInt); + EP.template addHandler( + handleWriteUInt); + EP.template addHandler( + handleWriteUInt); + EP.template addHandler(handleWriteBuffer); + + EP.template addHandler(*this, &ThisT::loadDylib); + EP.template addHandler(*this, + &ThisT::lookupSymbols); + + EP.template addHandler(*this, &ThisT::runMain); + EP.template addHandler(*this, &ThisT::runWrapper); + + EP.template addHandler(*this, + &ThisT::closeConnection); + } + + /// Set the ProgramName to be used as the first argv element when running + /// functions via runAsMain. + void setProgramName(Optional ProgramName = None) { + this->ProgramName = std::move(ProgramName); + } + + /// Get the RPC endpoint for this server. + RPCEndpointT &getEndpoint() { return EP; } + + /// Run the server loop. + Error run() { + while (!Finished) { + if (auto Err = EP.handleOne()) + return Err; + } + return Error::success(); + } + +private: + std::string getTargetTriple() { return TripleStr; } + uint64_t getPageSize() { return PageSize; } + + template + static void handleWriteUInt(const std::vector &Ws) { + using ValueT = decltype(std::declval().Value); + for (auto &W : Ws) + *jitTargetAddressToPointer(W.Address) = W.Value; + } + + std::string getProtStr(orcrpctpc::WireProtectionFlags WPF) { + std::string Result; + Result += (WPF & orcrpctpc::WPF_Read) ? 'R' : '-'; + Result += (WPF & orcrpctpc::WPF_Write) ? 'W' : '-'; + Result += (WPF & orcrpctpc::WPF_Exec) ? 'X' : '-'; + return Result; + } + + static void handleWriteBuffer(const std::vector &Ws) { + for (auto &W : Ws) { + memcpy(jitTargetAddressToPointer(W.Address), W.Buffer.data(), + W.Buffer.size()); + } + } + + Expected + reserveMemory(const orcrpctpc::ReserveMemRequest &Request) { + orcrpctpc::ReserveMemResult Allocs; + auto PF = sys::Memory::MF_READ | sys::Memory::MF_WRITE; + + uint64_t TotalSize = 0; + + for (const auto &E : Request) { + uint64_t Size = alignTo(E.Size, PageSize); + uint16_t Align = E.Alignment; + + if ((Align > PageSize) || (PageSize % Align)) + return make_error( + "Page alignmen does not satisfy requested alignment", + inconvertibleErrorCode()); + + TotalSize += Size; + } + + // Allocate memory slab. + std::error_code EC; + auto MB = sys::Memory::allocateMappedMemory(TotalSize, nullptr, PF, EC); + if (EC) + return make_error("Unable to allocate memory: " + + EC.message(), + inconvertibleErrorCode()); + + // Zero-fill the whole thing. + memset(MB.base(), 0, MB.allocatedSize()); + + // Carve up sections to return. + uint64_t SectionBase = 0; + for (const auto &E : Request) { + uint64_t SectionSize = alignTo(E.Size, PageSize); + Allocs.push_back({E.Prot, + pointerToJITTargetAddress(MB.base()) + SectionBase, + SectionSize}); + SectionBase += SectionSize; + } + + return Allocs; + } + + Error finalizeMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &FMR) { + for (const auto &E : FMR) { + sys::MemoryBlock MB(jitTargetAddressToPointer(E.Address), E.Size); + + auto PF = orcrpctpc::fromWireProtectionFlags(E.Prot); + if (auto EC = + sys::Memory::protectMappedMemory(MB, static_cast(PF))) + return make_error("error protecting memory: " + + EC.message(), + inconvertibleErrorCode()); + } + return Error::success(); + } + + Error releaseMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &RMR) { + for (const auto &E : RMR) { + sys::MemoryBlock MB(jitTargetAddressToPointer(E.Address), E.Size); + + if (auto EC = sys::Memory::releaseMappedMemory(MB)) + return make_error("error release memory: " + EC.message(), + inconvertibleErrorCode()); + } + return Error::success(); + } + + Expected loadDylib(const std::string &Path) { + std::string ErrMsg; + const char *DLPath = !Path.empty() ? Path.c_str() : nullptr; + auto DL = sys::DynamicLibrary::getPermanentLibrary(DLPath, &ErrMsg); + if (!DL.isValid()) + return make_error(std::move(ErrMsg), + inconvertibleErrorCode()); + + tpctypes::DylibHandle H = Dylibs.size(); + Dylibs[H] = std::move(DL); + return H; + } + + Expected> + lookupSymbols(const std::vector &Request) { + std::vector Result; + + for (const auto &E : Request) { + auto I = Dylibs.find(E.first); + if (I == Dylibs.end()) + return make_error("Unrecognized handle", + inconvertibleErrorCode()); + auto &DL = I->second; + Result.push_back({}); + + for (const auto &KV : E.second) { + auto &SymString = KV.first; + bool WeakReference = KV.second; + + const char *Sym = SymString.c_str(); +#ifdef __APPLE__ + if (*Sym == '_') + ++Sym; +#endif + + void *Addr = DL.getAddressOfSymbol(Sym); + if (!Addr && !WeakReference) + return make_error(Twine("Missing definition for ") + Sym, + inconvertibleErrorCode()); + + Result.back().push_back(pointerToJITTargetAddress(Addr)); + } + } + + return Result; + } + + int32_t runMain(JITTargetAddress MainFnAddr, + const std::vector &Args) { + Optional ProgramNameOverride; + if (ProgramName) + ProgramNameOverride = *ProgramName; + + return runAsMain( + jitTargetAddressToFunction(MainFnAddr), Args, + ProgramNameOverride); + } + + tpctypes::WrapperFunctionResult + runWrapper(JITTargetAddress WrapperFnAddr, + const std::vector &ArgBuffer) { + using WrapperFnTy = tpctypes::CWrapperFunctionResult (*)( + const uint8_t *Data, uint64_t Size); + auto *WrapperFn = jitTargetAddressToFunction(WrapperFnAddr); + return WrapperFn(ArgBuffer.data(), ArgBuffer.size()); + } + + void closeConnection() { Finished = true; } + + std::string TripleStr; + uint64_t PageSize = 0; + Optional ProgramName; + RPCEndpointT &EP; + std::atomic Finished{false}; + DenseMap Dylibs; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h @@ -0,0 +1,41 @@ +//===----- RegisterEHFrames.h -- Register EH frame sections -----*- 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 +// +//===----------------------------------------------------------------------===// +// +// Support for dynamically registering and deregistering eh-frame sections +// in-process via libunwind. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H +#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H + +#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace orc { + +/// Register frames in the given eh-frame section with libunwind. +Error registerEHFrameSection(const void *EHFrameSectionAddr, + size_t EHFrameSectionSize); + +/// Unregister frames in the given eh-frame section with libunwind. +Error deregisterEHFrameSection(const void *EHFrameSectionAddr, + size_t EHFrameSectionSize); + +} // end namespace orc +} // end namespace llvm + +extern "C" llvm::orc::tpctypes::CWrapperFunctionResult +llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size); + +extern "C" llvm::orc::tpctypes::CWrapperFunctionResult +llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size); + +#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h @@ -0,0 +1,38 @@ +//===-- TargetExecutionUtils.h - Utils for execution in target --*- 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 +// +//===----------------------------------------------------------------------===// +// +// Utilities for execution in the target process. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_TARGETEXECUTIONUTILS_H +#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_TARGETEXECUTIONUTILS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { +namespace orc { + +/// Run a main function, returning the result. +/// +/// If the optional ProgramName argument is given then it will be inserted +/// before the strings in Args as the first argument to the called function. +/// +/// It is legal to have an empty argument list and no program name, however +/// many main functions will expect a name argument at least, and will fail +/// if none is provided. +int runAsMain(int (*Main)(int, char *[]), ArrayRef Args, + Optional ProgramName = None); + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_TARGETEXECUTIONUTILS_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcessControl.h --- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcessControl.h @@ -18,6 +18,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" #include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/MSVCErrorWorkarounds.h" @@ -33,76 +34,55 @@ /// APIs for manipulating memory in the target process. class MemoryAccess { public: - template struct UIntWrite { - UIntWrite() = default; - UIntWrite(JITTargetAddress Address, T Value) - : Address(Address), Value(Value) {} - - JITTargetAddress Address = 0; - T Value = 0; - }; - - using UInt8Write = UIntWrite; - using UInt16Write = UIntWrite; - using UInt32Write = UIntWrite; - using UInt64Write = UIntWrite; - - struct BufferWrite { - BufferWrite(JITTargetAddress Address, StringRef Buffer) - : Address(Address), Buffer(Buffer) {} - - JITTargetAddress Address = 0; - StringRef Buffer; - }; - + /// Callback function for asynchronous writes. using WriteResultFn = unique_function; virtual ~MemoryAccess(); - virtual void writeUInt8s(ArrayRef Ws, + virtual void writeUInt8s(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt16s(ArrayRef Ws, + virtual void writeUInt16s(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt32s(ArrayRef Ws, + virtual void writeUInt32s(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt64s(ArrayRef Ws, + virtual void writeUInt64s(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; - virtual void writeBuffers(ArrayRef Ws, + virtual void writeBuffers(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; - Error writeUInt8s(ArrayRef Ws) { + Error writeUInt8s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); writeUInt8s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } - Error writeUInt16s(ArrayRef Ws) { + Error writeUInt16s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); writeUInt16s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } - Error writeUInt32s(ArrayRef Ws) { + Error writeUInt32s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); writeUInt32s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } - Error writeUInt64s(ArrayRef Ws) { + Error writeUInt64s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); writeUInt64s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } - Error writeBuffers(ArrayRef Ws) { + Error writeBuffers(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); writeBuffers(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); @@ -110,43 +90,30 @@ } }; - /// A handle for a library opened via loadDylib. - /// - /// Note that this handle does not necessarily represent a JITDylib: it may - /// be a regular dynamic library or shared object (e.g. one opened via a - /// dlopen in the target process). - using DylibHandle = JITTargetAddress; - - /// Request lookup within the given DylibHandle. - struct LookupRequestElement { - LookupRequestElement(DylibHandle Handle, const SymbolLookupSet &Symbols) - : Handle(Handle), Symbols(Symbols) {} - DylibHandle Handle; - const SymbolLookupSet &Symbols; - }; - - using LookupRequest = ArrayRef; + virtual ~TargetProcessControl(); - using LookupResult = std::vector>; + /// Intern a symbol name in the SymbolStringPool. + SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); } - virtual ~TargetProcessControl(); + /// Return a shared pointer to the SymbolStringPool for this instance. + std::shared_ptr getSymbolStringPool() const { return SSP; } /// Return the Triple for the target process. - const Triple &getTargetTriple() const { return TT; } + const Triple &getTargetTriple() const { return TargetTriple; } /// Get the page size for the target process. unsigned getPageSize() const { return PageSize; } - /// Return a JITLinkMemoryManager for the target process. - jitlink::JITLinkMemoryManager &getMemMgr() const { return *MemMgr; } - /// Return a MemoryAccess object for the target process. MemoryAccess &getMemoryAccess() const { return *MemAccess; } + /// Return a JITLinkMemoryManager for the target process. + jitlink::JITLinkMemoryManager &getMemMgr() const { return *MemMgr; } + /// Load the dynamic library at the given path and return a handle to it. /// If LibraryPath is null this function will return the global handle for /// the target process. - virtual Expected loadDylib(const char *DylibPath) = 0; + virtual Expected loadDylib(const char *DylibPath) = 0; /// Search for symbols in the target process. /// @@ -154,14 +121,37 @@ /// that correspond to the lookup order. If a required symbol is not /// found then this method will return an error. If a weakly referenced /// symbol is not found then it be assigned a '0' value in the result. - virtual Expected lookupSymbols(LookupRequest Request) = 0; + /// that correspond to the lookup order. + virtual Expected> + lookupSymbols(ArrayRef Request) = 0; + + /// Run function with a main-like signature. + virtual Expected runAsMain(JITTargetAddress MainFnAddr, + ArrayRef Args) = 0; + + /// Run a wrapper function with signature: + /// + /// \code{.cpp} + /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); + /// \endcode{.cpp} + /// + virtual Expected + runWrapper(JITTargetAddress WrapperFnAddr, ArrayRef ArgBuffer) = 0; + + /// Disconnect from the target process. + /// + /// This should be called after the JIT session is shut down. + virtual Error disconnect() = 0; protected: + TargetProcessControl(std::shared_ptr SSP) + : SSP(std::move(SSP)) {} - Triple TT; + std::shared_ptr SSP; + Triple TargetTriple; unsigned PageSize = 0; - jitlink::JITLinkMemoryManager *MemMgr = nullptr; MemoryAccess *MemAccess = nullptr; + jitlink::JITLinkMemoryManager *MemMgr = nullptr; }; /// A TargetProcessControl implementation targeting the current process. @@ -169,33 +159,44 @@ private TargetProcessControl::MemoryAccess { public: SelfTargetProcessControl( - Triple TT, unsigned PageSize, - std::unique_ptr MemMgr); + std::shared_ptr SSP, Triple TargetTriple, + unsigned PageSize, std::unique_ptr MemMgr); /// Create a SelfTargetProcessControl with the given memory manager. /// If no memory manager is given a jitlink::InProcessMemoryManager will /// be used by default. static Expected> - Create(std::unique_ptr MemMgr = nullptr); + Create(std::shared_ptr SSP, + std::unique_ptr MemMgr = nullptr); + + Expected loadDylib(const char *DylibPath) override; + + Expected> + lookupSymbols(ArrayRef Request) override; + + Expected runAsMain(JITTargetAddress MainFnAddr, + ArrayRef Args) override; - Expected loadDylib(const char *DylibPath) override; + Expected + runWrapper(JITTargetAddress WrapperFnAddr, + ArrayRef ArgBuffer) override; - Expected lookupSymbols(LookupRequest Request) override; + Error disconnect() override; private: - void writeUInt8s(ArrayRef Ws, + void writeUInt8s(ArrayRef Ws, WriteResultFn OnWriteComplete) override; - void writeUInt16s(ArrayRef Ws, + void writeUInt16s(ArrayRef Ws, WriteResultFn OnWriteComplete) override; - void writeUInt32s(ArrayRef Ws, + void writeUInt32s(ArrayRef Ws, WriteResultFn OnWriteComplete) override; - void writeUInt64s(ArrayRef Ws, + void writeUInt64s(ArrayRef Ws, WriteResultFn OnWriteComplete) override; - void writeBuffers(ArrayRef Ws, + void writeBuffers(ArrayRef Ws, WriteResultFn OnWriteComplete) override; std::unique_ptr OwnedMemMgr; diff --git a/llvm/lib/ExecutionEngine/CMakeLists.txt b/llvm/lib/ExecutionEngine/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/CMakeLists.txt @@ -21,7 +21,6 @@ add_subdirectory(Interpreter) add_subdirectory(JITLink) add_subdirectory(MCJIT) -add_subdirectory(OrcError) add_subdirectory(Orc) add_subdirectory(RuntimeDyld) diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -22,4 +22,6 @@ target_link_libraries(LLVMJITLink PRIVATE LLVMObject + LLVMOrcTargetProcess + LLVMSupport ) diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp --- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp @@ -11,6 +11,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Config/config.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" #include "llvm/Support/DynamicLibrary.h" #define DEBUG_TYPE "jitlink" @@ -630,142 +631,18 @@ return PC.G.addAnonymousSymbol(*B, Addr - B->getAddress(), 0, false, false); } -#if defined(HAVE_REGISTER_FRAME) && defined(HAVE_DEREGISTER_FRAME) && \ - !defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) -extern "C" void __register_frame(const void *); -extern "C" void __deregister_frame(const void *); - -Error registerFrameWrapper(const void *P) { - __register_frame(P); - return Error::success(); -} - -Error deregisterFrameWrapper(const void *P) { - __deregister_frame(P); - return Error::success(); -} - -#else - -// The building compiler does not have __(de)register_frame but -// it may be found at runtime in a dynamically-loaded library. -// For example, this happens when building LLVM with Visual C++ -// but using the MingW runtime. -static Error registerFrameWrapper(const void *P) { - static void((*RegisterFrame)(const void *)) = 0; - - if (!RegisterFrame) - *(void **)&RegisterFrame = - llvm::sys::DynamicLibrary::SearchForAddressOfSymbol("__register_frame"); - - if (RegisterFrame) { - RegisterFrame(P); - return Error::success(); - } - - return make_error("could not register eh-frame: " - "__register_frame function not found"); -} - -static Error deregisterFrameWrapper(const void *P) { - static void((*DeregisterFrame)(const void *)) = 0; - - if (!DeregisterFrame) - *(void **)&DeregisterFrame = - llvm::sys::DynamicLibrary::SearchForAddressOfSymbol( - "__deregister_frame"); - - if (DeregisterFrame) { - DeregisterFrame(P); - return Error::success(); - } - - return make_error("could not deregister eh-frame: " - "__deregister_frame function not found"); -} -#endif - -#ifdef __APPLE__ - -template -Error walkAppleEHFrameSection(const char *const SectionStart, - size_t SectionSize, - HandleFDEFn HandleFDE) { - const char *CurCFIRecord = SectionStart; - const char *End = SectionStart + SectionSize; - uint64_t Size = *reinterpret_cast(CurCFIRecord); - - while (CurCFIRecord != End && Size != 0) { - const char *OffsetField = CurCFIRecord + (Size == 0xffffffff ? 12 : 4); - if (Size == 0xffffffff) - Size = *reinterpret_cast(CurCFIRecord + 4) + 12; - else - Size += 4; - uint32_t Offset = *reinterpret_cast(OffsetField); - - LLVM_DEBUG({ - dbgs() << "Registering eh-frame section:\n"; - dbgs() << "Processing " << (Offset ? "FDE" : "CIE") << " @" - << (void *)CurCFIRecord << ": ["; - for (unsigned I = 0; I < Size; ++I) - dbgs() << format(" 0x%02" PRIx8, *(CurCFIRecord + I)); - dbgs() << " ]\n"; - }); - - if (Offset != 0) - if (auto Err = HandleFDE(CurCFIRecord)) - return Err; - - CurCFIRecord += Size; - - Size = *reinterpret_cast(CurCFIRecord); - } - - return Error::success(); -} - -#endif // __APPLE__ - -Error registerEHFrameSection(const void *EHFrameSectionAddr, - size_t EHFrameSectionSize) { -#ifdef __APPLE__ - // On Darwin __register_frame has to be called for each FDE entry. - return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), - EHFrameSectionSize, - registerFrameWrapper); -#else - // On Linux __register_frame takes a single argument: - // a pointer to the start of the .eh_frame section. - - // How can it find the end? Because crtendS.o is linked - // in and it has an .eh_frame section with four zero chars. - return registerFrameWrapper(EHFrameSectionAddr); -#endif -} - -Error deregisterEHFrameSection(const void *EHFrameSectionAddr, - size_t EHFrameSectionSize) { -#ifdef __APPLE__ - return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), - EHFrameSectionSize, - deregisterFrameWrapper); -#else - return deregisterFrameWrapper(EHFrameSectionAddr); -#endif -} - EHFrameRegistrar::~EHFrameRegistrar() {} Error InProcessEHFrameRegistrar::registerEHFrames( JITTargetAddress EHFrameSectionAddr, size_t EHFrameSectionSize) { - return registerEHFrameSection( + return orc::registerEHFrameSection( jitTargetAddressToPointer(EHFrameSectionAddr), EHFrameSectionSize); } Error InProcessEHFrameRegistrar::deregisterEHFrames( JITTargetAddress EHFrameSectionAddr, size_t EHFrameSectionSize) { - return deregisterEHFrameSection( + return orc::deregisterEHFrameSection( jitTargetAddressToPointer(EHFrameSectionAddr), EHFrameSectionSize); } diff --git a/llvm/lib/ExecutionEngine/JITLink/LLVMBuild.txt b/llvm/lib/ExecutionEngine/JITLink/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/JITLink/LLVMBuild.txt +++ b/llvm/lib/ExecutionEngine/JITLink/LLVMBuild.txt @@ -18,4 +18,4 @@ type = Library name = JITLink parent = ExecutionEngine -required_libraries = BinaryFormat Object Support +required_libraries = BinaryFormat Object OrcTargetProcess Support diff --git a/llvm/lib/ExecutionEngine/LLVMBuild.txt b/llvm/lib/ExecutionEngine/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/LLVMBuild.txt +++ b/llvm/lib/ExecutionEngine/LLVMBuild.txt @@ -16,7 +16,7 @@ [common] subdirectories = Interpreter MCJIT JITLink RuntimeDyld IntelJITEvents - OProfileJIT Orc OrcError PerfJITEvents + OProfileJIT Orc PerfJITEvents [component_0] type = Library diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -23,6 +23,7 @@ TargetProcessControl.cpp ThreadSafeModule.cpp TPCDynamicLibrarySearchGenerator.cpp + TPCEHFrameRegistrar.cpp TPCIndirectionUtils.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc @@ -31,6 +32,9 @@ intrinsics_gen ) +add_subdirectory(Shared) +add_subdirectory(TargetProcess) + target_link_libraries(LLVMOrcJIT PRIVATE LLVMAnalysis diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp --- a/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp @@ -21,32 +21,6 @@ namespace llvm { namespace orc { -int runAsMain(int (*Main)(int, char *[]), ArrayRef Args, - Optional ProgramName) { - std::vector> ArgVStorage; - std::vector ArgV; - - ArgVStorage.reserve(Args.size() + (ProgramName ? 1 : 0)); - ArgV.reserve(Args.size() + 1 + (ProgramName ? 1 : 0)); - - if (ProgramName) { - ArgVStorage.push_back(std::make_unique(ProgramName->size() + 1)); - llvm::copy(*ProgramName, &ArgVStorage.back()[0]); - ArgVStorage.back()[ProgramName->size()] = '\0'; - ArgV.push_back(ArgVStorage.back().get()); - } - - for (auto &Arg : Args) { - ArgVStorage.push_back(std::make_unique(Arg.size() + 1)); - llvm::copy(Arg, &ArgVStorage.back()[0]); - ArgVStorage.back()[Arg.size()] = '\0'; - ArgV.push_back(ArgVStorage.back().get()); - } - ArgV.push_back(nullptr); - - return Main(Args.size() + !!ProgramName, ArgV.data()); -} - CtorDtorIterator::CtorDtorIterator(const GlobalVariable *GV, bool End) : InitList( GV ? dyn_cast_or_null(GV->getInitializer()) : nullptr), diff --git a/llvm/lib/ExecutionEngine/Orc/LLVMBuild.txt b/llvm/lib/ExecutionEngine/Orc/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/Orc/LLVMBuild.txt +++ b/llvm/lib/ExecutionEngine/Orc/LLVMBuild.txt @@ -14,9 +14,12 @@ ; ;===------------------------------------------------------------------------===; +[common] +subdirectories = Shared TargetProcess + [component_0] type = Library name = OrcJIT parent = ExecutionEngine -required_libraries = Core ExecutionEngine JITLink Object OrcError MC Passes +required_libraries = Core ExecutionEngine JITLink Object OrcShared MC Passes RuntimeDyld Support Target TransformUtils diff --git a/llvm/lib/ExecutionEngine/OrcError/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt rename from llvm/lib/ExecutionEngine/OrcError/CMakeLists.txt rename to llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/OrcError/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt @@ -1,6 +1,7 @@ -add_llvm_component_library(LLVMOrcError +add_llvm_component_library(LLVMOrcShared OrcError.cpp RPCError.cpp + TargetProcessControlTypes.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc ) diff --git a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt b/llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt rename from llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt rename to llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt +++ b/llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./lib/ExecutionEngine/OrcError/LLVMBuild.txt -------------*- Conf -*--===; +;===- ./lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt ------------*- Conf -*--===; ; ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ; See https://llvm.org/LICENSE.txt for license information. @@ -16,6 +16,6 @@ [component_0] type = Library -name = OrcError -parent = ExecutionEngine +name = OrcShared +parent = OrcJIT required_libraries = Support diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt.rej b/llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt.rej new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Shared/LLVMBuild.txt.rej @@ -0,0 +1,27 @@ +*************** +*** 1,4 **** +- ;===- ./lib/ExecutionEngine/OrcError/LLVMBuild.txt -------------*- Conf -*--===; + ; + ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + ; See https://llvm.org/LICENSE.txt for license information. +--- 1,4 ---- ++ ;===- ./tools/llvm-jitlink/llvm-jitlink-executor/LLVMBuild.txt -*- Conf -*--===; + ; + ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + ; See https://llvm.org/LICENSE.txt for license information. +*************** +*** 15,21 **** + ;===------------------------------------------------------------------------===; + + [component_0] +- type = Library +- name = OrcError +- parent = ExecutionEngine +- required_libraries = Support +--- 15,20 ---- + ;===------------------------------------------------------------------------===; + + [component_0] ++ type = Tool ++ name = llvm-jitlink-executor ++ parent = llvm-jitlink diff --git a/llvm/lib/ExecutionEngine/OrcError/OrcError.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcError.cpp rename from llvm/lib/ExecutionEngine/OrcError/OrcError.cpp rename to llvm/lib/ExecutionEngine/Orc/Shared/OrcError.cpp --- a/llvm/lib/ExecutionEngine/OrcError/OrcError.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcError.cpp @@ -71,7 +71,7 @@ }; static ManagedStatic OrcErrCat; -} +} // namespace namespace llvm { namespace orc { @@ -84,9 +84,8 @@ return std::error_code(static_cast(ErrCode), *OrcErrCat); } - DuplicateDefinition::DuplicateDefinition(std::string SymbolName) - : SymbolName(std::move(SymbolName)) {} + : SymbolName(std::move(SymbolName)) {} std::error_code DuplicateDefinition::convertToErrorCode() const { return orcError(OrcErrorCode::DuplicateDefinition); @@ -101,7 +100,7 @@ } JITSymbolNotFound::JITSymbolNotFound(std::string SymbolName) - : SymbolName(std::move(SymbolName)) {} + : SymbolName(std::move(SymbolName)) {} std::error_code JITSymbolNotFound::convertToErrorCode() const { typedef std::underlying_type::type UT; @@ -117,5 +116,5 @@ return SymbolName; } -} -} +} // namespace orc +} // namespace llvm diff --git a/llvm/lib/ExecutionEngine/OrcError/RPCError.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/RPCError.cpp rename from llvm/lib/ExecutionEngine/OrcError/RPCError.cpp rename to llvm/lib/ExecutionEngine/Orc/Shared/RPCError.cpp --- a/llvm/lib/ExecutionEngine/OrcError/RPCError.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Shared/RPCError.cpp @@ -14,8 +14,8 @@ #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" -#include #include +#include char llvm::orc::rpc::RPCFatalError::ID = 0; char llvm::orc::rpc::ConnectionClosed::ID = 0; @@ -53,7 +53,6 @@ OS << "Could not negotiate RPC function " << Signature; } - } // end namespace rpc } // end namespace orc } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.cpp @@ -0,0 +1,43 @@ +//===---------- TargetProcessControlTypes.cpp - Shared TPC types ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TargetProcessControl types. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" + +namespace llvm { +namespace orc { +namespace tpctypes { + +WrapperFunctionResult WrapperFunctionResult::from(StringRef S) { + CWrapperFunctionResult R = {0, {.ValuePtr = nullptr}, nullptr}; + R.Size = S.size(); + if (R.Size > sizeof(uint64_t)) { + R.Data.ValuePtr = new uint8_t[R.Size]; + memcpy(R.Data.ValuePtr, S.data(), R.Size); + R.Destroy = destroyWithDeleteArray; + } else + memcpy(R.Data.Value, S.data(), R.Size); + return R; +} + +void WrapperFunctionResult::destroyWithFree(CWrapperFunctionResultData Data, + uint64_t Size) { + free(Data.ValuePtr); +} + +void WrapperFunctionResult::destroyWithDeleteArray( + CWrapperFunctionResultData Data, uint64_t Size) { + delete[] Data.ValuePtr; +} + +} // end namespace tpctypes +} // end namespace orc +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.cpp b/llvm/lib/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.cpp --- a/llvm/lib/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.cpp @@ -41,7 +41,7 @@ SymbolMap NewSymbols; - TargetProcessControl::LookupRequestElement Request(H, LookupSymbols); + tpctypes::LookupRequest Request(H, LookupSymbols); auto Result = TPC.lookupSymbols(Request); if (!Result) return Result.takeError(); diff --git a/llvm/lib/ExecutionEngine/Orc/TPCEHFrameRegistrar.cpp b/llvm/lib/ExecutionEngine/Orc/TPCEHFrameRegistrar.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TPCEHFrameRegistrar.cpp @@ -0,0 +1,80 @@ +//===------ TPCEHFrameRegistrar.cpp - TPC-based eh-frame registration -----===// +// +// 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 "llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h" +#include "llvm/Support/BinaryStreamWriter.h" + +namespace llvm { +namespace orc { + +Expected> +TPCEHFrameRegistrar::Create(TargetProcessControl &TPC) { + // FIXME: Proper mangling here -- we really need to decouple linker mangling + // from DataLayout. + + // Find the addresses of the registration/deregistration functions in the + // target process. + auto ProcessHandle = TPC.loadDylib(nullptr); + if (!ProcessHandle) + return ProcessHandle.takeError(); + + std::string RegisterWrapperName, DeregisterWrapperName; + if (TPC.getTargetTriple().isOSBinFormatMachO()) { + RegisterWrapperName += '_'; + DeregisterWrapperName += '_'; + } + RegisterWrapperName += "llvm_orc_registerEHFrameSectionWrapper"; + DeregisterWrapperName += "llvm_orc_deregisterEHFrameSectionWrapper"; + + SymbolLookupSet RegistrationSymbols; + RegistrationSymbols.add(TPC.intern(RegisterWrapperName)); + RegistrationSymbols.add(TPC.intern(DeregisterWrapperName)); + + auto Result = TPC.lookupSymbols({{*ProcessHandle, RegistrationSymbols}}); + if (!Result) + return Result.takeError(); + + assert(Result->size() == 1 && "Unexpected number of dylibs in result"); + assert((*Result)[0].size() == 2 && + "Unexpected number of addresses in result"); + + auto RegisterEHFrameWrapperFnAddr = (*Result)[0][0]; + auto DeregisterEHFrameWrapperFnAddr = (*Result)[0][1]; + + return std::make_unique( + TPC, RegisterEHFrameWrapperFnAddr, DeregisterEHFrameWrapperFnAddr); +} + +Error TPCEHFrameRegistrar::registerEHFrames(JITTargetAddress EHFrameSectionAddr, + size_t EHFrameSectionSize) { + constexpr size_t ArgBufferSize = sizeof(uint64_t) + sizeof(uint64_t); + uint8_t ArgBuffer[ArgBufferSize]; + BinaryStreamWriter ArgWriter( + MutableArrayRef(ArgBuffer, ArgBufferSize), + support::endianness::big); + cantFail(ArgWriter.writeInteger(static_cast(EHFrameSectionAddr))); + cantFail(ArgWriter.writeInteger(static_cast(EHFrameSectionSize))); + + return TPC.runWrapper(RegisterEHFrameWrapperFnAddr, ArgBuffer).takeError(); +} + +Error TPCEHFrameRegistrar::deregisterEHFrames( + JITTargetAddress EHFrameSectionAddr, size_t EHFrameSectionSize) { + constexpr size_t ArgBufferSize = sizeof(uint64_t) + sizeof(uint64_t); + uint8_t ArgBuffer[ArgBufferSize]; + BinaryStreamWriter ArgWriter( + MutableArrayRef(ArgBuffer, ArgBufferSize), + support::endianness::big); + cantFail(ArgWriter.writeInteger(static_cast(EHFrameSectionAddr))); + cantFail(ArgWriter.writeInteger(static_cast(EHFrameSectionSize))); + + return TPC.runWrapper(DeregisterEHFrameWrapperFnAddr, ArgBuffer).takeError(); +} + +} // end namespace orc +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/TPCIndirectionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/TPCIndirectionUtils.cpp --- a/llvm/lib/ExecutionEngine/Orc/TPCIndirectionUtils.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TPCIndirectionUtils.cpp @@ -160,7 +160,7 @@ switch (TPCIU.getABISupport().getPointerSize()) { case 4: { unsigned ASIdx = 0; - std::vector PtrUpdates; + std::vector PtrUpdates; for (auto &SI : StubInits) PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress, static_cast(SI.second.first)}); @@ -168,7 +168,7 @@ } case 8: { unsigned ASIdx = 0; - std::vector PtrUpdates; + std::vector PtrUpdates; for (auto &SI : StubInits) PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress, static_cast(SI.second.first)}); @@ -213,11 +213,11 @@ auto &MemAccess = TPCIU.getTargetProcessControl().getMemoryAccess(); switch (TPCIU.getABISupport().getPointerSize()) { case 4: { - TargetProcessControl::MemoryAccess::UInt32Write PUpdate(PtrAddr, NewAddr); + tpctypes::UInt32Write PUpdate(PtrAddr, NewAddr); return MemAccess.writeUInt32s(PUpdate); } case 8: { - TargetProcessControl::MemoryAccess::UInt64Write PUpdate(PtrAddr, NewAddr); + tpctypes::UInt64Write PUpdate(PtrAddr, NewAddr); return MemAccess.writeUInt64s(PUpdate); } default: diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_component_library(LLVMOrcTargetProcess + RegisterEHFrames.cpp + TargetExecutionUtils.cpp + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc + ) diff --git a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LLVMBuild.txt copy from llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt copy to llvm/lib/ExecutionEngine/Orc/TargetProcess/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./lib/ExecutionEngine/OrcError/LLVMBuild.txt -------------*- Conf -*--===; +;===- ./lib/ExecutionEngine/OrcTargetProcess/LLVMBuild.txt -----*- Conf -*--===; ; ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ; See https://llvm.org/LICENSE.txt for license information. @@ -16,6 +16,6 @@ [component_0] type = Library -name = OrcError -parent = ExecutionEngine -required_libraries = Support +name = OrcTargetProcess +parent = OrcJIT +required_libraries = OrcShared Support diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.cpp @@ -0,0 +1,207 @@ +//===--------- RegisterEHFrames.cpp - Register EH frame sections ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" + +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Support/FormatVariadic.h" + +#define DEBUG_TYPE "orc" + +using namespace llvm; +using namespace llvm::orc; +using namespace llvm::orc::tpctypes; + +namespace llvm { +namespace orc { + +#if defined(HAVE_REGISTER_FRAME) && defined(HAVE_DEREGISTER_FRAME) && \ + !defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) + +extern "C" void __register_frame(const void *); +extern "C" void __deregister_frame(const void *); + +Error registerFrameWrapper(const void *P) { + __register_frame(P); + return Error::success(); +} + +Error deregisterFrameWrapper(const void *P) { + __deregister_frame(P); + return Error::success(); +} + +#else + +// The building compiler does not have __(de)register_frame but +// it may be found at runtime in a dynamically-loaded library. +// For example, this happens when building LLVM with Visual C++ +// but using the MingW runtime. +static Error registerFrameWrapper(const void *P) { + static void((*RegisterFrame)(const void *)) = 0; + + if (!RegisterFrame) + *(void **)&RegisterFrame = + llvm::sys::DynamicLibrary::SearchForAddressOfSymbol("__register_frame"); + + if (RegisterFrame) { + RegisterFrame(P); + return Error::success(); + } + + return make_error("could not register eh-frame: " + "__register_frame function not found", + inconvertibleErrorCode()); +} + +static Error deregisterFrameWrapper(const void *P) { + static void((*DeregisterFrame)(const void *)) = 0; + + if (!DeregisterFrame) + *(void **)&DeregisterFrame = + llvm::sys::DynamicLibrary::SearchForAddressOfSymbol( + "__deregister_frame"); + + if (DeregisterFrame) { + DeregisterFrame(P); + return Error::success(); + } + + return make_error("could not deregister eh-frame: " + "__deregister_frame function not found", + inconvertibleErrorCode()); +} +#endif + +#ifdef __APPLE__ + +template +Error walkAppleEHFrameSection(const char *const SectionStart, + size_t SectionSize, HandleFDEFn HandleFDE) { + const char *CurCFIRecord = SectionStart; + const char *End = SectionStart + SectionSize; + uint64_t Size = *reinterpret_cast(CurCFIRecord); + + while (CurCFIRecord != End && Size != 0) { + const char *OffsetField = CurCFIRecord + (Size == 0xffffffff ? 12 : 4); + if (Size == 0xffffffff) + Size = *reinterpret_cast(CurCFIRecord + 4) + 12; + else + Size += 4; + uint32_t Offset = *reinterpret_cast(OffsetField); + + LLVM_DEBUG({ + dbgs() << "Registering eh-frame section:\n"; + dbgs() << "Processing " << (Offset ? "FDE" : "CIE") << " @" + << (void *)CurCFIRecord << ": ["; + for (unsigned I = 0; I < Size; ++I) + dbgs() << format(" 0x%02" PRIx8, *(CurCFIRecord + I)); + dbgs() << " ]\n"; + }); + + if (Offset != 0) + if (auto Err = HandleFDE(CurCFIRecord)) + return Err; + + CurCFIRecord += Size; + + Size = *reinterpret_cast(CurCFIRecord); + } + + return Error::success(); +} + +#endif // __APPLE__ + +Error registerEHFrameSection(const void *EHFrameSectionAddr, + size_t EHFrameSectionSize) { +#ifdef __APPLE__ + // On Darwin __register_frame has to be called for each FDE entry. + return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), + EHFrameSectionSize, registerFrameWrapper); +#else + // On Linux __register_frame takes a single argument: + // a pointer to the start of the .eh_frame section. + + // How can it find the end? Because crtendS.o is linked + // in and it has an .eh_frame section with four zero chars. + return registerFrameWrapper(EHFrameSectionAddr); +#endif +} + +Error deregisterEHFrameSection(const void *EHFrameSectionAddr, + size_t EHFrameSectionSize) { +#ifdef __APPLE__ + return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), + EHFrameSectionSize, deregisterFrameWrapper); +#else + return deregisterFrameWrapper(EHFrameSectionAddr); +#endif +} + +} // end namespace orc +} // end namespace llvm + +extern "C" CWrapperFunctionResult +llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) { + if (Size != sizeof(uint64_t) + sizeof(uint64_t)) + return WrapperFunctionResult::from( + "Invalid arguments to llvm_orc_registerEHFrameSectionWrapper") + .release(); + + uint64_t EHFrameSectionAddr; + uint64_t EHFrameSectionSize; + + { + BinaryStreamReader ArgReader(ArrayRef(Data, Size), + support::endianness::big); + cantFail(ArgReader.readInteger(EHFrameSectionAddr)); + cantFail(ArgReader.readInteger(EHFrameSectionSize)); + } + + if (auto Err = registerEHFrameSection( + jitTargetAddressToPointer(EHFrameSectionAddr), + EHFrameSectionSize)) { + auto ErrMsg = toString(std::move(Err)); + return WrapperFunctionResult::from(ErrMsg).release(); + } + return WrapperFunctionResult().release(); +} + +extern "C" CWrapperFunctionResult +llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) { + if (Size != sizeof(uint64_t) + sizeof(uint64_t)) + return WrapperFunctionResult::from( + "Invalid arguments to llvm_orc_registerEHFrameSectionWrapper") + .release(); + + uint64_t EHFrameSectionAddr; + uint64_t EHFrameSectionSize; + + { + BinaryStreamReader ArgReader(ArrayRef(Data, Size), + support::endianness::big); + cantFail(ArgReader.readInteger(EHFrameSectionAddr)); + cantFail(ArgReader.readInteger(EHFrameSectionSize)); + } + + if (auto Err = deregisterEHFrameSection( + jitTargetAddressToPointer(EHFrameSectionAddr), + EHFrameSectionSize)) { + auto ErrMsg = toString(std::move(Err)); + return WrapperFunctionResult::from(ErrMsg).release(); + } + return WrapperFunctionResult().release(); +} diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.cpp @@ -0,0 +1,43 @@ +//===--- TargetExecutionUtils.cpp - Execution utils for target processes --===// +// +// 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 "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" + +#include + +namespace llvm { +namespace orc { + +int runAsMain(int (*Main)(int, char *[]), ArrayRef Args, + Optional ProgramName) { + std::vector> ArgVStorage; + std::vector ArgV; + + ArgVStorage.reserve(Args.size() + (ProgramName ? 1 : 0)); + ArgV.reserve(Args.size() + 1 + (ProgramName ? 1 : 0)); + + if (ProgramName) { + ArgVStorage.push_back(std::make_unique(ProgramName->size() + 1)); + llvm::copy(*ProgramName, &ArgVStorage.back()[0]); + ArgVStorage.back()[ProgramName->size()] = '\0'; + ArgV.push_back(ArgVStorage.back().get()); + } + + for (const auto &Arg : Args) { + ArgVStorage.push_back(std::make_unique(Arg.size() + 1)); + llvm::copy(Arg, &ArgVStorage.back()[0]); + ArgVStorage.back()[Arg.size()] = '\0'; + ArgV.push_back(ArgVStorage.back().get()); + } + ArgV.push_back(nullptr); + + return Main(Args.size() + !!ProgramName, ArgV.data()); +} + +} // End namespace orc. +} // End namespace llvm. diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcessControl.cpp --- a/llvm/lib/ExecutionEngine/Orc/TargetProcessControl.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcessControl.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" #include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/Support/Host.h" #include "llvm/Support/Process.h" @@ -22,23 +23,25 @@ TargetProcessControl::~TargetProcessControl() {} SelfTargetProcessControl::SelfTargetProcessControl( - Triple TT, unsigned PageSize, - std::unique_ptr MemMgr) { + std::shared_ptr SSP, Triple TargetTriple, + unsigned PageSize, std::unique_ptr MemMgr) + : TargetProcessControl(std::move(SSP)) { OwnedMemMgr = std::move(MemMgr); if (!OwnedMemMgr) OwnedMemMgr = std::make_unique(); - this->TT = std::move(TT); + this->TargetTriple = std::move(TargetTriple); this->PageSize = PageSize; this->MemMgr = OwnedMemMgr.get(); this->MemAccess = this; - if (this->TT.isOSBinFormatMachO()) + if (this->TargetTriple.isOSBinFormatMachO()) GlobalManglingPrefix = '_'; } Expected> SelfTargetProcessControl::Create( + std::shared_ptr SSP, std::unique_ptr MemMgr) { auto PageSize = sys::Process::getPageSize(); if (!PageSize) @@ -46,11 +49,11 @@ Triple TT(sys::getProcessTriple()); - return std::make_unique(std::move(TT), *PageSize, - std::move(MemMgr)); + return std::make_unique( + std::move(SSP), std::move(TT), *PageSize, std::move(MemMgr)); } -Expected +Expected SelfTargetProcessControl::loadDylib(const char *DylibPath) { std::string ErrMsg; auto Dylib = std::make_unique( @@ -61,9 +64,10 @@ return pointerToJITTargetAddress(DynamicLibraries.back().get()); } -Expected -SelfTargetProcessControl::lookupSymbols(LookupRequest Request) { - LookupResult R; +Expected> +SelfTargetProcessControl::lookupSymbols( + ArrayRef Request) { + std::vector R; for (auto &Elem : Request) { auto *Dylib = jitTargetAddressToPointer(Elem.Handle); @@ -92,35 +96,53 @@ return R; } -void SelfTargetProcessControl::writeUInt8s(ArrayRef Ws, +Expected +SelfTargetProcessControl::runAsMain(JITTargetAddress MainFnAddr, + ArrayRef Args) { + using MainTy = int (*)(int, char *[]); + return orc::runAsMain(jitTargetAddressToFunction(MainFnAddr), Args); +} + +Expected +SelfTargetProcessControl::runWrapper(JITTargetAddress WrapperFnAddr, + ArrayRef ArgBuffer) { + using WrapperFnTy = + tpctypes::CWrapperFunctionResult (*)(const uint8_t *Data, uint64_t Size); + auto *WrapperFn = jitTargetAddressToFunction(WrapperFnAddr); + return WrapperFn(ArgBuffer.data(), ArgBuffer.size()); +}; + +Error SelfTargetProcessControl::disconnect() { return Error::success(); } + +void SelfTargetProcessControl::writeUInt8s(ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *jitTargetAddressToPointer(W.Address) = W.Value; OnWriteComplete(Error::success()); } -void SelfTargetProcessControl::writeUInt16s(ArrayRef Ws, +void SelfTargetProcessControl::writeUInt16s(ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *jitTargetAddressToPointer(W.Address) = W.Value; OnWriteComplete(Error::success()); } -void SelfTargetProcessControl::writeUInt32s(ArrayRef Ws, +void SelfTargetProcessControl::writeUInt32s(ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *jitTargetAddressToPointer(W.Address) = W.Value; OnWriteComplete(Error::success()); } -void SelfTargetProcessControl::writeUInt64s(ArrayRef Ws, +void SelfTargetProcessControl::writeUInt64s(ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *jitTargetAddressToPointer(W.Address) = W.Value; OnWriteComplete(Error::success()); } -void SelfTargetProcessControl::writeBuffers(ArrayRef Ws, +void SelfTargetProcessControl::writeBuffers(ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) memcpy(jitTargetAddressToPointer(W.Address), W.Buffer.data(), diff --git a/llvm/tools/lli/CMakeLists.txt b/llvm/tools/lli/CMakeLists.txt --- a/llvm/tools/lli/CMakeLists.txt +++ b/llvm/tools/lli/CMakeLists.txt @@ -11,8 +11,9 @@ MC MCJIT Object - OrcError + OrcShared OrcJIT + OrcTargetProcess Passes RuntimeDyld SelectionDAG diff --git a/llvm/tools/lli/ChildTarget/CMakeLists.txt b/llvm/tools/lli/ChildTarget/CMakeLists.txt --- a/llvm/tools/lli/ChildTarget/CMakeLists.txt +++ b/llvm/tools/lli/ChildTarget/CMakeLists.txt @@ -1,5 +1,5 @@ set(LLVM_LINK_COMPONENTS - OrcError + OrcShared OrcJIT RuntimeDyld Support diff --git a/llvm/tools/lli/LLVMBuild.txt b/llvm/tools/lli/LLVMBuild.txt --- a/llvm/tools/lli/LLVMBuild.txt +++ b/llvm/tools/lli/LLVMBuild.txt @@ -28,6 +28,9 @@ Instrumentation Interpreter MCJIT + OrcJIT + OrcShared + OrcTargetProcess Native NativeCodeGen SelectionDAG diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp --- a/llvm/tools/lli/lli.cpp +++ b/llvm/tools/lli/lli.cpp @@ -31,6 +31,7 @@ #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" diff --git a/llvm/tools/llvm-jitlink/CMakeLists.txt b/llvm/tools/llvm-jitlink/CMakeLists.txt --- a/llvm/tools/llvm-jitlink/CMakeLists.txt +++ b/llvm/tools/llvm-jitlink/CMakeLists.txt @@ -1,3 +1,7 @@ +if ( LLVM_INCLUDE_UTILS ) + add_subdirectory(llvm-jitlink-executor) +endif() + set(LLVM_LINK_COMPONENTS AllTargetsDescs AllTargetsDisassemblers @@ -8,6 +12,8 @@ MC Object OrcJIT + OrcShared + OrcTargetProcess RuntimeDyld Support ) diff --git a/llvm/tools/llvm-jitlink/LLVMBuild.txt b/llvm/tools/llvm-jitlink/LLVMBuild.txt --- a/llvm/tools/llvm-jitlink/LLVMBuild.txt +++ b/llvm/tools/llvm-jitlink/LLVMBuild.txt @@ -18,5 +18,5 @@ type = Tool name = llvm-jitlink parent = Tools -required_libraries = JITLink BinaryFormat MC Object RuntimeDyld Support - all-targets +required_libraries = JITLink BinaryFormat MC Object OrcJIT OrcTargetProcess + Support all-targets diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS + OrcShared + OrcTargetProcess + Support + ) + +add_llvm_utility(llvm-jitlink-executor + llvm-jitlink-executor.cpp + + DEPENDS + intrinsics_gen +) + +export_executable_symbols(llvm-jitlink-executor) diff --git a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/LLVMBuild.txt rename from llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt rename to llvm/tools/llvm-jitlink/llvm-jitlink-executor/LLVMBuild.txt --- a/llvm/lib/ExecutionEngine/OrcError/LLVMBuild.txt +++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./lib/ExecutionEngine/OrcError/LLVMBuild.txt -------------*- Conf -*--===; +;===- ./tools/llvm-jitlink/llvm-jitlink-executor/LLVMBuild.txt -*- Conf -*--===; ; ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ; See https://llvm.org/LICENSE.txt for license information. @@ -15,7 +15,7 @@ ;===------------------------------------------------------------------------===; [component_0] -type = Library -name = OrcError -parent = ExecutionEngine -required_libraries = Support +type = Tool +name = llvm-jitlink-executor +parent = llvm-jitlink +required_libraries = OrcTargetProcess Support diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp @@ -0,0 +1,127 @@ +//===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Simple out-of-process executor for llvm-jitlink. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include + +#ifdef LLVM_ON_UNIX + +#include +#include + +#endif + +using namespace llvm; +using namespace llvm::orc; + +ExitOnError ExitOnErr; + +LLVM_ATTRIBUTE_USED void linkComponents() { + errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper + << (void *)&llvm_orc_deregisterEHFrameSectionWrapper; +} + +void printErrorAndExit(Twine ErrMsg) { + errs() << "error: " << ErrMsg.str() << "\n\n" + << "Usage:\n" + << " llvm-jitlink-executor filedescs=, [args...]\n" + << " llvm-jitlink-executor listen=: [args...]\n"; + exit(1); +} + +int openListener(std::string Host, int Port) { +#ifndef LLVM_ON_UNIX + // FIXME: Add TCP support for Windows. + printErrorAndExit("listen option not supported"); +#else + int SockFD = socket(PF_INET, SOCK_STREAM, 0); + struct sockaddr_in ServerAddr, ClientAddr; + socklen_t ClientAddrLen = sizeof(ClientAddr); + memset(&ServerAddr, 0, sizeof(ServerAddr)); + ServerAddr.sin_family = PF_INET; + ServerAddr.sin_family = INADDR_ANY; + ServerAddr.sin_port = htons(Port); + + { + // lose the "Address already in use" error message + int Yes = 1; + if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) { + errs() << "Error calling setsockopt.\n"; + exit(1); + } + } + + if (bind(SockFD, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) < 0) { + errs() << "Error on binding.\n"; + exit(1); + } + + listen(SockFD, 1); + return accept(SockFD, (struct sockaddr *)&ClientAddr, &ClientAddrLen); +#endif +} + +int main(int argc, char *argv[]) { + + ExitOnErr.setBanner(std::string(argv[0]) + ": "); + + int InFD = 0; + int OutFD = 0; + + if (argc < 2) + printErrorAndExit("insufficient arguments"); + else { + StringRef Arg1 = argv[1]; + StringRef SpecifierType, Specifier; + std::tie(SpecifierType, Specifier) = Arg1.split('='); + if (SpecifierType == "filedescs") { + StringRef FD1Str, FD2Str; + std::tie(FD1Str, FD2Str) = Specifier.split(','); + if (FD1Str.getAsInteger(10, InFD)) + printErrorAndExit(FD1Str + " is not a valid file descriptor"); + if (FD2Str.getAsInteger(10, OutFD)) + printErrorAndExit(FD2Str + " is not a valid file descriptor"); + } else if (SpecifierType == "listen") { + StringRef Host, PortStr; + std::tie(Host, PortStr) = Specifier.split(':'); + + int Port = 0; + if (PortStr.getAsInteger(10, Port)) + printErrorAndExit("port" + PortStr + " is not a valid integer"); + + InFD = OutFD = openListener(Host.str(), Port); + } else + printErrorAndExit("invalid specifier type \"" + SpecifierType + "\""); + } + + ExitOnErr.setBanner(std::string(argv[0]) + ":"); + + using JITLinkExecutorEndpoint = + rpc::MultiThreadedRPCEndpoint; + + rpc::registerStringError(); + + rpc::FDRawByteChannel C(InFD, OutFD); + JITLinkExecutorEndpoint EP(C, true); + OrcRPCTPCServer Server(EP); + Server.setProgramName(std::string("llvm-jitlink-executor")); + + ExitOnErr(Server.run()); + + return 0; +} diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h --- a/llvm/tools/llvm-jitlink/llvm-jitlink.h +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h @@ -18,6 +18,9 @@ #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h" +#include "llvm/ExecutionEngine/Orc/RPC/FDRawByteChannel.h" +#include "llvm/ExecutionEngine/Orc/RPC/RPCUtils.h" #include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "llvm/Support/Error.h" @@ -45,9 +48,71 @@ Session &S; }; +using LLVMJITLinkChannel = orc::rpc::FDRawByteChannel; +using LLVMJITLinkRPCEndpoint = + orc::rpc::MultiThreadedRPCEndpoint; +using LLVMJITLinkRemoteMemoryManager = + orc::OrcRPCTPCJITLinkMemoryManager; +using LLVMJITLinkRemoteMemoryAccess = + orc::OrcRPCTPCMemoryAccess; + +class LLVMJITLinkRemoteTargetProcessControl + : public orc::OrcRPCTargetProcessControlBase { +public: + using BaseT = orc::OrcRPCTargetProcessControlBase; + static Expected> LaunchExecutor(); + + static Expected> ConnectToExecutor(); + + Error disconnect() override; + +private: + using LLVMJITLinkRemoteMemoryAccess = + orc::OrcRPCTPCMemoryAccess; + + using LLVMJITLinkRemoteMemoryManager = + orc::OrcRPCTPCJITLinkMemoryManager; + + LLVMJITLinkRemoteTargetProcessControl( + std::shared_ptr SSP, + std::unique_ptr Channel, + std::unique_ptr Endpoint, + ErrorReporter ReportError, Error &Err) + : BaseT(std::move(SSP), *Endpoint, std::move(ReportError)), + Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) { + ErrorAsOutParameter _(&Err); + + ListenerThread = std::thread([&]() { + while (!Finished) { + if (auto Err = this->Endpoint->handleOne()) { + reportError(std::move(Err)); + return; + } + } + }); + + if (auto Err2 = initializeORCRPCTPCBase()) { + Err = joinErrors(std::move(Err2), disconnect()); + return; + } + + OwnedMemAccess = std::make_unique(*this); + MemAccess = OwnedMemAccess.get(); + OwnedMemMgr = std::make_unique(*this); + MemMgr = OwnedMemMgr.get(); + } + + std::unique_ptr Channel; + std::unique_ptr Endpoint; + std::unique_ptr OwnedMemAccess; + std::unique_ptr OwnedMemMgr; + std::atomic Finished{false}; + std::thread ListenerThread; +}; + struct Session { - orc::ExecutionSession ES; std::unique_ptr TPC; + orc::ExecutionSession ES; orc::JITDylib *MainJD; LLVMJITLinkObjectLinkingLayer ObjLayer; std::vector JDSearchOrder; @@ -93,7 +158,7 @@ DenseMap CanonicalWeakDefs; private: - Session(Triple TT, uint64_t PageSize, Error &Err); + Session(std::unique_ptr TPC, Error &Err); }; /// Record symbols, GOT entries, stubs, and sections for ELF file. diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -15,9 +15,9 @@ #include "llvm-jitlink.h" #include "llvm/BinaryFormat/Magic.h" -#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h" +#include "llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -41,6 +41,13 @@ #include #include +#ifdef LLVM_ON_UNIX +#include +#include +#include +#include +#endif // LLVM_ON_UNIX + #define DEBUG_TYPE "llvm_jitlink" using namespace llvm; @@ -138,6 +145,14 @@ cl::desc("resolve all otherwise unresolved externals to null"), cl::init(false)); +static cl::opt OutOfProcessExecutor( + "oop-executor", cl::desc("Launch an out-of-process executor to run code"), + cl::ValueOptional); + +static cl::opt OutOfProcessExecutorConnect( + "oop-executor-connect", + cl::desc("Connect to an out-of-process executor via TCP")); + ExitOnError ExitOnErr; namespace llvm { @@ -561,6 +576,165 @@ return JD.define(std::move(MU), std::move(RT)); } +Expected> +LLVMJITLinkRemoteTargetProcessControl::LaunchExecutor() { +#ifndef LLVM_ON_UNIX + // FIXME: Add support for Windows. + return make_error("-" + OutOfProcessExecutor.ArgStr + + " not supported on non-unix platforms", + inconvertibleErrorCode()); +#else + + rpc::registerStringError(); + + constexpr int ReadEnd = 0; + constexpr int WriteEnd = 1; + + // Pipe FDs. + int ToExecutor[2]; + int FromExecutor[2]; + + pid_t ChildPID; + + // Create pipes to/from the executor.. + if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0) + return make_error("Unable to create pipe for executor", + inconvertibleErrorCode()); + + ChildPID = fork(); + + if (ChildPID == 0) { + // In the child... + + // Close the parent ends of the pipes + close(ToExecutor[WriteEnd]); + close(FromExecutor[ReadEnd]); + + // Execute the child process. + std::unique_ptr ExecutorPath, FDSpecifier; + { + ExecutorPath = std::make_unique(OutOfProcessExecutor.size() + 1); + strcpy(ExecutorPath.get(), OutOfProcessExecutor.data()); + + 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[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr}; + int RC = execvp(ExecutorPath.get(), Args); + if (RC != 0) { + errs() << "unable to launch out-of-process executor \"" + << ExecutorPath.get() << "\"\n"; + exit(1); + } + } + // else we're the parent... + + // Close the child ends of the pipes + close(ToExecutor[ReadEnd]); + close(FromExecutor[WriteEnd]); + + // Return an RPC channel connected to our end of the pipes. + auto SSP = std::make_shared(); + auto Channel = std::make_unique(FromExecutor[ReadEnd], + ToExecutor[WriteEnd]); + auto Endpoint = std::make_unique(*Channel, true); + + auto ReportError = [](Error Err) { + logAllUnhandledErrors(std::move(Err), errs(), ""); + }; + + Error Err = Error::success(); + std::unique_ptr RTPC( + new LLVMJITLinkRemoteTargetProcessControl( + std::move(SSP), std::move(Channel), std::move(Endpoint), + std::move(ReportError), Err)); + if (Err) + return std::move(Err); + return std::move(RTPC); +#endif +} + +Expected> +LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor() { +#ifndef LLVM_ON_UNIX + // FIXME: Add TCP support for Windows. + return make_error("-" + OutOfProcessExecutorConnect.ArgStr + + " not supported on non-unix platforms", + inconvertibleErrorCode()); +#else + + rpc::registerStringError(); + + StringRef HostNameStr, PortStr; + std::tie(HostNameStr, PortStr) = + StringRef(OutOfProcessExecutorConnect).split(':'); + + if (HostNameStr.empty()) + return make_error("host name for -" + + OutOfProcessExecutorConnect.ArgStr + + " can not be empty", + inconvertibleErrorCode()); + if (PortStr.empty()) + return make_error( + "port for -" + OutOfProcessExecutorConnect.ArgStr + " can not be empty", + inconvertibleErrorCode()); + + std::string HostName = HostNameStr.str(); + int Port = 0; + if (PortStr.getAsInteger(10, Port)) + return make_error("port number " + PortStr + + " is not a valid integer", + inconvertibleErrorCode()); + + int SockFD = socket(PF_INET, SOCK_STREAM, 0); + hostent *Server = gethostbyname(HostName.c_str()); + sockaddr_in ServAddr; + memset(&ServAddr, 0, sizeof(ServAddr)); + ServAddr.sin_family = PF_INET; + memmove(&Server->h_addr, &ServAddr.sin_addr.s_addr, Server->h_length); + ServAddr.sin_port = htons(Port); + if (connect(SockFD, reinterpret_cast(&ServAddr), + sizeof(ServAddr)) < 0) + return make_error("Failed to connect to " + HostName + ":" + + Twine(Port), + inconvertibleErrorCode()); + + auto SSP = std::make_shared(); + auto Channel = std::make_unique(SockFD, SockFD); + auto Endpoint = std::make_unique(*Channel, true); + + auto ReportError = [](Error Err) { + logAllUnhandledErrors(std::move(Err), errs(), ""); + }; + + Error Err = Error::success(); + std::unique_ptr RTPC( + new LLVMJITLinkRemoteTargetProcessControl( + std::move(SSP), std::move(Channel), std::move(Endpoint), + std::move(ReportError), Err)); + if (Err) + return std::move(Err); + return std::move(RTPC); +#endif +} + +Error LLVMJITLinkRemoteTargetProcessControl::disconnect() { + std::promise P; + auto F = P.get_future(); + auto Err = closeConnection([&](Error Err) -> Error { + P.set_value(std::move(Err)); + Finished = true; + return Error::success(); + }); + ListenerThread.join(); + return joinErrors(std::move(Err), F.get()); +}; + class PhonyExternalsGenerator : public DefinitionGenerator { public: Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD, @@ -574,13 +748,30 @@ }; Expected> Session::Create(Triple TT) { - Error Err = Error::success(); auto PageSize = sys::Process::getPageSize(); if (!PageSize) return PageSize.takeError(); - std::unique_ptr S(new Session(std::move(TT), *PageSize, Err)); + /// If -oop-executor is passed then launch the executor. + std::unique_ptr TPC; + if (OutOfProcessExecutor.getNumOccurrences()) { + if (auto RTPC = LLVMJITLinkRemoteTargetProcessControl::LaunchExecutor()) + TPC = std::move(*RTPC); + else + return RTPC.takeError(); + } else if (OutOfProcessExecutorConnect.getNumOccurrences()) { + if (auto RTPC = LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor()) + TPC = std::move(*RTPC); + else + return RTPC.takeError(); + } else + TPC = std::make_unique( + std::make_shared(), std::move(TT), *PageSize, + createMemoryManager()); + + Error Err = Error::success(); + std::unique_ptr S(new Session(std::move(TPC), Err)); if (Err) return std::move(Err); return std::move(S); @@ -593,10 +784,8 @@ // FIXME: Move to createJITDylib if/when we start using Platform support in // llvm-jitlink. -Session::Session(Triple TT, uint64_t PageSize, Error &Err) - : TPC(std::make_unique(std::move(TT), PageSize, - createMemoryManager())), - ObjLayer(*this, TPC->getMemMgr()) { +Session::Session(std::unique_ptr TPC, Error &Err) + : TPC(std::move(TPC)), ObjLayer(*this, this->TPC->getMemMgr()) { /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the /// Session. @@ -630,9 +819,9 @@ return; } - if (!NoExec && !TT.isOSWindows()) + if (!NoExec && !this->TPC->getTargetTriple().isOSWindows()) ObjLayer.addPlugin(std::make_unique( - ES, std::make_unique())); + ES, ExitOnErr(TPCEHFrameRegistrar::Create(*this->TPC)))); ObjLayer.addPlugin(std::make_unique(*this)); @@ -785,22 +974,28 @@ } // end namespace llvm static Triple getFirstFileTriple() { - assert(!InputFiles.empty() && "InputFiles can not be empty"); - auto ObjBuffer = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front()))); - auto Obj = ExitOnErr( - object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); - return Obj->makeTriple(); + static Triple FirstTT = []() { + assert(!InputFiles.empty() && "InputFiles can not be empty"); + auto ObjBuffer = + ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front()))); + auto Obj = ExitOnErr( + object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); + return Obj->makeTriple(); + }(); + + return FirstTT; } -static Error sanitizeArguments(const Session &S) { +static Error sanitizeArguments(const Triple &TT, const char *ArgV0) { + // Set the entry point name if not specified. if (EntryPointName.empty()) { - if (S.TPC->getTargetTriple().getObjectFormat() == Triple::MachO) + if (TT.getObjectFormat() == Triple::MachO) EntryPointName = "_main"; else EntryPointName = "main"; } + // -noexec and --args should not be used together. if (NoExec && !InputArgv.empty()) outs() << "Warning: --args passed to -noexec run will be ignored.\n"; @@ -812,14 +1007,35 @@ inconvertibleErrorCode()); } + // Only one of -oop-executor and -oop-executor-connect can be used. + if (!!OutOfProcessExecutor.getNumOccurrences() && + !!OutOfProcessExecutorConnect.getNumOccurrences()) + return make_error( + "Only one of -" + OutOfProcessExecutor.ArgStr + " and -" + + OutOfProcessExecutorConnect.ArgStr + " can be specified", + inconvertibleErrorCode()); + + // If -oop-executor was used but no value was specified then use a sensible + // default. + if (!!OutOfProcessExecutor.getNumOccurrences() && + OutOfProcessExecutor.empty()) { + SmallString<256> OOPExecutorPath(sys::fs::getMainExecutable( + ArgV0, reinterpret_cast(&sanitizeArguments))); + sys::path::remove_filename(OOPExecutorPath); + if (OOPExecutorPath.back() != '/') + OOPExecutorPath += '/'; + OOPExecutorPath += "llvm-jitlink-executor"; + OutOfProcessExecutor = OOPExecutorPath.str().str(); + } + return Error::success(); } static Error loadProcessSymbols(Session &S) { - auto InternedEntryPointName = S.ES.intern(EntryPointName); - auto FilterMainEntryPoint = [InternedEntryPointName](SymbolStringPtr Name) { - return Name != InternedEntryPointName; - }; + auto FilterMainEntryPoint = + [EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) { + return Name != EPName; + }; S.MainJD->addGenerator( ExitOnErr(orc::TPCDynamicLibrarySearchGenerator::GetForTargetProcess( *S.TPC, std::move(FilterMainEntryPoint)))); @@ -827,15 +1043,12 @@ return Error::success(); } -static Error loadDylibs() { - // FIXME: This should all be handled inside DynamicLibrary. +static Error loadDylibs(Session &S) { for (const auto &Dylib : Dylibs) { - if (!sys::fs::is_regular_file(Dylib)) - return make_error("\"" + Dylib + "\" is not a regular file", - inconvertibleErrorCode()); - std::string ErrMsg; - if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg)) - return make_error(ErrMsg, inconvertibleErrorCode()); + auto G = orc::TPCDynamicLibrarySearchGenerator::Load(*S.TPC, Dylib.c_str()); + if (!G) + return G.takeError(); + S.MainJD->addGenerator(std::move(*G)); } return Error::success(); @@ -1072,9 +1285,9 @@ std::unique_ptr Timers = ShowTimes ? std::make_unique() : nullptr; - auto S = ExitOnErr(Session::Create(getFirstFileTriple())); + ExitOnErr(sanitizeArguments(getFirstFileTriple(), argv[0])); - ExitOnErr(sanitizeArguments(*S)); + auto S = ExitOnErr(Session::Create(getFirstFileTriple())); { TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr); @@ -1083,7 +1296,7 @@ if (!NoProcessSymbols) ExitOnErr(loadProcessSymbols(*S)); - ExitOnErr(loadDylibs()); + ExitOnErr(loadDylibs(*S)); if (PhonyExternals) addPhonyExternalsGenerator(*S); @@ -1110,11 +1323,12 @@ int Result = 0; { - using MainTy = int (*)(int, char *[]); - auto EntryFn = jitTargetAddressToFunction(EntryPoint.getAddress()); TimeRegion TR(Timers ? &Timers->RunTimer : nullptr); - Result = runAsMain(EntryFn, InputArgv, StringRef(InputFiles.front())); + Result = ExitOnErr(S->TPC->runAsMain(EntryPoint.getAddress(), InputArgv)); } + ExitOnErr(S->ES.endSession()); + ExitOnErr(S->TPC->disconnect()); + return Result; } diff --git a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt @@ -2,6 +2,8 @@ ${LLVM_TARGETS_TO_BUILD} JITLink Object + OrcShared + OrcTargetProcess RuntimeDyld Support ) diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt --- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -3,8 +3,8 @@ Core ExecutionEngine Object - OrcError OrcJIT + OrcShared Passes RuntimeDyld Support