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.
186 lines
6.8 KiB
C++
186 lines
6.8 KiB
C++
//===------- 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<SPSError(SPSExecutorAddr, SPSExecutorAddr)>::handle(
|
|
Data, Size,
|
|
[](ExecutorAddr Instance, ExecutorAddr FindFn) {
|
|
return Instance.toPtr<UnwindInfoManager *>()->enable(
|
|
FindFn.toPtr<void *>());
|
|
})
|
|
.release();
|
|
}
|
|
|
|
static orc::shared::CWrapperFunctionResult
|
|
llvm_orc_rt_alt_UnwindInfoManager_disable(const char *Data, uint64_t Size) {
|
|
return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(
|
|
Data, Size,
|
|
[](ExecutorAddr Instance) {
|
|
return Instance.toPtr<UnwindInfoManager *>()->disable();
|
|
})
|
|
.release();
|
|
}
|
|
|
|
static orc::shared::CWrapperFunctionResult
|
|
llvm_orc_rt_alt_UnwindInfoManager_register(const char *Data, uint64_t Size) {
|
|
using SPSSig =
|
|
SPSError(SPSExecutorAddr, SPSSequence<SPSExecutorAddrRange>,
|
|
SPSExecutorAddr, SPSExecutorAddrRange, SPSExecutorAddrRange);
|
|
|
|
return WrapperFunction<SPSSig>::handle(
|
|
Data, Size,
|
|
[](ExecutorAddr Instance,
|
|
std::vector<ExecutorAddrRange> CodeRanges, ExecutorAddr DSOBase,
|
|
ExecutorAddrRange DWARFRange,
|
|
ExecutorAddrRange CompactUnwindRange) {
|
|
return Instance.toPtr<UnwindInfoManager *>()->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<SPSExecutorAddrRange>);
|
|
|
|
return WrapperFunction<SPSSig>::handle(
|
|
Data, Size,
|
|
[](ExecutorAddr Instance,
|
|
std::vector<ExecutorAddrRange> CodeRanges) {
|
|
return Instance.toPtr<UnwindInfoManager *>()->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> 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<UnwindInfoManager>(new UnwindInfoManager(
|
|
AddFindDynamicUnwindSections, RemoveFindDynamicUnwindSections));
|
|
}
|
|
|
|
Error UnwindInfoManager::shutdown() { return Error::success(); }
|
|
|
|
void UnwindInfoManager::addBootstrapSymbols(StringMap<ExecutorAddr> &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<StringError>(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<StringError>(
|
|
Twine("Could not deregister function via ") + RemoveFnName +
|
|
"error code = " + Twine(Err),
|
|
inconvertibleErrorCode());
|
|
|
|
FindDynamicUnwindSections = nullptr;
|
|
return Error::success();
|
|
}
|
|
|
|
Error UnwindInfoManager::registerSections(
|
|
ArrayRef<ExecutorAddrRange> CodeRanges, ExecutorAddr DSOBase,
|
|
ExecutorAddrRange DWARFEHFrame, ExecutorAddrRange CompactUnwind) {
|
|
std::lock_guard<std::mutex> Lock(M);
|
|
for (auto &R : CodeRanges)
|
|
UWSecs[R.Start.getValue()] = UnwindSections{
|
|
DSOBase.getValue(), DWARFEHFrame.Start.getValue(), DWARFEHFrame.size(),
|
|
CompactUnwind.Start.getValue(), CompactUnwind.size()};
|
|
return Error::success();
|
|
}
|
|
|
|
Error UnwindInfoManager::deregisterSections(
|
|
ArrayRef<ExecutorAddrRange> CodeRanges) {
|
|
std::lock_guard<std::mutex> Lock(M);
|
|
for (auto &R : CodeRanges) {
|
|
auto I = UWSecs.find(R.Start.getValue());
|
|
if (I == UWSecs.end())
|
|
return make_error<StringError>(
|
|
"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<std::mutex> 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
|