blob: 87491b956ba3b7d9198755554cb8330e154c48ff [file] [edit]
//===- StandaloneMachOUnwindInfoRegistrar.cpp -----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Standalone registration service for MachO unwind info.
//
// Note: Should not be used together with MachO-Platform, which provides its
// own unwind-info registration.
//
//===----------------------------------------------------------------------===//
#include "orc-rt/StandaloneMachOUnwindInfoRegistrar.h"
#include "orc-rt/Compiler.h"
#include <mutex>
// --- From libunwind/src/libunwind_ext.h ---
struct unw_dynamic_unwind_sections;
using namespace orc_rt;
namespace {
typedef int (*unw_find_dynamic_unwind_sections)(
uintptr_t addr, struct unw_dynamic_unwind_sections *info);
extern "C" int __unw_add_find_dynamic_unwind_sections(
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections)
ORC_RT_WEAK_IMPORT;
extern "C" int __unw_remove_find_dynamic_unwind_sections(
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections)
ORC_RT_WEAK_IMPORT;
// --- end libunwind decls ---
std::mutex LibunwindRegistrationMtx;
size_t LibunwindRegistrationCount;
} // namespace
namespace orc_rt {
// === UnwindInfoMap method definitions ===
Error StandaloneMachOUnwindInfoRegistrar::UnwindInfoMap::registerRanges(
const std::vector<ExecutorAddrRange> &CodeRanges,
const DynamicUnwindSections &Info) {
std::scoped_lock<std::mutex> Lock(M);
for (auto &CodeRange : CodeRanges) {
// TODO: We're assuming safe conversion from ExecutorAddr to uintptr_t
// here. In the future we should either check, or make sure that
// invalid values aren't deserializable (by switching the wire
// representation to match the pointer size).
if (CodeRange.Start.getValue() > std::numeric_limits<uintptr_t>::max() ||
CodeRange.End.getValue() > std::numeric_limits<uintptr_t>::max())
return make_error<StringError>(
"Invalid code-range for unwind-info registration");
uintptr_t Start = CodeRange.Start.getValue();
uintptr_t End = CodeRange.End.getValue();
// Ignore empty ranges.
// FIXME: Should we hard-error on these instead?
if (Start == End)
continue;
// Check for overlap with neighboring ranges (including any added earlier
// in this call, since each successful insertion is visible to subsequent
// iterations).
auto MakeRangeOverlapError = [] {
return make_error<StringError>(
"Code-range for unwind-info registration overlaps an existing range");
};
auto I = Ranges.upper_bound(Start);
if (I != Ranges.end() && I->first < End)
return MakeRangeOverlapError();
if (I != Ranges.begin()) {
auto PrevI = std::prev(I);
if (PrevI->second.End > Start)
return MakeRangeOverlapError();
}
Ranges.emplace_hint(I, Start, Entry{Info, End});
}
return Error::success();
}
Error StandaloneMachOUnwindInfoRegistrar::UnwindInfoMap::deregisterRanges(
const std::vector<ExecutorAddrRange> &CodeRanges) {
std::scoped_lock<std::mutex> Lock(M);
for (auto &CodeRange : CodeRanges) {
auto I = Ranges.find(CodeRange.Start.getValue());
if (I == Ranges.end())
return make_error<StringError>(
"No unwind-info sections registered for range");
Ranges.erase(I);
}
return Error::success();
}
std::optional<StandaloneMachOUnwindInfoRegistrar::DynamicUnwindSections>
StandaloneMachOUnwindInfoRegistrar::UnwindInfoMap::lookup(
uintptr_t Addr) const {
std::scoped_lock<std::mutex> Lock(M);
auto I = Ranges.upper_bound(Addr);
if (I == Ranges.begin())
return std::nullopt;
--I;
if (Addr >= I->second.End)
return std::nullopt;
return I->second.Info;
}
// === Registrar libunwind plumbing ===
StandaloneMachOUnwindInfoRegistrar::UnwindInfoMap &
StandaloneMachOUnwindInfoRegistrar::unwindInfoMap() {
static UnwindInfoMap Map;
return Map;
}
int StandaloneMachOUnwindInfoRegistrar::findUnwindInfoSections(
uintptr_t Addr, DynamicUnwindSections *Info) {
auto S = unwindInfoMap().lookup(Addr);
if (!S)
return 0;
*Info = *S;
return 1;
}
Error StandaloneMachOUnwindInfoRegistrar::registerWithLibunwind() {
// Check whether the necessary libunwind APIs are available, as they were
// only introduced in macOS 15.
if (!__unw_add_find_dynamic_unwind_sections ||
!__unw_remove_find_dynamic_unwind_sections)
return make_error<StringError>(
"libunwind unwind-info registration APIs not available");
std::scoped_lock<std::mutex> Lock(LibunwindRegistrationMtx);
// If we've already registered then just bump the ref count and exit.
if (LibunwindRegistrationCount > 0) {
++LibunwindRegistrationCount;
return Error::success();
}
// Try to register.
// findUnwindInfoSections has the same call ABI as libunwind expects, since
// DynamicUnwindSections is laid out identically to
// unw_dynamic_unwind_sections, so the reinterpret_cast here should be safe
// in practice.
if (__unw_add_find_dynamic_unwind_sections(
reinterpret_cast<unw_find_dynamic_unwind_sections>(
findUnwindInfoSections)) != 0)
return make_error<StringError>(
"libunwind find-dynamic-unwind-sections registration failed");
// If we succeeded, bump the ref count.
++LibunwindRegistrationCount;
return Error::success();
}
void StandaloneMachOUnwindInfoRegistrar::deregisterWithLibunwind() {
std::scoped_lock<std::mutex> Lock(LibunwindRegistrationMtx);
if (--LibunwindRegistrationCount == 0)
__unw_remove_find_dynamic_unwind_sections(
reinterpret_cast<unw_find_dynamic_unwind_sections>(
findUnwindInfoSections));
}
// === Public API ===
StandaloneMachOUnwindInfoRegistrar::Registration::Registration(bool Active)
: Active(Active) {}
StandaloneMachOUnwindInfoRegistrar::Registration::Registration(
Registration &&Other)
: Active(Other.Active) {
Other.Active = false;
}
StandaloneMachOUnwindInfoRegistrar::Registration &
StandaloneMachOUnwindInfoRegistrar::Registration::operator=(
Registration &&Other) {
if (Active)
deregisterWithLibunwind();
Active = Other.Active;
Other.Active = false;
return *this;
}
StandaloneMachOUnwindInfoRegistrar::Registration::~Registration() {
if (Active)
deregisterWithLibunwind();
}
Expected<StandaloneMachOUnwindInfoRegistrar::Registration>
StandaloneMachOUnwindInfoRegistrar::enable(
SimpleSymbolTable &ST, SimpleSymbolTable::MutatorFn AddInterface) {
if (auto Err = registerWithLibunwind())
return std::move(Err);
Registration R(true); // Create an active registration object.
if (auto Err = AddInterface(ST))
return std::move(Err);
return std::move(R);
}
Error StandaloneMachOUnwindInfoRegistrar::registerSections(
std::vector<ExecutorAddrRange> CodeRanges, ExecutorAddr DSOBase,
ExecutorAddrRange DWARFEHFrame, ExecutorAddrRange CompactUnwind) {
DynamicUnwindSections Info{
DSOBase.getValue(), DWARFEHFrame.Start.getValue(),
DWARFEHFrame.size(), CompactUnwind.Start.getValue(),
CompactUnwind.size(),
};
return unwindInfoMap().registerRanges(CodeRanges, Info);
}
Error StandaloneMachOUnwindInfoRegistrar::deregisterSections(
std::vector<ExecutorAddrRange> CodeRanges) {
return unwindInfoMap().deregisterRanges(CodeRanges);
}
} // namespace orc_rt