microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
chained_unwinder.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
9#include "dwarf_abi.hpp"
10#include "frame_pointer.hpp"
11#include "register_context.hpp"
12#include "unwind_hint.hpp"
13#include <cstdint>
14#include <tuple>
15
16namespace microfmt {
17
25
26template <typename AbiTraits> struct chained_unwinder_tag {};
27
28template <typename AbiTraits> struct frame_unwinder_traits<chained_unwinder_tag<AbiTraits>> {
30
41 static bool step(value_ref<const context_type> context, register_context_ref reg_ctx, uintptr_t current_pc,
42 uintptr_t &next_fp, uintptr_t &next_pc) noexcept {
43 if (!reg_ctx)
44 return false;
45
46 uintptr_t trial_fp = 0;
47 uintptr_t trial_pc = 0;
48
49 if (context->exidx_unwinder && context->exidx_unwinder.step(reg_ctx, current_pc, trial_fp, trial_pc)) {
50 next_fp = trial_fp;
51 next_pc = trial_pc;
52 return true;
53 }
54
55 if (context->dwarf_unwinder && context->dwarf_unwinder.step(reg_ctx, current_pc, trial_fp, trial_pc)) {
56 next_fp = trial_fp;
57 next_pc = trial_pc;
58 return true;
59 }
60
61 if (context->fp_unwinder && context->fp_unwinder.step(reg_ctx, current_pc, trial_fp, trial_pc)) {
62 next_fp = trial_fp;
63 next_pc = trial_pc;
64 return true;
65 }
66
67 if (!context->hints)
68 return false;
69
70 if (current_pc == 0)
71 return false;
72
73 unwind_hint hint{};
74 if (!context->hints.find_hint(AbiTraits::normalize_pc(current_pc), hint) || !hint.routine)
75 return false;
76
77 // The hint receives reg_ctx directly. Do not snapshot only FP/PC here:
78 // non-standard unwinders can keep addresses in, and recover values from,
79 // any GPR. The routine may also mutate the register context in-place.
80 if (!hint.routine(context->space, reg_ctx, next_fp, next_pc))
81 return false;
82
83 // reg_ctx is always a mutable scratch snapshot (a dead-process register
84 // view or a local ucontext_t/GPR-array copy), never a live process to
85 // resume. Hint routines are plain type-erased function pointers with no
86 // AbiTraits of their own, so a routine author may reasonably fill only
87 // next_fp/next_pc without also writing the fp/ra registers back into
88 // reg_ctx; without that write-back, a *subsequent* step() (through this
89 // same hint tier, or any other tier that reads the same registers) would
90 // silently re-read this frame's now-stale fp/ra and loop forever, as
91 // fp_unwinder_tag/dwarf_unwinder_tag did before they were fixed to write
92 // their own results back. chained_unwinder_tag knows AbiTraits, so it
93 // closes that gap here on every hint routine's behalf, best-effort (a
94 // read-only register context still lets this single step succeed via
95 // next_fp/next_pc, it just can't chain further through this tier).
96 const auto fp_value = static_cast<typename AbiTraits::register_type>(next_fp);
97 const auto ra_value = static_cast<typename AbiTraits::register_type>(next_pc);
98 std::ignore = reg_ctx.write_raw(resolve_fp_register<AbiTraits>(reg_ctx), &fp_value, AbiTraits::pointer_size);
99 std::ignore = reg_ctx.write_raw(AbiTraits::ra_reg, &ra_value, AbiTraits::pointer_size);
100
101 return true;
102 }
103};
104
105} // namespace microfmt
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Type-erased, two-word handle to a frame unwinder.
Definition frame_pointer.hpp:68
Zero-allocation, type-erased handle for inspecting and modifying CPU registers from a frame context.
Definition register_context.hpp:118
Type-erased, two-word handle to an unwind hint registry.
Definition unwind_hint.hpp:95
Per-architecture ABI traits describing registers used during DWARF unwinding.
Type-erased frame unwinder handles, frame cursor iteration, and backtrace views.
Definition assert.hpp:19
Definition chained_unwinder.hpp:18
frame_unwinder_ref exidx_unwinder
Definition chained_unwinder.hpp:20
frame_unwinder_ref fp_unwinder
Definition chained_unwinder.hpp:22
address_space_ref space
Definition chained_unwinder.hpp:19
frame_unwinder_ref dwarf_unwinder
Definition chained_unwinder.hpp:21
unwind_hint_registry_ref hints
Definition chained_unwinder.hpp:23
Definition chained_unwinder.hpp:26
static bool step(value_ref< const context_type > context, register_context_ref reg_ctx, uintptr_t current_pc, uintptr_t &next_fp, uintptr_t &next_pc) noexcept
Walks one frame by trying EXIDX, DWARF, frame-pointer, then hints.
Definition chained_unwinder.hpp:41
Static customization point describing a frame unwinder backend.
Definition frame_pointer.hpp:56
PC-range descriptor for direct, layout-based fallback unwinding.
Definition unwind_hint.hpp:35
PC-range unwind hint tables for direct, layout-based fallback unwinding.