diff --git a/clang/test/Interpreter/simple-exception.cpp b/clang/test/Interpreter/simple-exception.cpp index 6749acd6e6bd2..651e8d9402f89 100644 --- a/clang/test/Interpreter/simple-exception.cpp +++ b/clang/test/Interpreter/simple-exception.cpp @@ -1,7 +1,7 @@ // clang-format off // UNSUPPORTED: system-aix -// XFAIL for arm and arm64, or running on Windows. -// XFAIL: target=arm{{.*}}, system-windows +// XFAIL for arm, or running on Windows. +// XFAIL: target=arm-{{.*}}, target=armv{{.*}}, system-windows // RUN: cat %s | clang-repl | FileCheck %s // Incompatible with msan. It passes with -O3 but fail -Oz. Interpreter diff --git a/compiler-rt/lib/orc/macho_platform.cpp b/compiler-rt/lib/orc/macho_platform.cpp index 8ca68587aeb36..4b603fd95e316 100644 --- a/compiler-rt/lib/orc/macho_platform.cpp +++ b/compiler-rt/lib/orc/macho_platform.cpp @@ -557,6 +557,12 @@ Error MachOPlatformRuntimeState::registerObjectPlatformSections( return make_error(ErrStream.str()); } + ORC_RT_DEBUG({ + printdbg(" UnwindInfo: %s, UseCallbackStyleUnwindInfo: %s\n", + UnwindInfo ? "true" : "false", + UseCallbackStyleUnwindInfo ? "true" : "false"); + }); + if (UnwindInfo && UseCallbackStyleUnwindInfo) { ORC_RT_DEBUG({ printdbg(" Registering new-style unwind info for:\n" diff --git a/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions-stress-test-tower.cpp b/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions-stress-test-tower.cpp new file mode 100644 index 0000000000000..245afa084f5c0 --- /dev/null +++ b/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions-stress-test-tower.cpp @@ -0,0 +1,24 @@ +// RUN: %clangxx -c -o %t %s +// RUN: %llvm_jitlink -slab-allocate=20Mb %t +// +// REQUIRES: system-darwin && host-arch-compatible + +// Test that we can throw and catch an exception through a large number of +// stack frames. The number (1022) is chosen to force emission of multiple +// unwind info second-level pages. + +template +void f() { try { f(); } catch (...) { throw; } } + +template <> +void f<0>() { throw 42; } + +int main(int argc, char *argv[]) { + try { + f<1020>(); + } catch (int n) { + return 42 - n; + } + return 1; +} + diff --git a/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp b/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp new file mode 100644 index 0000000000000..304588a327386 --- /dev/null +++ b/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx -c -o %t %s +// RUN: %llvm_jitlink -slab-allocate=20Mb %t +// +// REQUIRES: system-darwin && host-arch-compatible + +// Test that trivial throw / catch works. +int main(int argc, char *argv[]) { + try { + throw 42; + } catch (int E) { + return 42 - E; + } + return 1; +} diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h index 297e603164b24..8f0dfea0c97ac 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -587,6 +587,9 @@ class Symbol { return static_cast(*Base); } + /// Return the Section for this Symbol (Symbol must be defined). + Section &getSection() const { return getBlock().getSection(); } + /// Returns the offset for this symbol within the underlying addressable. orc::ExecutorAddrDiff getOffset() const { return Offset; } @@ -1460,7 +1463,7 @@ class LinkGraph { A.setAddress(orc::ExecutorAddr()); } else { assert(Sym.isDefined() && "Sym is not a defined symbol"); - Section &Sec = Sym.getBlock().getSection(); + Section &Sec = Sym.getSection(); Sec.removeSymbol(Sym); Sym.makeExternal(createAddressable(orc::ExecutorAddr(), false)); } @@ -1488,7 +1491,7 @@ class LinkGraph { Sym.setScope(Scope::Local); } else { assert(Sym.isDefined() && "Sym is not a defined symbol"); - Section &Sec = Sym.getBlock().getSection(); + Section &Sec = Sym.getSection(); Sec.removeSymbol(Sym); Sym.makeAbsolute(createAddressable(Address)); } @@ -1534,7 +1537,7 @@ class LinkGraph { transferDefinedSymbol(Symbol &Sym, Block &DestBlock, orc::ExecutorAddrDiff NewOffset, std::optional ExplicitNewSize) { - auto &OldSection = Sym.getBlock().getSection(); + auto &OldSection = Sym.getSection(); Sym.setBlock(DestBlock); Sym.setOffset(NewOffset); if (ExplicitNewSize) @@ -1622,7 +1625,7 @@ class LinkGraph { /// Removes defined symbols. Does not remove the underlying block. void removeDefinedSymbol(Symbol &Sym) { assert(Sym.isDefined() && "Sym is not a defined symbol"); - Sym.getBlock().getSection().removeSymbol(Sym); + Sym.getSection().removeSymbol(Sym); destroySymbol(Sym); } diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index db853362f6573..cecb4094c9a57 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -1204,8 +1204,13 @@ class JITDylib : public ThreadSafeRefCountedBase, JITDylib(ExecutionSession &ES, std::string Name); - std::pair> - IL_removeTracker(ResourceTracker &RT); + struct RemoveTrackerResult { + AsynchronousSymbolQuerySet QueriesToFail; + std::shared_ptr FailedSymbols; + std::vector> DefunctMUs; + }; + + RemoveTrackerResult IL_removeTracker(ResourceTracker &RT); void transferTracker(ResourceTracker &DstRT, ResourceTracker &SrcRT); @@ -1551,6 +1556,30 @@ class ExecutionSession { EPC->getDispatcher().dispatch(std::move(T)); } + /// Returns the bootstrap map. + const StringMap> &getBootstrapMap() const { + return EPC->getBootstrapMap(); + } + + /// Look up and SPS-deserialize a bootstrap map value. + template + Error getBootstrapMapValue(StringRef Key, std::optional &Val) const { + return EPC->getBootstrapMapValue(Key, Val); + } + + /// Returns the bootstrap symbol map. + const StringMap &getBootstrapSymbolsMap() const { + return EPC->getBootstrapSymbolsMap(); + } + + /// For each (ExecutorAddr&, StringRef) pair, looks up the string in the + /// bootstrap symbols map and writes its address to the ExecutorAddr if + /// found. If any symbol is not found then the function returns an error. + Error getBootstrapSymbols( + ArrayRef> Pairs) const { + return EPC->getBootstrapSymbols(Pairs); + } + /// Run a wrapper function in the executor. /// /// The wrapper function should be callable as: diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h index dcf5592f1717c..86e98e74b7055 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -20,6 +20,7 @@ #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h" #include "llvm/ExecutionEngine/Orc/TaskDispatch.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/MSVCErrorWorkarounds.h" @@ -507,6 +508,9 @@ class SelfExecutorProcessControl : public ExecutorProcessControl, SymbolLookupCompleteFn F) override; std::unique_ptr OwnedMemMgr; +#ifdef __APPLE__ + std::unique_ptr UnwindInfoMgr; +#endif // __APPLE__ char GlobalManglingPrefix = 0; }; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h b/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h index 6e99f6c03a7c6..91842714f6c4c 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h @@ -368,6 +368,7 @@ class MachOPlatform : public Platform { DenseMap RegisteredInitSymbols; std::mutex PlatformMutex; + bool ForceEHFrames = false; BootstrapInfo *Bootstrap = nullptr; DenseMap JITDylibToHeaderAddr; DenseMap HeaderAddrToJITDylib; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h index b927dfbce992a..8eb8c1b4cc89d 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h @@ -25,7 +25,7 @@ namespace orc { extern StringRef MachODataCommonSectionName; extern StringRef MachODataDataSectionName; extern StringRef MachOEHFrameSectionName; -extern StringRef MachOCompactUnwindInfoSectionName; +extern StringRef MachOCompactUnwindSectionName; extern StringRef MachOCStringSectionName; extern StringRef MachOModInitFuncSectionName; extern StringRef MachOObjCCatListSectionName; @@ -53,6 +53,7 @@ extern StringRef MachOTextTextSectionName; extern StringRef MachOThreadBSSSectionName; extern StringRef MachOThreadDataSectionName; extern StringRef MachOThreadVarsSectionName; +extern StringRef MachOUnwindInfoSectionName; extern StringRef MachOInitSectionNames[22]; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h index aed43f6308cba..db5ff135a7164 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h @@ -88,6 +88,15 @@ using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr, using SPSRunAsVoidFunctionSignature = int32_t(shared::SPSExecutorAddr); using SPSRunAsIntFunctionSignature = int32_t(shared::SPSExecutorAddr, int32_t); } // end namespace rt + +namespace rt_alt { +extern const char *UnwindInfoManagerInstanceName; +extern const char *UnwindInfoManagerFindSectionsHelperName; +extern const char *UnwindInfoManagerEnableWrapperName; +extern const char *UnwindInfoManagerDisableWrapperName; +extern const char *UnwindInfoManagerRegisterActionName; +extern const char *UnwindInfoManagerDeregisterActionName; +} // end namespace rt_alt } // end namespace orc } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h new file mode 100644 index 0000000000000..d3277e61eeb7b --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h @@ -0,0 +1,28 @@ +//===- DefaultHostBootstrapValues.h - Defaults for host process -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Set sensible default bootstrap values for JIT execution in the host process. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DEFAULTHOSTBOOTSTRAPVALUES_H +#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DEFAULTHOSTBOOTSTRAPVALUES_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include + +namespace llvm::orc { + +void addDefaultBootstrapValuesForHostProcess( + StringMap> &BootstrapMap, + StringMap &BootstrapSymbols); + +} // namespace llvm::orc + +#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_DEFAULTHOSTBOOTSTRAPVALUES_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h new file mode 100644 index 0000000000000..fc7719f282122 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h @@ -0,0 +1,78 @@ +//===--- UnwindInfoManager.h -- Register unwind info 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 +// +//===----------------------------------------------------------------------===// +// +// Utilities for managing eh-frame and compact-unwind registration and lookup +// through libunwind's find_dynamic_unwind_sections mechanism. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H +#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H + +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm::orc { + +class UnwindInfoManager : public ExecutorBootstrapService { +public: + // This struct's layout should match the unw_dynamic_unwind_sections struct + // from libunwind/src/libunwid_ext.h. + struct UnwindSections { + uintptr_t dso_base; + uintptr_t dwarf_section; + size_t dwarf_section_length; + uintptr_t compact_unwind_section; + size_t compact_unwind_section_length; + }; + + /// If the libunwind find-dynamic-unwind-info callback registration APIs are + /// available then this method will return an UnwindInfoManager instance, + /// otherwise it will return nullptr. + static std::unique_ptr TryCreate(); + + Error shutdown() override; + void addBootstrapSymbols(StringMap &M) override; + + Error enable(void *FindDynamicUnwindSections); + Error disable(void); + + Error registerSections(ArrayRef CodeRanges, + orc::ExecutorAddr DSOBase, + orc::ExecutorAddrRange DWARFEHFrame, + orc::ExecutorAddrRange CompactUnwind); + + Error deregisterSections(ArrayRef CodeRanges); + + int findSections(uintptr_t Addr, UnwindSections *Info); + +private: + UnwindInfoManager(int (*AddFindDynamicUnwindSections)(void *), + int (*RemoveFindDynamicUnwindSections)(void *)) + : AddFindDynamicUnwindSections(AddFindDynamicUnwindSections), + RemoveFindDynamicUnwindSections(RemoveFindDynamicUnwindSections) {} + + static int findSectionsHelper(UnwindInfoManager *Instance, uintptr_t Addr, + UnwindSections *Info); + + std::mutex M; + std::map UWSecs; + + int (*AddFindDynamicUnwindSections)(void *) = nullptr; + int (*RemoveFindDynamicUnwindSections)(void *) = nullptr; + void *FindDynamicUnwindSections = nullptr; + + static const char *AddFnName, *RemoveFnName; +}; + +} // namespace llvm::orc + +#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.h b/llvm/include/llvm/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.h new file mode 100644 index 0000000000000..eb883a79a93d8 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.h @@ -0,0 +1,70 @@ +//===- UnwindInfoRegistrationPlugin.h -- libunwind 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 +// +//===----------------------------------------------------------------------===// +// +// Register eh-frame and compact-unwind sections with libunwind +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H +#define LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H + +#include "llvm/ExecutionEngine/Orc/LinkGraphLinkingLayer.h" + +namespace llvm::orc { + +class UnwindInfoRegistrationPlugin : public LinkGraphLinkingLayer::Plugin { +public: + static Expected> + Create(IRLayer &IRL, JITDylib &PlatformJD, ExecutorAddr Instance, + ExecutorAddr FindHelper, ExecutorAddr Enable, ExecutorAddr Disable, + ExecutorAddr Register, ExecutorAddr Deregister); + + static Expected> + Create(IRLayer &IRL, JITDylib &PlatformJD); + + ~UnwindInfoRegistrationPlugin(); + + void modifyPassConfig(MaterializationResponsibility &MR, + jitlink::LinkGraph &G, + jitlink::PassConfiguration &PassConfig) override; + + Error notifyEmitted(MaterializationResponsibility &MR) override { + return Error::success(); + } + + Error notifyFailed(MaterializationResponsibility &MR) override { + return Error::success(); + } + + Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { + return Error::success(); + } + + void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, + ResourceKey SrcKey) override {} + +private: + UnwindInfoRegistrationPlugin(ExecutionSession &ES, ExecutorAddr Instance, + ExecutorAddr Disable, ExecutorAddr Register, + ExecutorAddr Deregister) + : ES(ES), Instance(Instance), Disable(Disable), Register(Register), + Deregister(Deregister) { + DSOBaseName = ES.intern("__jitlink$libunwind_dso_base"); + } + + static Expected makeBouncerModule(ExecutionSession &ES); + Error addUnwindInfoRegistrationActions(jitlink::LinkGraph &G); + + ExecutionSession &ES; + SymbolStringPtr DSOBaseName; + ExecutorAddr Instance, Disable, Register, Deregister; +}; + +} // namespace llvm::orc + +#endif // LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt index e5f5a99c39bc0..65dd0c7468ae1 100644 --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -3,6 +3,7 @@ tablegen(LLVM COFFOptions.inc -gen-opt-parser-defs) add_public_tablegen_target(JITLinkTableGen) add_llvm_component_library(LLVMJITLink + CompactUnwindSupport.cpp DWARFRecordSectionSplitter.cpp EHFrameSupport.cpp JITLink.cpp diff --git a/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp index 151f1b337087d..8ceb08051e423 100644 --- a/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp @@ -221,8 +221,7 @@ class COFFLinkGraphLowering_x86_64 { } case EdgeKind_coff_x86_64::SecRel32: { E.setAddend(E.getAddend() - - getSectionStart(E.getTarget().getBlock().getSection()) - .getValue()); + getSectionStart(E.getTarget().getSection()).getValue()); E.setKind(x86_64::Pointer32); break; } diff --git a/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.cpp new file mode 100644 index 0000000000000..51e3d26479ffd --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.cpp @@ -0,0 +1,103 @@ +//=------- CompactUnwindSupport.cpp - Compact Unwind format support -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Compact Unwind support. +// +//===----------------------------------------------------------------------===// + +#include "CompactUnwindSupport.h" + +#include "llvm/ADT/Sequence.h" + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection, + size_t RecordSize) { + + std::vector OriginalBlocks(CompactUnwindSection.blocks().begin(), + CompactUnwindSection.blocks().end()); + LLVM_DEBUG({ + dbgs() << "In " << G.getName() << " splitting compact unwind section " + << CompactUnwindSection.getName() << " containing " + << OriginalBlocks.size() << " initial blocks...\n"; + }); + + while (!OriginalBlocks.empty()) { + auto *B = OriginalBlocks.back(); + OriginalBlocks.pop_back(); + + if (B->getSize() == 0) { + LLVM_DEBUG({ + dbgs() << " Skipping empty block at " + << formatv("{0:x16}", B->getAddress()) << "\n"; + }); + continue; + } + + unsigned NumBlocks = B->getSize() / RecordSize; + + LLVM_DEBUG({ + dbgs() << " Splitting block at " << formatv("{0:x16}", B->getAddress()) + << " into " << NumBlocks << " compact unwind record(s)\n"; + }); + + if (B->getSize() % RecordSize) + return make_error( + "Error splitting compact unwind record in " + G.getName() + + ": block at " + formatv("{0:x}", B->getAddress()) + " has size " + + formatv("{0:x}", B->getSize()) + + " (not a multiple of CU record size of " + + formatv("{0:x}", RecordSize) + ")"); + + auto Blocks = + G.splitBlock(*B, map_range(seq(1U, NumBlocks), [=](Edge::OffsetT Idx) { + return Idx * RecordSize; + })); + + for (auto *CURec : Blocks) { + bool AddedKeepAlive = false; + + for (auto &E : CURec->edges()) { + if (E.getOffset() == 0) { + LLVM_DEBUG({ + dbgs() << " Updating compact unwind record at " + << CURec->getAddress() << " to point to " + << (E.getTarget().hasName() ? *E.getTarget().getName() + : StringRef()) + << " (at " << E.getTarget().getAddress() << ")\n"; + }); + + if (E.getTarget().isExternal()) + return make_error( + "Error adding keep-alive edge for compact unwind record at " + + formatv("{0:x}", CURec->getAddress()) + ": target " + + *E.getTarget().getName() + " is an external symbol"); + auto &TgtBlock = E.getTarget().getBlock(); + auto &CURecSym = + G.addAnonymousSymbol(*CURec, 0, RecordSize, false, false); + TgtBlock.addEdge(Edge::KeepAlive, 0, CURecSym, 0); + AddedKeepAlive = true; + } + } + + if (!AddedKeepAlive) + return make_error( + "Error adding keep-alive edge for compact unwind record at " + + formatv("{0:x}", CURec->getAddress()) + + ": no outgoing target edge at offset 0"); + } + } + + return Error::success(); +} + +} // end namespace jitlink +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.h b/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.h new file mode 100644 index 0000000000000..c3f8834949335 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.h @@ -0,0 +1,682 @@ +//===- CompactUnwindSupportImpl.h - Compact Unwind format impl --*- 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 +// +//===----------------------------------------------------------------------===// +// +// Compact Unwind format support implementation details. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H +#define LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ExecutionEngine/JITLink/MachO.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" + +#define DEBUG_TYPE "jitlink_cu" + +namespace llvm { +namespace jitlink { + +/// Split blocks in an __LD,__compact_unwind section on record boundaries. +/// When this function returns edges within each record are guaranteed to be +/// sorted by offset. +Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection, + size_t RecordSize); + +/// CRTP base for compact unwind traits classes. Automatically provides derived +/// constants. +/// +/// FIXME: Passing PtrSize as a template parameter is a hack to work around a +/// bug in older MSVC compilers (until at least MSVC 15) where constexpr +/// fields in the CRTP impl class were not visible to the base class. +/// Once we no longer need to support these compilers the PtrSize +/// template argument should be removed and PointerSize should be +/// defined as a member in the CRTP Impl classes. +template struct CompactUnwindTraits { + static constexpr size_t PointerSize = PtrSize; + static constexpr size_t Size = 3 * PointerSize + 2 * 4; + static constexpr size_t FnFieldOffset = 0; + static constexpr size_t SizeFieldOffset = FnFieldOffset + PointerSize; + static constexpr size_t EncodingFieldOffset = SizeFieldOffset + 4; + static constexpr size_t PersonalityFieldOffset = EncodingFieldOffset + 4; + static constexpr size_t LSDAFieldOffset = + PersonalityFieldOffset + PointerSize; + + static uint32_t readPCRangeSize(ArrayRef RecordContent) { + assert(SizeFieldOffset + 4 <= RecordContent.size() && + "Truncated CU record?"); + return support::endian::read32(RecordContent.data() + + SizeFieldOffset); + } + + static uint32_t readEncoding(ArrayRef RecordContent) { + assert(EncodingFieldOffset + 4 <= RecordContent.size() && + "Truncated CU record?"); + return support::endian::read32(RecordContent.data() + + EncodingFieldOffset); + } + + static std::optional encodeDWARFOffset(size_t Delta) { + uint32_t Encoded = + static_cast(Delta) & CRTPImpl::DWARFSectionOffsetMask; + if (Encoded != Delta) + return std::nullopt; + return Encoded; + } +}; + +/// Architecture specific implementation of CompactUnwindManager. +template class CompactUnwindManager { +public: + CompactUnwindManager(StringRef CompactUnwindSectionName, + StringRef UnwindInfoSectionName, + StringRef EHFrameSectionName) + : CompactUnwindSectionName(CompactUnwindSectionName), + UnwindInfoSectionName(UnwindInfoSectionName), + EHFrameSectionName(EHFrameSectionName) {} + + // Split compact unwind records, add keep-alive edges from functions to + // compact unwind records, and from compact unwind records to FDEs where + // needed. + // + // This method must be called *after* __eh_frame has been processed: it + // assumes that eh-frame records have been split up and keep-alive edges have + // been inserted. + Error prepareForPrune(LinkGraph &G) { + Section *CUSec = G.findSectionByName(CompactUnwindSectionName); + if (!CUSec || CUSec->empty()) { + LLVM_DEBUG({ + dbgs() << "Compact unwind: No compact unwind info for " << G.getName() + << "\n"; + }); + return Error::success(); + } + + LLVM_DEBUG({ + dbgs() << "Compact unwind: preparing " << G.getName() << " for prune\n"; + }); + + Section *EHFrameSec = G.findSectionByName(EHFrameSectionName); + + if (auto Err = splitCompactUnwindBlocks(G, *CUSec, CURecTraits::Size)) + return Err; + + LLVM_DEBUG({ + dbgs() << " Preparing " << CUSec->blocks_size() << " blocks in " + << CompactUnwindSectionName << "\n"; + }); + + for (auto *B : CUSec->blocks()) { + + // Find target function edge. + Edge *PCBeginEdge = nullptr; + for (auto &E : B->edges_at(CURecTraits::FnFieldOffset)) { + PCBeginEdge = &E; + break; + } + + if (!PCBeginEdge) + return make_error( + "In " + G.getName() + ", compact unwind record at " + + formatv("{0:x}", B->getAddress()) + " has no pc-begin edge"); + + if (!PCBeginEdge->getTarget().isDefined()) + return make_error( + "In " + G.getName() + ", compact unwind record at " + + formatv("{0:x}", B->getAddress()) + " points at external symbol " + + *PCBeginEdge->getTarget().getName()); + + auto &Fn = PCBeginEdge->getTarget(); + + if (!Fn.isDefined()) { + LLVM_DEBUG({ + dbgs() << "In " << CompactUnwindSectionName << " for " << G.getName() + << " encountered unexpected pc-edge to undefined symbol " + << Fn.getName() << "\n"; + }); + continue; + } + + uint32_t Encoding = CURecTraits::readEncoding(B->getContent()); + bool NeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Encoding); + + LLVM_DEBUG({ + dbgs() << " Found record for function "; + if (Fn.hasName()) + dbgs() << Fn.getName(); + else + dbgs() << "'; + dbgs() << ": encoding = " << formatv("{0:x}", Encoding); + if (NeedsDWARF) + dbgs() << " (needs DWARF)"; + dbgs() << "\n"; + }); + + auto &CURecSym = + G.addAnonymousSymbol(*B, 0, CURecTraits::Size, false, false); + + bool KeepAliveAlreadyPresent = false; + if (EHFrameSec) { + Edge *KeepAliveEdge = nullptr; + for (auto &E : Fn.getBlock().edges_at(0)) { + if (E.getKind() == Edge::KeepAlive && E.getTarget().isDefined() && + &E.getTarget().getSection() == EHFrameSec) { + KeepAliveEdge = &E; + break; + } + } + + if (KeepAliveEdge) { + // Found a keep-alive edge to an FDE in the eh-frame. Switch the keep + // alive edge to point to the CU and if the CU needs DWARF then add + // an extra keep-alive edge from the CU to the FDE. + auto &FDE = KeepAliveEdge->getTarget(); + KeepAliveEdge->setTarget(CURecSym); + KeepAliveAlreadyPresent = true; + if (NeedsDWARF) { + LLVM_DEBUG({ + dbgs() << " Adding keep-alive edge to FDE at " + << FDE.getAddress() << "\n"; + }); + B->addEdge(Edge::KeepAlive, 0, FDE, 0); + } + } else { + if (NeedsDWARF) + return make_error( + "In " + G.getName() + ", compact unwind recard ot " + + formatv("{0:x}", B->getAddress()) + + " needs DWARF, but no FDE was found"); + } + } else { + if (NeedsDWARF) + return make_error( + "In " + G.getName() + ", compact unwind recard ot " + + formatv("{0:x}", B->getAddress()) + " needs DWARF, but no " + + EHFrameSectionName + " section exists"); + } + + if (!KeepAliveAlreadyPresent) { + // No FDE edge. We'll need to add a new edge from the function back + // to the CU record. + Fn.getBlock().addEdge(Edge::KeepAlive, 0, CURecSym, 0); + } + } + + return Error::success(); + } + + /// Process all __compact_unwind records and reserve space for __unwind_info. + Error processAndReserveUnwindInfo(LinkGraph &G) { + // Bail out early if no unwind info. + Section *CUSec = G.findSectionByName(CompactUnwindSectionName); + if (!CUSec) + return Error::success(); + + // The __LD/__compact_unwind section is only used as input for the linker. + // We'll create a new __TEXT,__unwind_info section for unwind info output. + CUSec->setMemLifetime(orc::MemLifetime::NoAlloc); + + // Find / make a mach-header to act as the base for unwind-info offsets + // (and to report the arch / subarch to libunwind). + if (auto Err = getOrCreateCompactUnwindBase(G)) + return Err; + + // Error out if there's already unwind-info in the graph: We have no idea + // how to merge unwind-info sections. + if (G.findSectionByName(UnwindInfoSectionName)) + return make_error("In " + G.getName() + ", " + + UnwindInfoSectionName + + " already exists"); + + // Process the __compact_unwind section to build the Records vector that + // we'll use for writing the __unwind_info section. + if (auto Err = processCompactUnwind(G, *CUSec)) + return Err; + + // Calculate the size of __unwind_info. + size_t UnwindInfoSectionSize = + UnwindInfoSectionHeaderSize + + Personalities.size() * PersonalityEntrySize + + (NumSecondLevelPages + 1) * IndexEntrySize + NumLSDAs * LSDAEntrySize + + NumSecondLevelPages * SecondLevelPageHeaderSize + + Records.size() * SecondLevelPageEntrySize; + + LLVM_DEBUG({ + dbgs() << "In " << G.getName() << ", reserving " + << formatv("{0:x}", UnwindInfoSectionSize) << " bytes for " + << UnwindInfoSectionName << "\n"; + }); + + // Create the __unwind_info section and reserve space for it. + Section &UnwindInfoSec = + G.createSection(UnwindInfoSectionName, orc::MemProt::Read); + + auto UnwindInfoSectionContent = G.allocateBuffer(UnwindInfoSectionSize); + memset(UnwindInfoSectionContent.data(), 0, UnwindInfoSectionContent.size()); + auto &B = G.createMutableContentBlock( + UnwindInfoSec, UnwindInfoSectionContent, orc::ExecutorAddr(), 8, 0); + + // Add Keep-alive edges from the __unwind_info block to all of the target + // functions. + for (auto &R : Records) + B.addEdge(Edge::KeepAlive, 0, *R.Fn, 0); + + return Error::success(); + } + + Error writeUnwindInfo(LinkGraph &G) { + Section *CUSec = G.findSectionByName(CompactUnwindSectionName); + if (!CUSec || CUSec->empty()) + return Error::success(); + + Section *UnwindInfoSec = G.findSectionByName(UnwindInfoSectionName); + if (!UnwindInfoSec) + return make_error("In " + G.getName() + ", " + + UnwindInfoSectionName + + " missing after allocation"); + + if (UnwindInfoSec->blocks_size() != 1) + return make_error( + "In " + G.getName() + ", " + UnwindInfoSectionName + + " contains more than one block post-allocation"); + + LLVM_DEBUG( + { dbgs() << "Writing unwind info for " << G.getName() << "...\n"; }); + + mergeRecords(); + + auto &UnwindInfoBlock = **UnwindInfoSec->blocks().begin(); + auto Content = UnwindInfoBlock.getMutableContent(G); + BinaryStreamWriter Writer( + {reinterpret_cast(Content.data()), Content.size()}, + CURecTraits::Endianness); + + // __unwind_info format, from mach-o/compact_unwind_encoding.h on Darwin: + // + // #define UNWIND_SECTION_VERSION 1 + // struct unwind_info_section_header + // { + // uint32_t version; // UNWIND_SECTION_VERSION + // uint32_t commonEncodingsArraySectionOffset; + // uint32_t commonEncodingsArrayCount; + // uint32_t personalityArraySectionOffset; + // uint32_t personalityArrayCount; + // uint32_t indexSectionOffset; + // uint32_t indexCount; + // // compact_unwind_encoding_t[] + // // uint32_t personalities[] + // // unwind_info_section_header_index_entry[] + // // unwind_info_section_header_lsda_index_entry[] + // }; + + if (auto Err = writeHeader(G, Writer)) + return Err; + + // Skip common encodings: JITLink doesn't use them. + + if (auto Err = writePersonalities(G, Writer)) + return Err; + + // Calculate the offset to the LSDAs. + size_t SectionOffsetToLSDAs = + Writer.getOffset() + (NumSecondLevelPages + 1) * IndexEntrySize; + + // Calculate offset to the 1st second-level page. + size_t SectionOffsetToSecondLevelPages = + SectionOffsetToLSDAs + NumLSDAs * LSDAEntrySize; + + if (auto Err = writeIndexes(G, Writer, SectionOffsetToLSDAs, + SectionOffsetToSecondLevelPages)) + return Err; + + if (auto Err = writeLSDAs(G, Writer)) + return Err; + + if (auto Err = writeSecondLevelPages(G, Writer)) + return Err; + + LLVM_DEBUG({ + dbgs() << " Wrote " << formatv("{0:x}", Writer.getOffset()) + << " bytes of unwind info.\n"; + }); + + return Error::success(); + } + +private: + // Calculate the size of unwind-info. + static constexpr size_t MaxPersonalities = 4; + static constexpr size_t PersonalityShift = 28; + + static constexpr size_t UnwindInfoSectionHeaderSize = 4 * 7; + static constexpr size_t PersonalityEntrySize = 4; + static constexpr size_t IndexEntrySize = 3 * 4; + static constexpr size_t LSDAEntrySize = 2 * 4; + static constexpr size_t SecondLevelPageSize = 4096; + static constexpr size_t SecondLevelPageHeaderSize = 8; + static constexpr size_t SecondLevelPageEntrySize = 8; + static constexpr size_t NumRecordsPerSecondLevelPage = + (SecondLevelPageSize - SecondLevelPageHeaderSize) / + SecondLevelPageEntrySize; + + struct CompactUnwindRecord { + Symbol *Fn = nullptr; + uint32_t Size = 0; + uint32_t Encoding = 0; + Symbol *LSDA = nullptr; + Symbol *FDE = nullptr; + }; + + Error processCompactUnwind(LinkGraph &G, Section &CUSec) { + // TODO: Reset NumLSDAs, Personalities and CompactUnwindRecords if + // processing more than once. + assert(NumLSDAs == 0 && "NumLSDAs should be zero"); + assert(Records.empty() && "CompactUnwindRecords vector should be empty."); + assert(Personalities.empty() && "Personalities vector should be empty."); + + SmallVector NonUniquedRecords; + NonUniquedRecords.reserve(CUSec.blocks_size()); + + // Process __compact_unwind blocks. + for (auto *B : CUSec.blocks()) { + CompactUnwindRecord R; + R.Encoding = CURecTraits::readEncoding(B->getContent()); + for (auto &E : B->edges()) { + switch (E.getOffset()) { + case CURecTraits::FnFieldOffset: + // This could be the function-pointer, or the FDE keep-alive. Check + // the type to decide. + if (E.getKind() == Edge::KeepAlive) + R.FDE = &E.getTarget(); + else + R.Fn = &E.getTarget(); + break; + case CURecTraits::PersonalityFieldOffset: { + // Add the Personality to the Personalities map and update the + // encoding. + size_t PersonalityIdx = 0; + for (; PersonalityIdx != Personalities.size(); ++PersonalityIdx) + if (Personalities[PersonalityIdx] == &E.getTarget()) + break; + if (PersonalityIdx == MaxPersonalities) + return make_error( + "In " + G.getName() + + ", __compact_unwind contains too many personalities (max " + + formatv("{}", MaxPersonalities) + ")"); + if (PersonalityIdx == Personalities.size()) + Personalities.push_back(&E.getTarget()); + + R.Encoding |= (PersonalityIdx + 1) << PersonalityShift; + break; + } + case CURecTraits::LSDAFieldOffset: + ++NumLSDAs; + R.LSDA = &E.getTarget(); + break; + default: + return make_error("In " + G.getName() + + ", compact unwind record at " + + formatv("{0:x}", B->getAddress()) + + " has unrecognized edge at offset " + + formatv("{0:x}", E.getOffset())); + } + } + Records.push_back(R); + } + + // Sort the records into ascending order. + llvm::sort(Records, [](const CompactUnwindRecord &LHS, + const CompactUnwindRecord &RHS) { + return LHS.Fn->getAddress() < RHS.Fn->getAddress(); + }); + + // Calculate the number of second-level pages required. + NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) / + NumRecordsPerSecondLevelPage; + + // Convert personality symbols to GOT entry pointers. + typename CURecTraits::GOTManager GOT(G); + for (auto &Personality : Personalities) + Personality = &GOT.getEntryForTarget(G, *Personality); + + LLVM_DEBUG({ + dbgs() << " In " << G.getName() << ", " << CompactUnwindSectionName + << ": raw records = " << Records.size() + << ", personalities = " << Personalities.size() + << ", lsdas = " << NumLSDAs << "\n"; + }); + + return Error::success(); + } + + void mergeRecords() { + SmallVector NonUniqued = std::move(Records); + Records.reserve(NonUniqued.size()); + + Records.push_back(NonUniqued.front()); + for (size_t I = 1; I != NonUniqued.size(); ++I) { + auto &Next = NonUniqued[I]; + auto &Last = Records.back(); + + bool NextNeedsDWARF = CURecTraits::encodingSpecifiesDWARF(Next.Encoding); + bool CannotBeMerged = CURecTraits::encodingCannotBeMerged(Next.Encoding); + if (NextNeedsDWARF || (Next.Encoding != Last.Encoding) || + CannotBeMerged || Next.LSDA || Last.LSDA) + Records.push_back(Next); + } + + // Recalculate derived values that may have changed. + NumSecondLevelPages = (Records.size() + NumRecordsPerSecondLevelPage - 1) / + NumRecordsPerSecondLevelPage; + } + + Error writeHeader(LinkGraph &G, BinaryStreamWriter &W) { + if (!isUInt<32>(NumSecondLevelPages + 1)) + return make_error("In " + G.getName() + ", too many " + + UnwindInfoSectionName + + "second-level pages required"); + + // Write __unwind_info header. + size_t IndexArrayOffset = UnwindInfoSectionHeaderSize + + Personalities.size() * PersonalityEntrySize; + + cantFail(W.writeInteger(1)); + cantFail(W.writeInteger(UnwindInfoSectionHeaderSize)); + cantFail(W.writeInteger(0)); + cantFail(W.writeInteger(UnwindInfoSectionHeaderSize)); + cantFail(W.writeInteger(Personalities.size())); + cantFail(W.writeInteger(IndexArrayOffset)); + cantFail(W.writeInteger(NumSecondLevelPages + 1)); + + return Error::success(); + } + + Error writePersonalities(LinkGraph &G, BinaryStreamWriter &W) { + // Write personalities. + for (auto *PSym : Personalities) { + auto Delta = PSym->getAddress() - CompactUnwindBase->getAddress(); + if (!isUInt<32>(Delta)) + return makePersonalityRangeError(G, *PSym); + cantFail(W.writeInteger(Delta)); + } + return Error::success(); + } + + Error writeIndexes(LinkGraph &G, BinaryStreamWriter &W, + size_t SectionOffsetToLSDAs, + size_t SectionOffsetToSecondLevelPages) { + // Assume that function deltas are ok in this method -- we'll error + // check all of them when we write the second level pages. + + // Write the header index entries. + size_t RecordIdx = 0; + size_t NumPreviousLSDAs = 0; + for (auto &R : Records) { + // If this record marks the start of a new second level page. + if (RecordIdx % NumRecordsPerSecondLevelPage == 0) { + auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress(); + auto SecondLevelPageOffset = + SectionOffsetToSecondLevelPages + + SecondLevelPageSize * (RecordIdx / NumRecordsPerSecondLevelPage); + auto LSDAOffset = + SectionOffsetToLSDAs + NumPreviousLSDAs * LSDAEntrySize; + + cantFail(W.writeInteger(FnDelta)); + cantFail(W.writeInteger(SecondLevelPageOffset)); + cantFail(W.writeInteger(LSDAOffset)); + } + if (R.LSDA) + ++NumPreviousLSDAs; + ++RecordIdx; + } + + // Write the index array terminator. + { + auto FnEndDelta = + Records.back().Fn->getRange().End - CompactUnwindBase->getAddress(); + + if (LLVM_UNLIKELY(!isUInt<32>(FnEndDelta))) + return make_error( + "In " + G.getName() + " " + UnwindInfoSectionName + + ", delta to end of functions " + + formatv("{0:x}", Records.back().Fn->getRange().End) + + " exceeds 32 bits"); + + cantFail(W.writeInteger(FnEndDelta)); + cantFail(W.writeInteger(0)); + cantFail(W.writeInteger(SectionOffsetToSecondLevelPages)); + } + + return Error::success(); + } + + Error writeLSDAs(LinkGraph &G, BinaryStreamWriter &W) { + // As with writeIndexes, assume that function deltas are ok for now. + for (auto &R : Records) { + if (R.LSDA) { + auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress(); + auto LSDADelta = R.LSDA->getAddress() - CompactUnwindBase->getAddress(); + + if (LLVM_UNLIKELY(!isUInt<32>(LSDADelta))) + return make_error( + "In " + G.getName() + " " + UnwindInfoSectionName + + ", delta to lsda at " + formatv("{0:x}", R.LSDA->getAddress()) + + " exceeds 32 bits"); + + cantFail(W.writeInteger(FnDelta)); + cantFail(W.writeInteger(LSDADelta)); + } + } + + return Error::success(); + } + + Error writeSecondLevelPages(LinkGraph &G, BinaryStreamWriter &W) { + size_t RecordIdx = 0; + + for (auto &R : Records) { + // When starting a new second-level page, write the page header: + // + // 2 : uint32_t -- UNWIND_SECOND_LEVEL_REGULAR + // 8 : uint16_t -- size of second level page table header + // count : uint16_t -- num entries in this second-level page + if (RecordIdx % NumRecordsPerSecondLevelPage == 0) { + constexpr uint32_t SecondLevelPageHeaderKind = 2; + constexpr uint16_t SecondLevelPageHeaderSize = 8; + uint16_t SecondLevelPageNumEntries = + std::min(Records.size() - RecordIdx, NumRecordsPerSecondLevelPage); + + cantFail(W.writeInteger(SecondLevelPageHeaderKind)); + cantFail(W.writeInteger(SecondLevelPageHeaderSize)); + cantFail(W.writeInteger(SecondLevelPageNumEntries)); + } + + // Write entry. + auto FnDelta = R.Fn->getAddress() - CompactUnwindBase->getAddress(); + + if (LLVM_UNLIKELY(!isUInt<32>(FnDelta))) + return make_error( + "In " + G.getName() + " " + UnwindInfoSectionName + + ", delta to function at " + formatv("{0:x}", R.Fn->getAddress()) + + " exceeds 32 bits"); + + auto Encoding = R.Encoding; + + if (LLVM_UNLIKELY(CURecTraits::encodingSpecifiesDWARF(R.Encoding))) { + if (!EHFrameBase) + EHFrameBase = SectionRange(R.FDE->getSection()).getStart(); + auto FDEDelta = R.FDE->getAddress() - EHFrameBase; + + if (auto EncodedFDEDelta = CURecTraits::encodeDWARFOffset(FDEDelta)) + Encoding |= *EncodedFDEDelta; + else + return make_error( + "In " + G.getName() + " " + UnwindInfoSectionName + + ", cannot encode delta " + formatv("{0:x}", FDEDelta) + + " to FDE at " + formatv("{0:x}", R.FDE->getAddress())); + } + + cantFail(W.writeInteger(FnDelta)); + cantFail(W.writeInteger(Encoding)); + + ++RecordIdx; + } + + return Error::success(); + } + + Error getOrCreateCompactUnwindBase(LinkGraph &G) { + auto Name = G.intern("__jitlink$libunwind_dso_base"); + CompactUnwindBase = G.findAbsoluteSymbolByName(Name); + if (!CompactUnwindBase) { + if (auto LocalCUBase = getOrCreateLocalMachOHeader(G)) { + CompactUnwindBase = &*LocalCUBase; + auto &B = LocalCUBase->getBlock(); + G.addDefinedSymbol(B, 0, *Name, B.getSize(), Linkage::Strong, + Scope::Local, false, true); + } else + return LocalCUBase.takeError(); + } + CompactUnwindBase->setLive(true); + return Error::success(); + } + + Error makePersonalityRangeError(LinkGraph &G, Symbol &PSym) { + std::string ErrMsg; + { + raw_string_ostream ErrStream(ErrMsg); + ErrStream << "In " << G.getName() << " " << UnwindInfoSectionName + << ", personality "; + if (PSym.hasName()) + ErrStream << PSym.getName() << " "; + ErrStream << "at " << PSym.getAddress() + << " is out of 32-bit delta range of compact-unwind base at " + << CompactUnwindBase->getAddress(); + } + return make_error(std::move(ErrMsg)); + } + + StringRef CompactUnwindSectionName; + StringRef UnwindInfoSectionName; + StringRef EHFrameSectionName; + Symbol *CompactUnwindBase = nullptr; + orc::ExecutorAddr EHFrameBase; + + size_t NumLSDAs = 0; + size_t NumSecondLevelPages = 0; + SmallVector Personalities; + SmallVector Records; +}; + +} // end namespace jitlink +} // end namespace llvm + +#undef DEBUG_TYPE + +#endif // LIB_EXECUTIONENGINE_JITLINK_COMPACTUNWINDSUPPORTIMPL_H diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp index 6b77330bb764b..e8ce9b2b9527d 100644 --- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp @@ -424,7 +424,7 @@ Error makeTargetOutOfRangeError(const LinkGraph &G, const Block &B, if (E.getTarget().hasName()) { ErrStream << "\"" << E.getTarget().getName() << "\""; } else - ErrStream << E.getTarget().getBlock().getSection().getName() << " + " + ErrStream << E.getTarget().getSection().getName() << " + " << formatv("{0:x}", E.getOffset()); ErrStream << " at address " << formatv("{0:x}", E.getTarget().getAddress()) << " is out of range of " << G.getEdgeKindName(E.getKind()) diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h index e5d05e6b1b7bf..0bf714d6fcdf3 100644 --- a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h +++ b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h @@ -162,7 +162,7 @@ template class JITLinker : public JITLinkerBase { // If B is a block in a Standard or Finalize section then make sure // that no edges point to symbols in NoAlloc sections. assert((NoAllocSection || !E.getTarget().isDefined() || - E.getTarget().getBlock().getSection().getMemLifetime() != + E.getTarget().getSection().getMemLifetime() != orc::MemLifetime::NoAlloc) && "Block in allocated section has edge pointing to no-alloc " "section"); diff --git a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp index 3e757f780b550..179e458c3cd1f 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp @@ -733,121 +733,5 @@ Error MachOLinkGraphBuilder::graphifyCStringSection( return Error::success(); } -Error CompactUnwindSplitter::operator()(LinkGraph &G) { - auto *CUSec = G.findSectionByName(CompactUnwindSectionName); - if (!CUSec) - return Error::success(); - - if (!G.getTargetTriple().isOSBinFormatMachO()) - return make_error( - "Error linking " + G.getName() + - ": compact unwind splitting not supported on non-macho target " + - G.getTargetTriple().str()); - - unsigned CURecordSize = 0; - unsigned PersonalityEdgeOffset = 0; - unsigned LSDAEdgeOffset = 0; - switch (G.getTargetTriple().getArch()) { - case Triple::aarch64: - case Triple::x86_64: - // 64-bit compact-unwind record format: - // Range start: 8 bytes. - // Range size: 4 bytes. - // CU encoding: 4 bytes. - // Personality: 8 bytes. - // LSDA: 8 bytes. - CURecordSize = 32; - PersonalityEdgeOffset = 16; - LSDAEdgeOffset = 24; - break; - default: - return make_error( - "Error linking " + G.getName() + - ": compact unwind splitting not supported on " + - G.getTargetTriple().getArchName()); - } - - std::vector OriginalBlocks(CUSec->blocks().begin(), - CUSec->blocks().end()); - LLVM_DEBUG({ - dbgs() << "In " << G.getName() << " splitting compact unwind section " - << CompactUnwindSectionName << " containing " - << OriginalBlocks.size() << " initial blocks...\n"; - }); - - while (!OriginalBlocks.empty()) { - auto *B = OriginalBlocks.back(); - OriginalBlocks.pop_back(); - - if (B->getSize() == 0) { - LLVM_DEBUG({ - dbgs() << " Skipping empty block at " - << formatv("{0:x16}", B->getAddress()) << "\n"; - }); - continue; - } - - unsigned NumBlocks = B->getSize() / CURecordSize; - - LLVM_DEBUG({ - dbgs() << " Splitting block at " << formatv("{0:x16}", B->getAddress()) - << " into " << NumBlocks << " compact unwind record(s)\n"; - }); - - if (B->getSize() % CURecordSize) - return make_error( - "Error splitting compact unwind record in " + G.getName() + - ": block at " + formatv("{0:x}", B->getAddress()) + " has size " + - formatv("{0:x}", B->getSize()) + - " (not a multiple of CU record size of " + - formatv("{0:x}", CURecordSize) + ")"); - - auto Blocks = - G.splitBlock(*B, map_range(seq(1U, NumBlocks), [=](Edge::OffsetT Idx) { - return Idx * CURecordSize; - })); - - for (auto *CURec : Blocks) { - bool AddedKeepAlive = false; - - for (auto &E : CURec->edges()) { - if (E.getOffset() == 0) { - LLVM_DEBUG({ - dbgs() << " Updating compact unwind record at " - << CURec->getAddress() << " to point to " - << (E.getTarget().hasName() ? *E.getTarget().getName() - : StringRef()) - << " (at " << E.getTarget().getAddress() << ")\n"; - }); - - if (E.getTarget().isExternal()) - return make_error( - "Error adding keep-alive edge for compact unwind record at " + - formatv("{0:x}", CURec->getAddress()) + ": target " + - *E.getTarget().getName() + " is an external symbol"); - auto &TgtBlock = E.getTarget().getBlock(); - auto &CURecSym = - G.addAnonymousSymbol(*CURec, 0, CURecordSize, false, false); - TgtBlock.addEdge(Edge::KeepAlive, 0, CURecSym, 0); - AddedKeepAlive = true; - } else if (E.getOffset() != PersonalityEdgeOffset && - E.getOffset() != LSDAEdgeOffset) - return make_error( - "Unexpected edge at offset " + formatv("{0:x}", E.getOffset()) + - " in compact unwind record at " + - formatv("{0:x}", CURec->getAddress())); - } - - if (!AddedKeepAlive) - return make_error( - "Error adding keep-alive edge for compact unwind record at " + - formatv("{0:x}", CURec->getAddress()) + - ": no outgoing target edge at offset 0"); - } - } - - return Error::success(); -} - } // end namespace jitlink } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h index 6afa01250f62d..343218ec9ad18 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h +++ b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h @@ -236,17 +236,6 @@ class MachOLinkGraphBuilder { StringMap CustomSectionParserFunctions; }; -/// A pass to split up __LD,__compact_unwind sections. -class CompactUnwindSplitter { -public: - CompactUnwindSplitter(StringRef CompactUnwindSectionName) - : CompactUnwindSectionName(CompactUnwindSectionName) {} - Error operator()(LinkGraph &G); - -private: - StringRef CompactUnwindSectionName; -}; - } // end namespace jitlink } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp index 29061fff9c2ae..3af0c0cdeb7c3 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp @@ -13,7 +13,9 @@ #include "llvm/ExecutionEngine/JITLink/MachO_arm64.h" #include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" #include "llvm/ExecutionEngine/JITLink/aarch64.h" +#include "llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h" +#include "CompactUnwindSupport.h" #include "DefineExternalSectionStartAndEndSymbols.h" #include "MachOLinkGraphBuilder.h" @@ -625,6 +627,28 @@ static Error applyPACSigningToModInitPointers(LinkGraph &G) { return Error::success(); } +struct CompactUnwindTraits_MachO_arm64 + : public CompactUnwindTraits { + // FIXME: Reinstate once we no longer need the MSVC workaround. See + // FIXME for CompactUnwindTraits in CompactUnwindSupport.h. + // constexpr static size_t PointerSize = 8; + + constexpr static endianness Endianness = endianness::little; + + constexpr static uint32_t EncodingModeMask = 0x0f000000; + constexpr static uint32_t DWARFSectionOffsetMask = 0x00ffffff; + + using GOTManager = aarch64::GOTTableManager; + + static bool encodingSpecifiesDWARF(uint32_t Encoding) { + constexpr uint32_t DWARFMode = 0x03000000; + return (Encoding & EncodingModeMask) == DWARFMode; + } + + static bool encodingCannotBeMerged(uint32_t Encoding) { return false; } +}; + void link_MachO_arm64(std::unique_ptr G, std::unique_ptr Ctx) { @@ -637,16 +661,21 @@ void link_MachO_arm64(std::unique_ptr G, else Config.PrePrunePasses.push_back(markAllSymbolsLive); - // Add compact unwind splitter pass. - Config.PrePrunePasses.push_back( - CompactUnwindSplitter("__LD,__compact_unwind")); - // Add eh-frame passes. - // FIXME: Prune eh-frames for which compact-unwind is available once - // we support compact-unwind registration with libunwind. Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_arm64()); Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_arm64()); + // Create a compact-unwind manager for use in passes below. + auto CompactUnwindMgr = + std::make_shared>( + orc::MachOCompactUnwindSectionName, orc::MachOUnwindInfoSectionName, + orc::MachOEHFrameSectionName); + + // Add compact unwind prepare pass. + Config.PrePrunePasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->prepareForPrune(G); + }); + // Resolve any external section start / end symbols. Config.PostAllocationPasses.push_back( createDefineExternalSectionStartAndEndSymbolsPass( @@ -663,6 +692,16 @@ void link_MachO_arm64(std::unique_ptr G, Config.PreFixupPasses.push_back( aarch64::lowerPointer64AuthEdgesToSigningFunction); } + + // Reserve unwind-info space. + Config.PostPrunePasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->processAndReserveUnwindInfo(G); + }); + + // Translate compact-unwind to unwind-info. + Config.PreFixupPasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->writeUnwindInfo(G); + }); } if (auto Err = Ctx->modifyPassConfig(*G, Config)) @@ -673,11 +712,11 @@ void link_MachO_arm64(std::unique_ptr G, } LinkGraphPassFunction createEHFrameSplitterPass_MachO_arm64() { - return DWARFRecordSectionSplitter("__TEXT,__eh_frame"); + return DWARFRecordSectionSplitter(orc::MachOEHFrameSectionName); } LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_arm64() { - return EHFrameEdgeFixer("__TEXT,__eh_frame", aarch64::PointerSize, + return EHFrameEdgeFixer(orc::MachOEHFrameSectionName, aarch64::PointerSize, aarch64::Pointer32, aarch64::Pointer64, aarch64::Delta32, aarch64::Delta64, aarch64::NegDelta32); diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp index 9547266dc9789..bb5f3ab7ed43c 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp @@ -13,7 +13,9 @@ #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h" #include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" #include "llvm/ExecutionEngine/JITLink/x86_64.h" +#include "llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h" +#include "CompactUnwindSupport.h" #include "DefineExternalSectionStartAndEndSymbols.h" #include "MachOLinkGraphBuilder.h" @@ -500,26 +502,58 @@ Expected> createLinkGraphFromMachOObject_x86_64( .buildGraph(); } +struct CompactUnwindTraits_MachO_x86_64 + : public CompactUnwindTraits { + // FIXME: Reinstate once we no longer need the MSVC workaround. See + // FIXME for CompactUnwindTraits in CompactUnwindSupport.h. + // constexpr static size_t PointerSize = 8; + + constexpr static endianness Endianness = endianness::little; + + constexpr static uint32_t EncodingModeMask = 0x0f000000; + constexpr static uint32_t DWARFSectionOffsetMask = 0x00ffffff; + + using GOTManager = x86_64::GOTTableManager; + + static bool encodingSpecifiesDWARF(uint32_t Encoding) { + constexpr uint32_t DWARFMode = 0x04000000; + return (Encoding & EncodingModeMask) == DWARFMode; + } + + static bool encodingCannotBeMerged(uint32_t Encoding) { + constexpr uint32_t StackIndirectMode = 0x03000000; + return (Encoding & EncodingModeMask) == StackIndirectMode; + } +}; + void link_MachO_x86_64(std::unique_ptr G, std::unique_ptr Ctx) { PassConfiguration Config; if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) { - // Add eh-frame passes. - Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_x86_64()); - Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_x86_64()); - - // Add compact unwind splitter pass. - Config.PrePrunePasses.push_back( - CompactUnwindSplitter("__LD,__compact_unwind")); - // Add a mark-live pass. if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple())) Config.PrePrunePasses.push_back(std::move(MarkLive)); else Config.PrePrunePasses.push_back(markAllSymbolsLive); + // Add eh-frame passes. + Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_x86_64()); + Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_x86_64()); + + // Create a compact-unwind manager for use in passes below. + auto CompactUnwindMgr = std::make_shared< + CompactUnwindManager>( + orc::MachOCompactUnwindSectionName, orc::MachOUnwindInfoSectionName, + orc::MachOEHFrameSectionName); + + // Add compact unwind prepare pass. + Config.PrePrunePasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->prepareForPrune(G); + }); + // Resolve any external section start / end symbols. Config.PostAllocationPasses.push_back( createDefineExternalSectionStartAndEndSymbolsPass( @@ -528,6 +562,16 @@ void link_MachO_x86_64(std::unique_ptr G, // Add an in-place GOT/Stubs pass. Config.PostPrunePasses.push_back(buildGOTAndStubs_MachO_x86_64); + // Reserve space for unwind-info. + Config.PostPrunePasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->processAndReserveUnwindInfo(G); + }); + + // Translate compact-unwind to unwind-info. + Config.PreFixupPasses.push_back([CompactUnwindMgr](LinkGraph &G) { + return CompactUnwindMgr->writeUnwindInfo(G); + }); + // Add GOT/Stubs optimizer pass. Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses); } @@ -540,11 +584,11 @@ void link_MachO_x86_64(std::unique_ptr G, } LinkGraphPassFunction createEHFrameSplitterPass_MachO_x86_64() { - return DWARFRecordSectionSplitter("__TEXT,__eh_frame"); + return DWARFRecordSectionSplitter(orc::MachOEHFrameSectionName); } LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_x86_64() { - return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize, + return EHFrameEdgeFixer(orc::MachOEHFrameSectionName, x86_64::PointerSize, x86_64::Pointer32, x86_64::Pointer64, x86_64::Delta32, x86_64::Delta64, x86_64::NegDelta32); } diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index 7b255717f2383..01bb6e0403578 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -874,7 +874,7 @@ bool StubsManager_prev7::visitEdge(LinkGraph &G, Block *B, Edge &E) { LLVM_DEBUG({ dbgs() << " Using " << (UseThumb ? "Thumb" : "Arm") << " entrypoint " << *StubEntrypoint << " in " - << StubEntrypoint->getBlock().getSection().getName() << "\n"; + << StubEntrypoint->getSection().getName() << "\n"; }); E.setTarget(*StubEntrypoint); @@ -919,8 +919,8 @@ bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) { "Instruction set states of stub and relocation site should be equal"); LLVM_DEBUG({ dbgs() << " Using " << (MakeThumb ? "Thumb" : "Arm") << " entry " - << *StubSymbol << " in " - << StubSymbol->getBlock().getSection().getName() << "\n"; + << *StubSymbol << " in " << StubSymbol->getSection().getName() + << "\n"; }); E.setTarget(*StubSymbol); diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt index 2ab5d6dd39b63..8a866294eee25 100644 --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -57,6 +57,7 @@ add_llvm_component_library(LLVMOrcJIT ExecutorProcessControl.cpp TaskDispatch.cpp ThreadSafeModule.cpp + UnwindInfoRegistrationPlugin.cpp RedirectionManager.cpp JITLinkRedirectableSymbolManager.cpp ReOptimizeLayer.cpp diff --git a/llvm/lib/ExecutionEngine/Orc/CompileUtils.cpp b/llvm/lib/ExecutionEngine/Orc/CompileUtils.cpp index 5d2f3cd4a8be8..c4d65af1b57f8 100644 --- a/llvm/lib/ExecutionEngine/Orc/CompileUtils.cpp +++ b/llvm/lib/ExecutionEngine/Orc/CompileUtils.cpp @@ -33,6 +33,9 @@ irManglingOptionsFromTargetOptions(const TargetOptions &Opts) { /// Compile a Module to an ObjectFile. Expected SimpleCompiler::operator()(Module &M) { + if (M.getDataLayout().isDefault()) + M.setDataLayout(TM.createDataLayout()); + CompileResult CachedObject = tryToLoadFromObjectCache(M); if (CachedObject) return std::move(CachedObject); diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp index d47eb4416d3c2..9f466e725668a 100644 --- a/llvm/lib/ExecutionEngine/Orc/Core.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -1251,9 +1251,7 @@ JITDylib::JITDylib(ExecutionSession &ES, std::string Name) LinkOrder.push_back({this, JITDylibLookupFlags::MatchAllSymbols}); } -std::pair> -JITDylib::IL_removeTracker(ResourceTracker &RT) { +JITDylib::RemoveTrackerResult JITDylib::IL_removeTracker(ResourceTracker &RT) { // Note: Should be called under the session lock. assert(State != Closed && "JD is defunct"); @@ -1292,7 +1290,10 @@ JITDylib::IL_removeTracker(ResourceTracker &RT) { SymbolsToFail.push_back(Sym); } - auto Result = ES.IL_failSymbols(*this, std::move(SymbolsToFail)); + auto [QueriesToFail, FailedSymbols] = + ES.IL_failSymbols(*this, std::move(SymbolsToFail)); + + std::vector> DefunctMUs; // Removed symbols should be taken out of the table altogether. for (auto &Sym : SymbolsToRemove) { @@ -1302,7 +1303,12 @@ JITDylib::IL_removeTracker(ResourceTracker &RT) { // Remove Materializer if present. if (I->second.hasMaterializerAttached()) { // FIXME: Should this discard the symbols? - UnmaterializedInfos.erase(Sym); + auto J = UnmaterializedInfos.find(Sym); + assert(J != UnmaterializedInfos.end() && + "Symbol table indicates MU present, but no UMI record"); + if (J->second->MU) + DefunctMUs.push_back(std::move(J->second->MU)); + UnmaterializedInfos.erase(J); } else { assert(!UnmaterializedInfos.count(Sym) && "Symbol has materializer attached"); @@ -1313,7 +1319,8 @@ JITDylib::IL_removeTracker(ResourceTracker &RT) { shrinkMaterializationInfoMemory(); - return Result; + return {std::move(QueriesToFail), std::move(FailedSymbols), + std::move(DefunctMUs)}; } void JITDylib::transferTracker(ResourceTracker &DstRT, ResourceTracker &SrcRT) { @@ -2180,16 +2187,17 @@ Error ExecutionSession::removeResourceTracker(ResourceTracker &RT) { }); std::vector CurrentResourceManagers; - JITDylib::AsynchronousSymbolQuerySet QueriesToFail; - std::shared_ptr FailedSymbols; + JITDylib::RemoveTrackerResult R; runSessionLocked([&] { CurrentResourceManagers = ResourceManagers; RT.makeDefunct(); - std::tie(QueriesToFail, FailedSymbols) = - RT.getJITDylib().IL_removeTracker(RT); + R = RT.getJITDylib().IL_removeTracker(RT); }); + // Release any defunct MaterializationUnits. + R.DefunctMUs.clear(); + Error Err = Error::success(); auto &JD = RT.getJITDylib(); @@ -2197,9 +2205,9 @@ Error ExecutionSession::removeResourceTracker(ResourceTracker &RT) { Err = joinErrors(std::move(Err), L->handleRemoveResources(JD, RT.getKeyUnsafe())); - for (auto &Q : QueriesToFail) - Q->handleFailed( - make_error(getSymbolStringPool(), FailedSymbols)); + for (auto &Q : R.QueriesToFail) + Q->handleFailed(make_error(getSymbolStringPool(), + R.FailedSymbols)); return Err; } diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp index 7a970f3cccf46..bc5b3bb538e39 100644 --- a/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp @@ -113,7 +113,7 @@ getCodeLoadRecord(const Symbol &Sym, std::atomic &CodeIndex) { static std::optional getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) { - auto &Section = Sym.getBlock().getSection(); + auto &Section = Sym.getSection(); auto Addr = Sym.getAddress(); auto Size = Sym.getSize(); auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()}; diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/VTuneSupportPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/VTuneSupportPlugin.cpp index 9715a503629bf..1f4557217cf24 100644 --- a/llvm/lib/ExecutionEngine/Orc/Debugging/VTuneSupportPlugin.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Debugging/VTuneSupportPlugin.cpp @@ -63,7 +63,7 @@ static VTuneMethodBatch getMethodBatch(LinkGraph &G, bool EmitDebugInfo) { if (!EmitDebugInfo) continue; - auto &Section = Sym->getBlock().getSection(); + auto &Section = Sym->getSection(); auto Addr = Sym->getAddress(); auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()}; diff --git a/llvm/lib/ExecutionEngine/Orc/EHFrameRegistrationPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/EHFrameRegistrationPlugin.cpp index 217c693dae9c9..161bd68ff0854 100644 --- a/llvm/lib/ExecutionEngine/Orc/EHFrameRegistrationPlugin.cpp +++ b/llvm/lib/ExecutionEngine/Orc/EHFrameRegistrationPlugin.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/Orc/EHFrameRegistrationPlugin.h" #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" +#include "llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h" #define DEBUG_TYPE "orc" @@ -21,11 +22,19 @@ EHFrameRegistrationPlugin::EHFrameRegistrationPlugin( : ES(ES), Registrar(std::move(Registrar)) {} void EHFrameRegistrationPlugin::modifyPassConfig( - MaterializationResponsibility &MR, LinkGraph &G, + MaterializationResponsibility &MR, LinkGraph &LG, PassConfiguration &PassConfig) { + if (LG.getTargetTriple().isOSBinFormatMachO()) + PassConfig.PrePrunePasses.insert( + PassConfig.PrePrunePasses.begin(), [](LinkGraph &G) { + if (auto *CUSec = G.findSectionByName(MachOCompactUnwindSectionName)) + G.removeSection(*CUSec); + return Error::success(); + }); + PassConfig.PostFixupPasses.push_back(createEHFrameRecorderPass( - G.getTargetTriple(), [this, &MR](ExecutorAddr Addr, size_t Size) { + LG.getTargetTriple(), [this, &MR](ExecutorAddr Addr, size_t Size) { if (Addr) { std::lock_guard Lock(EHFramePluginMutex); assert(!InProcessLinks.count(&MR) && diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp index aae7369fc29c4..7f0a45941cf9b 100644 --- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp @@ -1,5 +1,4 @@ -//===------ ELFNixPlatform.cpp - Utilities for executing ELFNix in Orc -//-----===// +//===----- ELFNixPlatform.cpp - Utilities for executing ELFNix in Orc -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp index aa799687e6d5d..7b38150ab4b65 100644 --- a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp @@ -9,8 +9,7 @@ #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Core.h" -#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" -#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/Support/Process.h" #include "llvm/TargetParser/Host.h" @@ -45,13 +44,20 @@ SelfExecutorProcessControl::SelfExecutorProcessControl( this->DylibMgr = this; this->JDI = {ExecutorAddr::fromPtr(jitDispatchViaWrapperFunctionManager), ExecutorAddr::fromPtr(this)}; + if (this->TargetTriple.isOSBinFormatMachO()) GlobalManglingPrefix = '_'; - this->BootstrapSymbols[rt::RegisterEHFrameSectionWrapperName] = - ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionWrapper); - this->BootstrapSymbols[rt::DeregisterEHFrameSectionWrapperName] = - ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionWrapper); + addDefaultBootstrapValuesForHostProcess(BootstrapMap, BootstrapSymbols); + +#ifdef __APPLE__ + // FIXME: Don't add an UnwindInfoManager by default -- it's redundant when + // the ORC runtime is loaded. We'll need a way to document this and + // allow clients to choose. + this->UnwindInfoMgr = UnwindInfoManager::TryCreate(); + if (this->UnwindInfoMgr) + this->UnwindInfoMgr->addBootstrapSymbols(this->BootstrapSymbols); +#endif // __APPLE__ } Expected> diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp index 80500d0fdd9bc..dd844ae3a42bc 100644 --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -21,6 +21,7 @@ #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" +#include "llvm/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IRBuilder.h" @@ -1220,12 +1221,45 @@ Expected setUpGenericLLVMIRPlatform(LLJIT &J) { if (auto *OLL = dyn_cast(&J.getObjLinkingLayer())) { - auto &ES = J.getExecutionSession(); - if (auto EHFrameRegistrar = EPCEHFrameRegistrar::Create(ES)) - OLL->addPlugin(std::make_unique( - ES, std::move(*EHFrameRegistrar))); - else - return EHFrameRegistrar.takeError(); + bool UseEHFrames = true; + + // Enable compact-unwind support if possible. + if (J.getTargetTriple().isOSDarwin() || + J.getTargetTriple().isOSBinFormatMachO()) { + + // Check if the bootstrap map says that we should force eh-frames: + // Older libunwinds require this as they don't have a dynamic + // registration API for compact-unwind. + std::optional ForceEHFrames; + if (auto Err = J.getExecutionSession().getBootstrapMapValue( + "darwin-use-ehframes-only", ForceEHFrames)) + return Err; + if (ForceEHFrames.has_value()) + UseEHFrames = *ForceEHFrames; + else + UseEHFrames = false; + + // If UseEHFrames hasn't been set then we're good to use compact-unwind. + if (!UseEHFrames) { + if (auto UIRP = UnwindInfoRegistrationPlugin::Create( + J.getIRCompileLayer(), PlatformJD)) { + OLL->addPlugin(std::move(*UIRP)); + LLVM_DEBUG(dbgs() << "Enabled compact-unwind support.\n"); + } else + return UIRP.takeError(); + } + } + + // Otherwise fall back to standard unwind registration. + if (UseEHFrames) { + auto &ES = J.getExecutionSession(); + if (auto EHFrameRegistrar = EPCEHFrameRegistrar::Create(ES)) { + OLL->addPlugin(std::make_unique( + ES, std::move(*EHFrameRegistrar))); + LLVM_DEBUG(dbgs() << "Enabled eh-frame support.\n"); + } else + return EHFrameRegistrar.takeError(); + } } J.setPlatformSupport( diff --git a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp index 48d54190fafb6..d4e341a96f5b1 100644 --- a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp +++ b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" #include "llvm/ExecutionEngine/JITLink/MachO.h" #include "llvm/ExecutionEngine/JITLink/aarch64.h" #include "llvm/ExecutionEngine/JITLink/x86_64.h" @@ -480,6 +481,15 @@ MachOPlatform::MachOPlatform( ObjLinkingLayer.addPlugin(std::make_unique(*this)); PlatformJD.addGenerator(std::move(OrcRuntimeGenerator)); + { + // Check for force-eh-frame + std::optional ForceEHFrames; + if ((Err = ES.getBootstrapMapValue("darwin-use-ehframes-only", + ForceEHFrames))) + return; + this->ForceEHFrames = ForceEHFrames.has_value() ? *ForceEHFrames : false; + } + BootstrapInfo BI; Bootstrap = &BI; @@ -810,6 +820,12 @@ void MachOPlatform::MachOPlatformPlugin::modifyPassConfig( HeaderAddr = I->second; } + // If we're forcing eh-frame use then discard the compact-unwind section + // immediately to prevent FDEs from being stripped. + if (MP.ForceEHFrames) + if (auto *CUSec = LG.findSectionByName(MachOCompactUnwindSectionName)) + LG.removeSection(*CUSec); + // Point the libunwind dso-base absolute symbol at the header for the // JITDylib. This will prevent us from synthesizing a new header for // every object. @@ -1070,7 +1086,7 @@ Error MachOPlatform::MachOPlatformPlugin::processObjCImageInfo( for (auto *B : Sec.blocks()) for (auto &E : B->edges()) if (E.getTarget().isDefined() && - &E.getTarget().getBlock().getSection() == ObjCImageInfo) + &E.getTarget().getSection() == ObjCImageInfo) return make_error(MachOObjCImageInfoSectionName + " is referenced within file " + G.getName(), @@ -1256,7 +1272,8 @@ MachOPlatform::MachOPlatformPlugin::findUnwindSectionInfo( // ScanSection records a section range and adds any executable blocks that // that section points to to the CodeBlocks vector. SmallVector CodeBlocks; - auto ScanUnwindInfoSection = [&](Section &Sec, ExecutorAddrRange &SecRange) { + auto ScanUnwindInfoSection = [&](Section &Sec, ExecutorAddrRange &SecRange, + auto AddCodeBlocks) { if (Sec.blocks().empty()) return; SecRange = (*Sec.blocks().begin())->getRange(); @@ -1264,23 +1281,31 @@ MachOPlatform::MachOPlatformPlugin::findUnwindSectionInfo( auto R = B->getRange(); SecRange.Start = std::min(SecRange.Start, R.Start); SecRange.End = std::max(SecRange.End, R.End); - for (auto &E : B->edges()) { - if (E.getKind() != Edge::KeepAlive || !E.getTarget().isDefined()) - continue; - auto &TargetBlock = E.getTarget().getBlock(); - auto &TargetSection = TargetBlock.getSection(); - if ((TargetSection.getMemProt() & MemProt::Exec) == MemProt::Exec) - CodeBlocks.push_back(&TargetBlock); - } + AddCodeBlocks(*B); } }; - if (Section *EHFrameSec = G.findSectionByName(MachOEHFrameSectionName)) - ScanUnwindInfoSection(*EHFrameSec, US.DwarfSection); + if (Section *EHFrameSec = G.findSectionByName(MachOEHFrameSectionName)) { + ScanUnwindInfoSection(*EHFrameSec, US.DwarfSection, [&](Block &B) { + if (auto *Fn = jitlink::EHFrameCFIBlockInspector::FromEdgeScan(B) + .getPCBeginEdge()) + if (Fn->getTarget().isDefined()) + CodeBlocks.push_back(&Fn->getTarget().getBlock()); + }); + } - if (Section *CUInfoSec = - G.findSectionByName(MachOCompactUnwindInfoSectionName)) - ScanUnwindInfoSection(*CUInfoSec, US.CompactUnwindSection); + if (Section *CUInfoSec = G.findSectionByName(MachOUnwindInfoSectionName)) { + ScanUnwindInfoSection( + *CUInfoSec, US.CompactUnwindSection, [&](Block &B) { + for (auto &E : B.edges()) { + assert(E.getTarget().isDefined() && + "unwind-info record edge has external target"); + assert(E.getKind() == Edge::KeepAlive && + "unwind-info record has unexpected edge kind"); + CodeBlocks.push_back(&E.getTarget().getBlock()); + } + }); + } // If we didn't find any pointed-to code-blocks then there's no need to // register any info. diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/MachOObjectFormat.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/MachOObjectFormat.cpp index 11e8eb7bc3a19..53488b75a2686 100644 --- a/llvm/lib/ExecutionEngine/Orc/Shared/MachOObjectFormat.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Shared/MachOObjectFormat.cpp @@ -18,7 +18,7 @@ namespace orc { StringRef MachODataCommonSectionName = "__DATA,__common"; StringRef MachODataDataSectionName = "__DATA,__data"; StringRef MachOEHFrameSectionName = "__TEXT,__eh_frame"; -StringRef MachOCompactUnwindInfoSectionName = "__TEXT,__unwind_info"; +StringRef MachOCompactUnwindSectionName = "__LD,__compact_unwind"; StringRef MachOCStringSectionName = "__TEXT,__cstring"; StringRef MachOModInitFuncSectionName = "__DATA,__mod_init_func"; StringRef MachOObjCCatListSectionName = "__DATA,__objc_catlist"; @@ -46,6 +46,7 @@ StringRef MachOTextTextSectionName = "__TEXT,__text"; StringRef MachOThreadBSSSectionName = "__DATA,__thread_bss"; StringRef MachOThreadDataSectionName = "__DATA,__thread_data"; StringRef MachOThreadVarsSectionName = "__DATA,__thread_vars"; +StringRef MachOUnwindInfoSectionName = "__TEXT,__unwind_info"; StringRef MachOInitSectionNames[22] = { MachOModInitFuncSectionName, MachOObjCCatListSectionName, diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp index 54a25c007c589..fef3ff989a52a 100644 --- a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp @@ -64,5 +64,19 @@ const char *RunAsIntFunctionWrapperName = "__llvm_orc_bootstrap_run_as_int_function_wrapper"; } // end namespace rt +namespace rt_alt { +const char *UnwindInfoManagerInstanceName = + "orc_rt_alt_UnwindInfoManager_Instance"; +const char *UnwindInfoManagerFindSectionsHelperName = + "orc_rt_alt_UnwindInfoManager_findSectionsHelper"; +const char *UnwindInfoManagerEnableWrapperName = + "orc_rt_alt_UnwindInfoManager_enable"; +const char *UnwindInfoManagerDisableWrapperName = + "orc_rt_alt_UnwindInfoManager_disable"; +const char *UnwindInfoManagerRegisterActionName = + "orc_rt_alt_UnwindInfoManager_register"; +const char *UnwindInfoManagerDeregisterActionName = + "orc_rt_alt_UnwindInfoManager_deregister"; +} // end namespace rt_alt } // end namespace orc } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt index 3d1dfe758c79d..282d4cfc2ddad 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt @@ -11,6 +11,7 @@ endif() add_llvm_component_library(LLVMOrcTargetProcess ExecutorSharedMemoryMapperService.cpp + DefaultHostBootstrapValues.cpp JITLoaderGDB.cpp JITLoaderPerf.cpp JITLoaderVTune.cpp @@ -20,6 +21,7 @@ add_llvm_component_library(LLVMOrcTargetProcess SimpleExecutorMemoryManager.cpp SimpleRemoteEPCServer.cpp TargetExecutionUtils.cpp + UnwindInfoManager.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp new file mode 100644 index 0000000000000..c95b7ac5159fe --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp @@ -0,0 +1,36 @@ +//===----- DefaultHostBootstrapValues.cpp - Defaults for host process -----===// +// +// 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/DefaultHostBootstrapValues.h" + +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" + +#ifdef __APPLE__ +#include +#endif // __APPLE__ + +namespace llvm::orc { + +void addDefaultBootstrapValuesForHostProcess( + StringMap> &BootstrapMap, + StringMap &BootstrapSymbols) { + + // FIXME: We probably shouldn't set these on Windows? + BootstrapSymbols[rt::RegisterEHFrameSectionWrapperName] = + ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionWrapper); + BootstrapSymbols[rt::DeregisterEHFrameSectionWrapperName] = + ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionWrapper); + +#ifdef __APPLE__ + if (!dlsym(RTLD_DEFAULT, "__unw_add_find_dynamic_unwind_sections")) + BootstrapMap["darwin-use-ehframes-only"].push_back(1); +#endif // __APPLE__ +} + +} // namespace llvm::orc diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp index d88fbbfc86385..c4f201b353d27 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp @@ -106,10 +106,6 @@ void addTo(StringMap &M) { ExecutorAddr::fromPtr(&writeBuffersWrapper); M[rt::MemoryWritePointersWrapperName] = ExecutorAddr::fromPtr(&writePointersWrapper); - M[rt::RegisterEHFrameSectionWrapperName] = - ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionWrapper); - M[rt::DeregisterEHFrameSectionWrapperName] = - ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionWrapper); M[rt::RunAsMainWrapperName] = ExecutorAddr::fromPtr(&runAsMainWrapper); M[rt::RunAsVoidFunctionWrapperName] = ExecutorAddr::fromPtr(&runAsVoidFunctionWrapper); diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.cpp new file mode 100644 index 0000000000000..9f748154c03e5 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.cpp @@ -0,0 +1,188 @@ +//===------- UnwindInfoManager.cpp - Register unwind info sections --------===// +// +// 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/UnwindInfoManager.h" +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" +#include "llvm/Support/DynamicLibrary.h" + +#define DEBUG_TYPE "orc" + +using namespace llvm; +using namespace llvm::orc; +using namespace llvm::orc::shared; + +static orc::shared::CWrapperFunctionResult +llvm_orc_rt_alt_UnwindInfoManager_enable(const char *Data, uint64_t Size) { + return WrapperFunction::handle( + Data, Size, + [](ExecutorAddr Instance, ExecutorAddr FindFn) { + return Instance.toPtr()->enable( + FindFn.toPtr()); + }) + .release(); +} + +static orc::shared::CWrapperFunctionResult +llvm_orc_rt_alt_UnwindInfoManager_disable(const char *Data, uint64_t Size) { + return WrapperFunction::handle( + Data, Size, + [](ExecutorAddr Instance) { + return Instance.toPtr()->disable(); + }) + .release(); +} + +static orc::shared::CWrapperFunctionResult +llvm_orc_rt_alt_UnwindInfoManager_register(const char *Data, uint64_t Size) { + using SPSSig = + SPSError(SPSExecutorAddr, SPSSequence, + SPSExecutorAddr, SPSExecutorAddrRange, SPSExecutorAddrRange); + + return WrapperFunction::handle( + Data, Size, + [](ExecutorAddr Instance, + std::vector CodeRanges, ExecutorAddr DSOBase, + ExecutorAddrRange DWARFRange, + ExecutorAddrRange CompactUnwindRange) { + return Instance.toPtr()->registerSections( + CodeRanges, DSOBase, DWARFRange, CompactUnwindRange); + }) + .release(); +} + +static orc::shared::CWrapperFunctionResult +llvm_orc_rt_alt_UnwindInfoManager_deregister(const char *Data, uint64_t Size) { + using SPSSig = SPSError(SPSExecutorAddr, SPSSequence); + + return WrapperFunction::handle( + Data, Size, + [](ExecutorAddr Instance, + std::vector CodeRanges) { + return Instance.toPtr()->deregisterSections( + CodeRanges); + }) + .release(); +} + +namespace llvm::orc { + +const char *UnwindInfoManager::AddFnName = + "__unw_add_find_dynamic_unwind_sections"; +const char *UnwindInfoManager::RemoveFnName = + "__unw_remove_find_dynamic_unwind_sections"; + +std::unique_ptr UnwindInfoManager::TryCreate() { + std::string ErrMsg; + auto DL = sys::DynamicLibrary::getPermanentLibrary(nullptr, &ErrMsg); + if (!DL.isValid()) + return nullptr; + + auto AddFindDynamicUnwindSections = + (int (*)(void *))DL.getAddressOfSymbol(AddFnName); + if (!AddFindDynamicUnwindSections) + return nullptr; + + auto RemoveFindDynamicUnwindSections = + (int (*)(void *))DL.getAddressOfSymbol(RemoveFnName); + if (!RemoveFindDynamicUnwindSections) + return nullptr; + + return std::unique_ptr(new UnwindInfoManager( + AddFindDynamicUnwindSections, RemoveFindDynamicUnwindSections)); +} + +Error UnwindInfoManager::shutdown() { return Error::success(); } + +void UnwindInfoManager::addBootstrapSymbols(StringMap &M) { + M[rt_alt::UnwindInfoManagerInstanceName] = ExecutorAddr::fromPtr(this); + M[rt_alt::UnwindInfoManagerFindSectionsHelperName] = + ExecutorAddr::fromPtr(&findSectionsHelper); + M[rt_alt::UnwindInfoManagerEnableWrapperName] = + ExecutorAddr::fromPtr(llvm_orc_rt_alt_UnwindInfoManager_enable); + M[rt_alt::UnwindInfoManagerDisableWrapperName] = + ExecutorAddr::fromPtr(llvm_orc_rt_alt_UnwindInfoManager_disable); + M[rt_alt::UnwindInfoManagerRegisterActionName] = + ExecutorAddr::fromPtr(llvm_orc_rt_alt_UnwindInfoManager_register); + M[rt_alt::UnwindInfoManagerDeregisterActionName] = + ExecutorAddr::fromPtr(llvm_orc_rt_alt_UnwindInfoManager_deregister); +} + +Error UnwindInfoManager::enable(void *FindDynamicUnwindSections) { + LLVM_DEBUG(dbgs() << "Enabling UnwindInfoManager.\n"); + + if (auto Err = AddFindDynamicUnwindSections(FindDynamicUnwindSections)) + return make_error(Twine("Could not register function via ") + + AddFnName + + ", error code = " + Twine(Err), + inconvertibleErrorCode()); + + this->FindDynamicUnwindSections = FindDynamicUnwindSections; + return Error::success(); +} + +Error UnwindInfoManager::disable(void) { + LLVM_DEBUG(dbgs() << "Disabling UnwindInfoManager.\n"); + + if (FindDynamicUnwindSections) + if (auto Err = RemoveFindDynamicUnwindSections(FindDynamicUnwindSections)) + return make_error( + Twine("Could not deregister function via ") + RemoveFnName + + "error code = " + Twine(Err), + inconvertibleErrorCode()); + + FindDynamicUnwindSections = nullptr; + return Error::success(); +} + +Error UnwindInfoManager::registerSections( + ArrayRef CodeRanges, ExecutorAddr DSOBase, + ExecutorAddrRange DWARFEHFrame, ExecutorAddrRange CompactUnwind) { + std::lock_guard Lock(M); + for (auto &R : CodeRanges) + UWSecs[R.Start.getValue()] = + UnwindSections{static_cast(DSOBase.getValue()), + static_cast(DWARFEHFrame.Start.getValue()), + static_cast(DWARFEHFrame.size()), + static_cast(CompactUnwind.Start.getValue()), + static_cast(CompactUnwind.size())}; + return Error::success(); +} + +Error UnwindInfoManager::deregisterSections( + ArrayRef CodeRanges) { + std::lock_guard Lock(M); + for (auto &R : CodeRanges) { + auto I = UWSecs.find(R.Start.getValue()); + if (I == UWSecs.end()) + return make_error( + "No unwind-info sections registered for range " + + formatv("{0:x} - {1:x}", R.Start, R.End), + inconvertibleErrorCode()); + UWSecs.erase(I); + } + return Error::success(); +} + +int UnwindInfoManager::findSections(uintptr_t Addr, UnwindSections *Info) { + std::lock_guard Lock(M); + auto I = UWSecs.upper_bound(Addr); + if (I == UWSecs.begin()) + return 0; + --I; + *Info = I->second; + return 1; +} + +int UnwindInfoManager::findSectionsHelper(UnwindInfoManager *Instance, + uintptr_t Addr, + UnwindSections *Info) { + return Instance->findSections(Addr, Info); +} + +} // namespace llvm::orc diff --git a/llvm/lib/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.cpp new file mode 100644 index 0000000000000..ae1f3f98269db --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.cpp @@ -0,0 +1,238 @@ +//===----- UnwindInfoRegistrationPlugin.cpp - libunwind 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/UnwindInfoRegistrationPlugin.h" + +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h" +#include "llvm/ExecutionEngine/Orc/Shared/MachOObjectFormat.h" +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" + +#define DEBUG_TYPE "orc" + +using namespace llvm::jitlink; + +static const char *FindDynamicUnwindSectionsFunctionName = + "_orc_rt_alt_find_dynamic_unwind_sections"; + +namespace llvm::orc { + +Expected> +UnwindInfoRegistrationPlugin::Create(IRLayer &IRL, JITDylib &PlatformJD, + ExecutorAddr Instance, + ExecutorAddr FindHelper, + ExecutorAddr Enable, ExecutorAddr Disable, + ExecutorAddr Register, + ExecutorAddr Deregister) { + + auto &ES = IRL.getExecutionSession(); + + // Build bouncer module. + auto M = makeBouncerModule(ES); + if (!M) + return M.takeError(); + + auto BouncerRT = PlatformJD.createResourceTracker(); + auto RemoveBouncerModule = make_scope_exit([&]() { + if (auto Err = BouncerRT->remove()) + ES.reportError(std::move(Err)); + }); + + if (auto Err = PlatformJD.define(absoluteSymbols( + {{ES.intern(rt_alt::UnwindInfoManagerInstanceName), + ExecutorSymbolDef(Instance, JITSymbolFlags())}, + {ES.intern(rt_alt::UnwindInfoManagerFindSectionsHelperName), + ExecutorSymbolDef(FindHelper, JITSymbolFlags::Callable)}}))) + return std::move(Err); + + if (auto Err = IRL.add(BouncerRT, std::move(*M))) + return Err; + + auto FindUnwindSections = + ES.lookup({&PlatformJD}, FindDynamicUnwindSectionsFunctionName); + if (!FindUnwindSections) + return FindUnwindSections.takeError(); + + using namespace shared; + using SPSEnableSig = SPSError(SPSExecutorAddr, SPSExecutorAddr); + Error CallErr = Error::success(); + if (auto Err = ES.callSPSWrapper( + Enable, CallErr, Instance, FindUnwindSections->getAddress())) { + consumeError(std::move(CallErr)); + return std::move(Err); + } + + if (CallErr) + return std::move(CallErr); + + RemoveBouncerModule.release(); + + return std::shared_ptr( + new UnwindInfoRegistrationPlugin(ES, Instance, Disable, Register, + Deregister)); +} + +Expected> +UnwindInfoRegistrationPlugin::Create(IRLayer &IRL, JITDylib &PlatformJD) { + + ExecutorAddr Instance, FindHelper, Enable, Disable, Register, Deregister; + + auto &EPC = IRL.getExecutionSession().getExecutorProcessControl(); + if (auto Err = EPC.getBootstrapSymbols( + {{Instance, rt_alt::UnwindInfoManagerInstanceName}, + {FindHelper, rt_alt::UnwindInfoManagerFindSectionsHelperName}, + {Enable, rt_alt::UnwindInfoManagerEnableWrapperName}, + {Disable, rt_alt::UnwindInfoManagerDisableWrapperName}, + {Register, rt_alt::UnwindInfoManagerRegisterActionName}, + {Deregister, rt_alt::UnwindInfoManagerDeregisterActionName}})) + return std::move(Err); + + return Create(IRL, PlatformJD, Instance, FindHelper, Enable, Disable, + Register, Deregister); +} + +UnwindInfoRegistrationPlugin::~UnwindInfoRegistrationPlugin() { + using namespace shared; + using SPSDisableSig = SPSError(SPSExecutorAddr); + Error CallErr = Error::success(); + if (auto Err = ES.callSPSWrapper(Disable, CallErr, Instance)) { + consumeError(std::move(CallErr)); + ES.reportError(std::move(Err)); + } + if (CallErr) + ES.reportError(std::move(CallErr)); +} + +void UnwindInfoRegistrationPlugin::modifyPassConfig( + MaterializationResponsibility &MR, LinkGraph &G, + PassConfiguration &PassConfig) { + + PassConfig.PostFixupPasses.push_back( + [this](LinkGraph &G) { return addUnwindInfoRegistrationActions(G); }); +} + +Expected +UnwindInfoRegistrationPlugin::makeBouncerModule(ExecutionSession &ES) { + auto Ctx = std::make_unique(); + auto M = std::make_unique("__libunwind_find_unwind_bouncer", *Ctx); + M->setTargetTriple(ES.getTargetTriple().str()); + + auto EscapeName = [](const char *N) { return std::string("\01") + N; }; + + auto *PtrTy = PointerType::getUnqual(*Ctx); + auto *OpaqueStructTy = StructType::create(*Ctx, "UnwindInfoMgr"); + auto *UnwindMgrInstance = new GlobalVariable( + *M, OpaqueStructTy, true, GlobalValue::ExternalLinkage, nullptr, + EscapeName(rt_alt::UnwindInfoManagerInstanceName)); + + auto *Int64Ty = Type::getInt64Ty(*Ctx); + auto *FindHelperTy = FunctionType::get(Int64Ty, {PtrTy, PtrTy, PtrTy}, false); + auto *FindHelperFn = Function::Create( + FindHelperTy, GlobalValue::ExternalLinkage, + EscapeName(rt_alt::UnwindInfoManagerFindSectionsHelperName), *M); + + auto *FindFnTy = FunctionType::get(Int64Ty, {PtrTy, PtrTy}, false); + auto *FindFn = + Function::Create(FindFnTy, GlobalValue::ExternalLinkage, + EscapeName(FindDynamicUnwindSectionsFunctionName), *M); + auto *EntryBlock = BasicBlock::Create(M->getContext(), StringRef(), FindFn); + IRBuilder<> IB(EntryBlock); + + std::vector FindHelperArgs; + FindHelperArgs.push_back(UnwindMgrInstance); + for (auto &Arg : FindFn->args()) + FindHelperArgs.push_back(&Arg); + + IB.CreateRet(IB.CreateCall(FindHelperFn, FindHelperArgs)); + + return ThreadSafeModule(std::move(M), std::move(Ctx)); +} + +Error UnwindInfoRegistrationPlugin::addUnwindInfoRegistrationActions( + LinkGraph &G) { + ExecutorAddrRange EHFrameRange, UnwindInfoRange; + + std::vector CodeBlocks; + + auto ScanUnwindInfoSection = [&](Section &Sec, ExecutorAddrRange &SecRange) { + if (Sec.empty()) + return; + + SecRange.Start = (*Sec.blocks().begin())->getAddress(); + for (auto *B : Sec.blocks()) { + auto R = B->getRange(); + SecRange.Start = std::min(SecRange.Start, R.Start); + SecRange.End = std::max(SecRange.End, R.End); + for (auto &E : B->edges()) { + if (E.getKind() != Edge::KeepAlive || !E.getTarget().isDefined()) + continue; + auto &TargetBlock = E.getTarget().getBlock(); + auto &TargetSection = TargetBlock.getSection(); + if ((TargetSection.getMemProt() & MemProt::Exec) == MemProt::Exec) + CodeBlocks.push_back(&TargetBlock); + } + } + }; + + if (auto *EHFrame = G.findSectionByName(MachOEHFrameSectionName)) + ScanUnwindInfoSection(*EHFrame, EHFrameRange); + + if (auto *UnwindInfo = G.findSectionByName(MachOUnwindInfoSectionName)) + ScanUnwindInfoSection(*UnwindInfo, UnwindInfoRange); + + if (CodeBlocks.empty()) + return Error::success(); + + if ((EHFrameRange == ExecutorAddrRange() && + UnwindInfoRange == ExecutorAddrRange())) + return Error::success(); + + llvm::sort(CodeBlocks, [](const Block *LHS, const Block *RHS) { + return LHS->getAddress() < RHS->getAddress(); + }); + + SmallVector CodeRanges; + for (auto *B : CodeBlocks) { + if (CodeRanges.empty() || CodeRanges.back().End != B->getAddress()) + CodeRanges.push_back(B->getRange()); + else + CodeRanges.back().End = B->getRange().End; + } + + ExecutorAddr DSOBase; + if (auto *DSOBaseSym = G.findAbsoluteSymbolByName(DSOBaseName)) + DSOBase = DSOBaseSym->getAddress(); + else if (auto *DSOBaseSym = G.findExternalSymbolByName(DSOBaseName)) + DSOBase = DSOBaseSym->getAddress(); + else if (auto *DSOBaseSym = G.findDefinedSymbolByName(DSOBaseName)) + DSOBase = DSOBaseSym->getAddress(); + else + return make_error("In " + G.getName() + + " could not find dso base symbol", + inconvertibleErrorCode()); + + using namespace shared; + using SPSRegisterArgs = + SPSArgList, + SPSExecutorAddr, SPSExecutorAddrRange, SPSExecutorAddrRange>; + using SPSDeregisterArgs = + SPSArgList>; + + G.allocActions().push_back( + {cantFail(WrapperFunctionCall::Create( + Register, Instance, CodeRanges, DSOBase, EHFrameRange, + UnwindInfoRange)), + cantFail(WrapperFunctionCall::Create( + Deregister, Instance, CodeRanges))}); + + return Error::success(); +} + +} // namespace llvm::orc diff --git a/llvm/test/ExecutionEngine/Orc/throw-catch-minimal.ll b/llvm/test/ExecutionEngine/Orc/throw-catch-minimal.ll new file mode 100644 index 0000000000000..4ee55c61a81ea --- /dev/null +++ b/llvm/test/ExecutionEngine/Orc/throw-catch-minimal.ll @@ -0,0 +1,52 @@ +; REQUIRES: system-darwin && host-unwind-supports-jit +; RUN: lli -jit-kind=orc %s +; +; Basic correctness testing for eh-frame processing and registration. + +@_ZTIi = external constant ptr + +declare ptr @__cxa_allocate_exception(i64) +declare void @__cxa_throw(ptr, ptr, ptr) + +declare i32 @__gxx_personality_v0(...) +declare i32 @llvm.eh.typeid.for(ptr) +declare ptr @__cxa_begin_catch(ptr) +declare void @__cxa_end_catch() + +define void @explode() { +entry: + %exception = tail call ptr @__cxa_allocate_exception(i64 4) + store i32 42, ptr %exception, align 16 + tail call void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) + unreachable +} + +define i32 @main(i32 %argc, ptr %argv) personality ptr @__gxx_personality_v0 { +entry: + invoke void @explode() + to label %return unwind label %lpad + +lpad: + %0 = landingpad { ptr, i32 } + catch ptr @_ZTIi + %1 = extractvalue { ptr, i32 } %0, 1 + %2 = tail call i32 @llvm.eh.typeid.for(ptr @_ZTIi) + %matches = icmp eq i32 %1, %2 + br i1 %matches, label %catch, label %eh.resume + +catch: + %3 = extractvalue { ptr, i32 } %0, 0 + %4 = tail call ptr @__cxa_begin_catch(ptr %3) + %5 = load i32, ptr %4, align 4 + %cmp = icmp ne i32 %5, 42 + %cond = zext i1 %cmp to i32 + tail call void @__cxa_end_catch() + br label %return + +return: + %retval.0 = phi i32 [ %cond, %catch ], [ 2, %entry ] + ret i32 %retval.0 + +eh.resume: + resume { ptr, i32 } %0 +} diff --git a/llvm/test/ExecutionEngine/Orc/throw-catch-no-frame-pointer.ll b/llvm/test/ExecutionEngine/Orc/throw-catch-no-frame-pointer.ll new file mode 100644 index 0000000000000..8032e71ad2f35 --- /dev/null +++ b/llvm/test/ExecutionEngine/Orc/throw-catch-no-frame-pointer.ll @@ -0,0 +1,51 @@ +; REQUIRES: system-darwin && host-unwind-supports-jit +; RUN: lli -jit-kind=orc %s +; +; Check that we can throw exceptions from no-fp functions. On systems that +; support compact-unwind this implicitly tests that we correctly handle +; unwind-info records that depend on DWARF FDEs. + +@_ZTIi = external constant ptr + +declare ptr @__cxa_allocate_exception(i64) +declare void @__cxa_throw(ptr, ptr, ptr) +declare ptr @__cxa_begin_catch(ptr) +declare void @__cxa_end_catch() +declare i32 @__gxx_personality_v0(...) +declare i32 @llvm.eh.typeid.for.p0(ptr) + +define void @_Z3foov() "frame-pointer"="none" { +entry: + %exception = tail call ptr @__cxa_allocate_exception(i64 4) + store i32 42, ptr %exception + tail call void @__cxa_throw(ptr %exception, ptr nonnull @_ZTIi, ptr null) + unreachable +} + +define i32 @main(i32 %argc, ptr %argv) "frame-pointer"="all" personality ptr @__gxx_personality_v0 { +entry: + invoke void @_Z3foov() + to label %return.unreachable unwind label %lpad + +lpad: + %0 = landingpad { ptr, i32 } + catch ptr @_ZTIi + %1 = extractvalue { ptr, i32 } %0, 1 + %2 = tail call i32 @llvm.eh.typeid.for.p0(ptr nonnull @_ZTIi) + %matches = icmp eq i32 %1, %2 + br i1 %matches, label %catch, label %eh.resume + +catch: + %3 = extractvalue { ptr, i32 } %0, 0 + %4 = tail call ptr @__cxa_begin_catch(ptr %3) + %5 = load i32, ptr %4 + %sub = sub nsw i32 42, %5 + tail call void @__cxa_end_catch() + ret i32 %sub + +return.unreachable: + unreachable + +eh.resume: + resume { ptr, i32 } %0 +} diff --git a/llvm/test/ExecutionEngine/OrcLazy/minimal-throw-catch.ll b/llvm/test/ExecutionEngine/OrcLazy/minimal-throw-catch.ll index 5bc5769a2c0d2..83d2d89a5d607 100644 --- a/llvm/test/ExecutionEngine/OrcLazy/minimal-throw-catch.ll +++ b/llvm/test/ExecutionEngine/OrcLazy/minimal-throw-catch.ll @@ -1,12 +1,8 @@ -; REQUIRES: x86_64-apple +; REQUIRES: system-darwin && host-unwind-supports-jit ; RUN: lli -jit-kind=orc-lazy %s ; ; Basic correctness testing for eh-frame processing and registration. -source_filename = "minimal-throw-catch.cpp" -target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" -target triple = "x86_64-apple-macosx10.14.0" - @_ZTIi = external constant ptr declare ptr @__cxa_allocate_exception(i64) diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py index 3c0069d10412a..aad7a088551b2 100644 --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -566,6 +566,54 @@ def have_ld64_plugin_support(): if have_ld64_plugin_support(): config.available_features.add("ld64_plugin") +def host_unwind_supports_jit(): + # Do we expect the host machine to support JIT registration of clang's + # default unwind info format for the host (e.g. eh-frames, compact-unwind, + # etc.). + + # Linux and the BSDs use DWARF eh-frames and all known unwinders support + # register_frame at minimum. + if platform.system() in [ "Linux", "FreeBSD", "NetBSD" ]: + return True + + # Windows does not support frame info without the ORC runtime. + if platform.system() == "Windows": + return False + + # On Darwin/x86-64 clang produces both eh-frames and compact-unwind, and + # libunwind supports register_frame. On Darwin/arm64 clang produces + # compact-unwind only, and JIT'd registration is not available before + # macOS 14.0. + if platform.system() == "Darwin": + + assert ( + "arm64" in config.host_triple + or "x86_64" in config.host_triple + ) + + if "x86_64" in config.host_triple: + return True + + # Must be arm64. Check the macOS version. + try: + osx_version = subprocess.check_output( + ["sw_vers", "-productVersion"], universal_newlines=True + ) + osx_version = tuple(int(x) for x in osx_version.split(".")) + if len(osx_version) == 2: + osx_version = (osx_version[0], osx_version[1], 0) + if osx_version >= (14, 0): + return True + except: + pass + + return False + + return False + +if host_unwind_supports_jit(): + config.available_features.add("host-unwind-supports-jit") + # Ask llvm-config about asserts llvm_config.feature_config( [ 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 index 86b89a38c1760..dbdec77327774 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS +#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" @@ -187,6 +188,8 @@ int main(int argc, char *argv[]) { std::make_unique()); S.bootstrapSymbols() = SimpleRemoteEPCServer::defaultBootstrapSymbols(); + addDefaultBootstrapValuesForHostProcess(S.bootstrapMap(), + S.bootstrapSymbols()); S.services().push_back( std::make_unique()); S.services().push_back( diff --git a/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp b/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp index fb60acddf7821..c760873493528 100644 --- a/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp +++ b/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp @@ -54,6 +54,36 @@ TEST(LinkGraphTest, AddressAccess) { EXPECT_EQ(B1.getFixupAddress(E1), B1Addr + 8) << "Incorrect fixup address"; } +TEST(LinkGraphTest, DefinedSymbolProperties) { + // Check that Section::empty behaves as expected. + LinkGraph G("foo", std::make_shared(), + Triple("x86_64-apple-darwin"), SubtargetFeatures(), + getGenericEdgeKindName); + auto &Sec = + G.createSection("__data", orc::MemProt::Read | orc::MemProt::Write); + auto &B = + G.createContentBlock(Sec, BlockContent, orc::ExecutorAddr(0x1000), 8, 0); + auto &S = G.addDefinedSymbol(B, 0, "sym", 4, Linkage::Strong, Scope::Default, + false, false); + + EXPECT_TRUE(S.hasName()); + EXPECT_EQ(*S.getName(), "sym"); + EXPECT_TRUE(S.isDefined()); + EXPECT_FALSE(S.isLive()); + EXPECT_FALSE(S.isCallable()); + EXPECT_FALSE(S.isExternal()); + EXPECT_FALSE(S.isAbsolute()); + EXPECT_EQ(&S.getBlock(), &B); + EXPECT_EQ(&S.getSection(), &Sec); + EXPECT_EQ(S.getOffset(), 0U); + EXPECT_EQ(S.getSize(), 4U); + EXPECT_EQ(S.getRange(), orc::ExecutorAddrRange(B.getAddress(), 4)); + EXPECT_EQ(S.getSymbolContent(), BlockContent.slice(0, 4)); + EXPECT_EQ(S.getLinkage(), Linkage::Strong); + EXPECT_EQ(S.getScope(), Scope::Default); + EXPECT_EQ(S.getTargetFlags(), 0U); +} + TEST(LinkGraphTest, SectionEmpty) { // Check that Section::empty behaves as expected. LinkGraph G("foo", std::make_shared(),