Skip to content

Commit 4f03258

Browse files
committed
[ORC] Enable JIT support for the compact-unwind frame info format on Darwin.
For Darwin/arm64 (including Apple Silicon Macs) this will enable exception handling and stack unwinding in JIT'd code. Darwin supports two unwind-info formats: DWARF eh-frames and compact-unwind. On Darwin/x86-64 compilers usually produce both by default, and ORC supported exceptions and unwinding via eh-frames (same as on Linux), discarding the redundant compact-unwind info. On Darwin/arm64 compilers typically default to producing compact-unwind only, with DWARF eh-frames as a fallback for functions that can't be described in compact-unwind. Since ORC did not previously support the compact-unwind format and eh-frames were not present ORC was unable to handle exceptions or unwinding by default in Darwin/arm64 JIT'd code. This patch enables support for the compact-unwind-info format, and contains three major moving parts: (1) The JITLink CompactUnwindManager class is responsible for transforming the __compact_unwind records produced by the linker into the __unwind_info tables that libunwind parses during unwinding. To enable this the CompactUnwindManager class provides three JITLink passes: The prepareForPrune pass that splits the __compact_unwind section into single-record blocks, allowing unused records to be dead-stripped; the processAndReserveUnwindInfo pass that reserves space for the final __unwind_info section, and the writeUnwindInfo pass that writes the __unwind_info section. (2) The OrcTargetProcess UnwindInfoManager class maintains a table of registered JIT'd __unwind_info and __eh_frame sections, and handles requests from libunwind for unwind info sections (by registering a callback with libunwind's __unw_add_find_dynamic_unwind_sections function). (3) The Orc UnwindInfoRegistrationPlugin, which scans LinkGraphs for __unwind_info and __eh_frame sections to register with the UnwindInfoManager. This commit adds the CompactUnwindManager passes to the default JITLink pipelines for Darwin/arm64 and Darwin/x86-64, and UnwindInfoManager intances to the SelfExecutorProcessControl class (when built for apple platforms) and the llvm-jitlink-executor tool. The LLJIT class will now create an UnwindInfoRegistrationPlugin when targeting a process running on Darwin if it detects that an UnwindInfoManager is available to handle the registrations. The ORC runtime macho_platform class already supported libunwind callbacks, so out-of-process execution and unwinding support will work when loading the ORC runtime. The llvm-jitlink tool will only support compact-unwind when the orc-runtime is loaded, as the UnwindInfoRegistrationPlugin requires access to an IR compiler to load a helper module and llvm-jitlink does not provide an IR compiler.
1 parent 25825d4 commit 4f03258

21 files changed

+1500
-149
lines changed

compiler-rt/lib/orc/macho_platform.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,12 @@ Error MachOPlatformRuntimeState::registerObjectPlatformSections(
557557
return make_error<StringError>(ErrStream.str());
558558
}
559559

560+
ORC_RT_DEBUG({
561+
printdbg(" UnwindInfo: %s, UseCallbackStyleUnwindInfo: %s\n",
562+
UnwindInfo ? "true" : "false",
563+
UseCallbackStyleUnwindInfo ? "true" : "false");
564+
});
565+
560566
if (UnwindInfo && UseCallbackStyleUnwindInfo) {
561567
ORC_RT_DEBUG({
562568
printdbg(" Registering new-style unwind info for:\n"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clangxx -c -o %t %s
2+
// RUN: %llvm_jitlink -slab-allocate=20Mb %t
3+
//
4+
// REQUIRES: system-darwin && host-arch-compatible
5+
6+
int main(int argc, char *argv[]) {
7+
try {
8+
throw 42;
9+
} catch (int E) {
10+
return 42 - E;
11+
}
12+
return 1;
13+
}

llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
2121
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
2222
#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
23+
#include "llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h"
2324
#include "llvm/ExecutionEngine/Orc/TaskDispatch.h"
2425
#include "llvm/Support/DynamicLibrary.h"
2526
#include "llvm/Support/MSVCErrorWorkarounds.h"
@@ -507,6 +508,7 @@ class SelfExecutorProcessControl : public ExecutorProcessControl,
507508
SymbolLookupCompleteFn F) override;
508509

509510
std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
511+
std::unique_ptr<UnwindInfoManager> UnwindInfoMgr;
510512
char GlobalManglingPrefix = 0;
511513
};
512514

llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr,
8888
using SPSRunAsVoidFunctionSignature = int32_t(shared::SPSExecutorAddr);
8989
using SPSRunAsIntFunctionSignature = int32_t(shared::SPSExecutorAddr, int32_t);
9090
} // end namespace rt
91+
92+
namespace rt_alt {
93+
extern const char *UnwindInfoManagerInstanceName;
94+
extern const char *UnwindInfoManagerFindSectionsHelperName;
95+
extern const char *UnwindInfoManagerEnableWrapperName;
96+
extern const char *UnwindInfoManagerDisableWrapperName;
97+
extern const char *UnwindInfoManagerRegisterActionName;
98+
extern const char *UnwindInfoManagerDeregisterActionName;
99+
} // end namespace rt_alt
91100
} // end namespace orc
92101
} // end namespace llvm
93102

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===--- UnwindInfoManager.h -- Register unwind info sections ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Utilities for managing eh-frame and compact-unwind registration and lookup
10+
// through libunwind's find_dynamic_unwind_sections mechanism.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H
15+
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H
16+
17+
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
18+
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
19+
#include "llvm/Support/Error.h"
20+
#include <map>
21+
#include <mutex>
22+
23+
namespace llvm::orc {
24+
25+
class UnwindInfoManager : public ExecutorBootstrapService {
26+
public:
27+
// This struct's layout should match the unw_dynamic_unwind_sections struct
28+
// from libunwind/src/libunwid_ext.h.
29+
struct UnwindSections {
30+
uintptr_t dso_base;
31+
uintptr_t dwarf_section;
32+
size_t dwarf_section_length;
33+
uintptr_t compact_unwind_section;
34+
size_t compact_unwind_section_length;
35+
};
36+
37+
/// If the libunwind find-dynamic-unwind-info callback registration APIs are
38+
/// available then this method will return an UnwindInfoManager instance,
39+
/// otherwise it will return nullptr.
40+
static std::unique_ptr<UnwindInfoManager> TryCreate();
41+
42+
Error shutdown() override;
43+
void addBootstrapSymbols(StringMap<ExecutorAddr> &M) override;
44+
45+
Error enable(void *FindDynamicUnwindSections);
46+
Error disable(void);
47+
48+
Error registerSections(ArrayRef<orc::ExecutorAddrRange> CodeRanges,
49+
orc::ExecutorAddr DSOBase,
50+
orc::ExecutorAddrRange DWARFEHFrame,
51+
orc::ExecutorAddrRange CompactUnwind);
52+
53+
Error deregisterSections(ArrayRef<orc::ExecutorAddrRange> CodeRanges);
54+
55+
int findSections(uintptr_t Addr, UnwindSections *Info);
56+
57+
private:
58+
UnwindInfoManager(int (*AddFindDynamicUnwindSections)(void *),
59+
int (*RemoveFindDynamicUnwindSections)(void *))
60+
: AddFindDynamicUnwindSections(AddFindDynamicUnwindSections),
61+
RemoveFindDynamicUnwindSections(RemoveFindDynamicUnwindSections) {}
62+
63+
static int findSectionsHelper(UnwindInfoManager *Instance, uintptr_t Addr,
64+
UnwindSections *Info);
65+
66+
std::mutex M;
67+
std::map<uintptr_t, UnwindSections> UWSecs;
68+
69+
int (*AddFindDynamicUnwindSections)(void *) = nullptr;
70+
int (*RemoveFindDynamicUnwindSections)(void *) = nullptr;
71+
void *FindDynamicUnwindSections = nullptr;
72+
73+
static const char *AddFnName, *RemoveFnName;
74+
};
75+
76+
} // namespace llvm::orc
77+
78+
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_UNWINDINFOMANAGER_H
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===- UnwindInfoRegistrationPlugin.h -- libunwind registration -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Register eh-frame and compact-unwind sections with libunwind
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H
14+
#define LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H
15+
16+
#include "llvm/ExecutionEngine/Orc/LinkGraphLinkingLayer.h"
17+
18+
namespace llvm::orc {
19+
20+
class UnwindInfoRegistrationPlugin : public LinkGraphLinkingLayer::Plugin {
21+
public:
22+
static Expected<std::shared_ptr<UnwindInfoRegistrationPlugin>>
23+
Create(IRLayer &IRL, JITDylib &PlatformJD, ExecutorAddr Instance,
24+
ExecutorAddr FindHelper, ExecutorAddr Enable, ExecutorAddr Disable,
25+
ExecutorAddr Register, ExecutorAddr Deregister);
26+
27+
static Expected<std::shared_ptr<UnwindInfoRegistrationPlugin>>
28+
Create(IRLayer &IRL, JITDylib &PlatformJD);
29+
30+
~UnwindInfoRegistrationPlugin();
31+
32+
void modifyPassConfig(MaterializationResponsibility &MR,
33+
jitlink::LinkGraph &G,
34+
jitlink::PassConfiguration &PassConfig) override;
35+
36+
Error notifyEmitted(MaterializationResponsibility &MR) override {
37+
return Error::success();
38+
}
39+
40+
Error notifyFailed(MaterializationResponsibility &MR) override {
41+
return Error::success();
42+
}
43+
44+
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
45+
return Error::success();
46+
}
47+
48+
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
49+
ResourceKey SrcKey) override {}
50+
51+
private:
52+
UnwindInfoRegistrationPlugin(ExecutionSession &ES, ExecutorAddr Instance,
53+
ExecutorAddr Disable, ExecutorAddr Register,
54+
ExecutorAddr Deregister)
55+
: ES(ES), Instance(Instance), Disable(Disable), Register(Register),
56+
Deregister(Deregister) {
57+
DSOBaseName = ES.intern("__jitlink$libunwind_dso_base");
58+
}
59+
60+
static Expected<ThreadSafeModule> makeBouncerModule(ExecutionSession &ES);
61+
Error addUnwindInfoRegistrationActions(jitlink::LinkGraph &G);
62+
63+
ExecutionSession &ES;
64+
SymbolStringPtr DSOBaseName;
65+
ExecutorAddr Instance, Disable, Register, Deregister;
66+
};
67+
68+
} // namespace llvm::orc
69+
70+
#endif // LLVM_EXECUTIONENGINE_ORC_UNWINDINFOREGISTRATIONPLUGIN_H

llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ tablegen(LLVM COFFOptions.inc -gen-opt-parser-defs)
33
add_public_tablegen_target(JITLinkTableGen)
44

55
add_llvm_component_library(LLVMJITLink
6+
CompactUnwindSupport.cpp
67
DWARFRecordSectionSplitter.cpp
78
EHFrameSupport.cpp
89
JITLink.cpp
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//=------- CompactUnwindSupport.cpp - Compact Unwind format support -------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Compact Unwind support.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CompactUnwindSupport.h"
14+
15+
#include "llvm/ADT/Sequence.h"
16+
17+
#define DEBUG_TYPE "jitlink"
18+
19+
namespace llvm {
20+
namespace jitlink {
21+
22+
Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection,
23+
size_t RecordSize) {
24+
25+
std::vector<Block *> OriginalBlocks(CompactUnwindSection.blocks().begin(),
26+
CompactUnwindSection.blocks().end());
27+
LLVM_DEBUG({
28+
dbgs() << "In " << G.getName() << " splitting compact unwind section "
29+
<< CompactUnwindSection.getName() << " containing "
30+
<< OriginalBlocks.size() << " initial blocks...\n";
31+
});
32+
33+
while (!OriginalBlocks.empty()) {
34+
auto *B = OriginalBlocks.back();
35+
OriginalBlocks.pop_back();
36+
37+
if (B->getSize() == 0) {
38+
LLVM_DEBUG({
39+
dbgs() << " Skipping empty block at "
40+
<< formatv("{0:x16}", B->getAddress()) << "\n";
41+
});
42+
continue;
43+
}
44+
45+
unsigned NumBlocks = B->getSize() / RecordSize;
46+
47+
LLVM_DEBUG({
48+
dbgs() << " Splitting block at " << formatv("{0:x16}", B->getAddress())
49+
<< " into " << NumBlocks << " compact unwind record(s)\n";
50+
});
51+
52+
if (B->getSize() % RecordSize)
53+
return make_error<JITLinkError>(
54+
"Error splitting compact unwind record in " + G.getName() +
55+
": block at " + formatv("{0:x}", B->getAddress()) + " has size " +
56+
formatv("{0:x}", B->getSize()) +
57+
" (not a multiple of CU record size of " +
58+
formatv("{0:x}", RecordSize) + ")");
59+
60+
auto Blocks =
61+
G.splitBlock(*B, map_range(seq(1U, NumBlocks), [=](Edge::OffsetT Idx) {
62+
return Idx * RecordSize;
63+
}));
64+
65+
for (auto *CURec : Blocks) {
66+
bool AddedKeepAlive = false;
67+
68+
for (auto &E : CURec->edges()) {
69+
if (E.getOffset() == 0) {
70+
LLVM_DEBUG({
71+
dbgs() << " Updating compact unwind record at "
72+
<< CURec->getAddress() << " to point to "
73+
<< (E.getTarget().hasName() ? *E.getTarget().getName()
74+
: StringRef())
75+
<< " (at " << E.getTarget().getAddress() << ")\n";
76+
});
77+
78+
if (E.getTarget().isExternal())
79+
return make_error<JITLinkError>(
80+
"Error adding keep-alive edge for compact unwind record at " +
81+
formatv("{0:x}", CURec->getAddress()) + ": target " +
82+
*E.getTarget().getName() + " is an external symbol");
83+
auto &TgtBlock = E.getTarget().getBlock();
84+
auto &CURecSym =
85+
G.addAnonymousSymbol(*CURec, 0, RecordSize, false, false);
86+
TgtBlock.addEdge(Edge::KeepAlive, 0, CURecSym, 0);
87+
AddedKeepAlive = true;
88+
}
89+
}
90+
91+
if (!AddedKeepAlive)
92+
return make_error<JITLinkError>(
93+
"Error adding keep-alive edge for compact unwind record at " +
94+
formatv("{0:x}", CURec->getAddress()) +
95+
": no outgoing target edge at offset 0");
96+
}
97+
}
98+
99+
return Error::success();
100+
}
101+
102+
} // end namespace jitlink
103+
} // end namespace llvm

0 commit comments

Comments
 (0)