microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
symbol_resolver.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
6
10#include "address_space.hpp"
11#include "demangle.hpp"
12#include <cstddef>
13#include <cstdint>
14#include <string_view>
15#include <type_traits>
16
17namespace microfmt {
18
19// ============================================================================
20// Raw Kernel / Platform Resolution Result
21// ============================================================================
22
30 microfmt::string_view symbol_name{""};
34 uintptr_t symbol_base{0};
38 bool is_exact{false};
39
43 microfmt::string_view image_name{""};
47 uintptr_t image_load_base{0};
48};
49
57 microfmt::string_view symbol_name{""};
61 uintptr_t symbol_base{0};
65 uintptr_t offset_from_symbol{0};
69 bool is_exact{false};
70
74 microfmt::string_view image_name{""};
78 uintptr_t image_load_base{0};
82 uintptr_t offset_from_image{0};
83
88 [[nodiscard]] constexpr bool has_symbol() const noexcept { return !symbol_name.empty(); }
89
94 [[nodiscard]] constexpr bool has_image() const noexcept { return !image_name.empty(); }
95};
96
101 constexpr explicit symbol_resolution_context(span<char> string_scratch RELOCO_LIFETIMEBOUND = {}) noexcept
102 : scratch(string_scratch) {}
103
104 template <size_t N>
105 constexpr explicit symbol_resolution_context(char (&string_scratch)[N]) noexcept : scratch(string_scratch, N) {}
106
107 span<char> scratch{};
110};
111
112// ============================================================================
113// Static Customization Point: symbol_resolver_traits
114// ============================================================================
115
120template <typename Tag> struct symbol_resolver_traits;
121
122// ============================================================================
123// Minimal-Stack Type-Erased Symbol Resolver (2 Words)
124// ============================================================================
125
132class RELOCO_POINTER symbol_resolver_ref {
133public:
137 struct vtable {
141 bool (*resolve)(const void *ctx, uintptr_t addr, span<char> scratch,
142 raw_resolved_symbol &out_raw) noexcept = nullptr;
143 };
144
148 constexpr symbol_resolver_ref() noexcept = default;
149
155 template <typename Tag, typename Traits = symbol_resolver_traits<Tag>,
156 std::enable_if_t<std::is_void_v<typename Traits::context_type>, int> = 0>
157 constexpr explicit symbol_resolver_ref(Tag) noexcept : ctx_(nullptr), vtbl_(&s_vtbl<Tag>) {}
158
167 template <typename Tag, typename Context, typename Traits = symbol_resolver_traits<Tag>,
168 std::enable_if_t<!std::is_void_v<typename Traits::context_type> &&
169 std::is_convertible_v<const Context *, const typename Traits::context_type *>,
170 int> = 0>
171 constexpr symbol_resolver_ref(Tag, const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
172 : ctx_(&ctx), vtbl_(&s_vtbl<Tag>) {}
173
174 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
175 constexpr symbol_resolver_ref(Tag, Context &&) = delete;
176
183 template <typename Tag, typename Traits = symbol_resolver_traits<Tag>,
184 std::enable_if_t<std::is_void_v<typename Traits::context_type>, int> = 0>
185 [[nodiscard]] static constexpr symbol_resolver_ref make() noexcept {
186 return symbol_resolver_ref(Tag{});
187 }
188
197 template <typename Tag, typename Context, typename Traits = symbol_resolver_traits<Tag>,
198 std::enable_if_t<!std::is_void_v<typename Traits::context_type>, int> = 0>
199 [[nodiscard]] static constexpr symbol_resolver_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept {
200 return symbol_resolver_ref(Tag{}, ctx);
201 }
202
203 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
204 static symbol_resolver_ref make(Context &&) = delete;
205
214 [[nodiscard]] bool resolve(uintptr_t addr, symbol_resolution_context &context) const noexcept {
215 return resolve(addr, context.scratch, context.raw, context.resolved);
216 }
217
227 [[nodiscard]] bool resolve(uintptr_t addr, span<char> scratch, raw_resolved_symbol &raw_storage,
228 resolved_symbol_info &out_info) const noexcept {
229 if (vtbl_ == nullptr)
230 return false;
231
232 raw_storage = {};
233 if (!vtbl_->resolve(ctx_.get(), addr, scratch, raw_storage)) {
234 return false;
235 }
236
237 out_info = {};
238 out_info.symbol_name = raw_storage.symbol_name;
239 out_info.symbol_base = raw_storage.symbol_base;
240 out_info.is_exact = raw_storage.is_exact;
241 out_info.image_name = raw_storage.image_name;
242 out_info.image_load_base = raw_storage.image_load_base;
243
244 if (raw_storage.symbol_base != 0 && addr >= raw_storage.symbol_base) {
245 out_info.offset_from_symbol = addr - raw_storage.symbol_base;
246 } else {
247 out_info.offset_from_symbol = 0;
248 }
249
250 if (raw_storage.image_load_base != 0 && addr >= raw_storage.image_load_base) {
251 out_info.offset_from_image = addr - raw_storage.image_load_base;
252 } else {
253 out_info.offset_from_image = 0;
254 }
255
256 return true;
257 }
258
263 [[nodiscard]] constexpr explicit operator bool() const noexcept { return vtbl_ != nullptr; }
264
265private:
266 template <typename Tag>
267 static bool resolve_entry(const void *context, uintptr_t address, span<char> scratch,
268 raw_resolved_symbol &result) noexcept {
269 using context_type = typename symbol_resolver_traits<Tag>::context_type;
270 if constexpr (std::is_void_v<context_type>) {
271 return symbol_resolver_traits<Tag>::resolve(address, scratch, result);
272 } else {
273 const auto &typed_context = *static_cast<const context_type *>(context);
274 return symbol_resolver_traits<Tag>::resolve(value_ref<const context_type>(typed_context), address, scratch,
275 result);
276 }
277 }
278
279 template <typename Tag> static constexpr vtable s_vtbl{&resolve_entry<Tag>};
280
281 value_ptr<const void> ctx_{};
282 const vtable *vtbl_{nullptr};
283};
284
285template <typename Tag, bool Stateless = std::is_void_v<typename symbol_resolver_traits<Tag>::context_type>>
287
288template <typename Tag> class RELOCO_OWNER symbol_resolver<Tag, false> {
289public:
291 using context_type = typename traits_type::context_type;
292
293 constexpr explicit symbol_resolver(context_type context) noexcept : context_(std::move(context)) {}
294
295 [[nodiscard]] constexpr value_ref<context_type> context() & noexcept RELOCO_LIFETIMEBOUND {
296 return value_ref<context_type>(context_);
297 }
298
299 [[nodiscard]] constexpr value_ref<const context_type> context() const & noexcept RELOCO_LIFETIMEBOUND {
300 return value_ref<const context_type>(context_);
301 }
302
303 [[nodiscard]] constexpr symbol_resolver_ref ref() const & noexcept RELOCO_LIFETIMEBOUND {
304 return symbol_resolver_ref(Tag{}, context_);
305 }
306
307 value_ref<context_type> context() && = delete;
308 value_ref<const context_type> context() const && = delete;
309 symbol_resolver_ref ref() const && = delete;
310
311private:
312 context_type context_;
313};
314
315template <typename Tag> class symbol_resolver<Tag, true> {
316public:
317 [[nodiscard]] static constexpr symbol_resolver_ref ref() noexcept { return symbol_resolver_ref(Tag{}); }
318};
319
320// ============================================================================
321// Remote Symbol View & Formatter
322// ============================================================================
323
328public:
332 constexpr remote_symbol_view() noexcept = default;
333
341 constexpr remote_symbol_view(uintptr_t addr, symbol_resolver_ref resolver,
342 symbol_resolution_context &context RELOCO_LIFETIMEBOUND, bool demangle = true) noexcept
343 : addr_(addr), resolver_(resolver), context_(&context), demangle_(demangle) {}
344
349 [[nodiscard]] constexpr uintptr_t address() const noexcept { return addr_; }
354 [[nodiscard]] constexpr symbol_resolver_ref resolver() const noexcept { return resolver_; }
358 [[nodiscard]] constexpr symbol_resolution_context &context() const noexcept RELOCO_LIFETIMEBOUND { return *context_; }
363 [[nodiscard]] constexpr bool demangle() const noexcept { return demangle_; }
364
365private:
367 uintptr_t addr_{0};
369 symbol_resolver_ref resolver_{};
371 value_ptr<symbol_resolution_context> context_{};
373 bool demangle_{true};
374};
375
382template <> struct formatter<remote_symbol_view> {
386 char mode{'\0'}; // '#' = verbose (image!symbol+off), '\0' = standard
387
392 constexpr void parse(format_parse_context &ctx) noexcept {
393 const auto spec = ctx.spec();
394 if (!spec.empty() && spec.front() == '#') {
395 mode = '#';
396 }
397 }
398
404 void format(const remote_symbol_view &view, const sink &out) const noexcept {
405 if (view.address() == 0) {
406 out.write("(null)");
407 return;
408 }
409
410 if (!view.resolver()) {
411 microfmt::format_to(out, "{:#x}", view.address());
412 return;
413 }
414
415 auto &context = view.context();
416 context.raw = {};
417 context.resolved = {};
418 const bool ok = view.resolver().resolve(view.address(), context);
419 const auto &info = context.resolved;
420
421 // 1. Unresolved fallback
422 if (!ok || (!info.has_symbol() && !info.has_image())) {
423 microfmt::format_to(out, "<unknown@{:#x}>", view.address());
424 return;
425 }
426
427 // Precise or nearest symbol matched
428 if (info.has_symbol()) {
429 if (mode == '#' && info.has_image()) {
430 microfmt::format_to(out, "{}!", info.image_name);
431 }
432
433 if (view.demangle()) {
434 microfmt::format_to(out, "{}", as_demangled(info.symbol_name));
435 } else {
436 out.write(info.symbol_name);
437 }
438
439 if (info.offset_from_symbol > 0) {
440 microfmt::format_to(out, "+{:#x}", info.offset_from_symbol);
441 }
442 return;
443 }
444
445 // Module/Image known, but symbol stripped (e.g. nvgpu.ko+0x1420)
446 if (info.has_image()) {
447 microfmt::format_to(out, "{}+{:#x}", info.image_name, info.offset_from_image);
448 }
449 }
450};
451
460[[nodiscard]] constexpr auto make_remote_symbol(uintptr_t addr, symbol_resolver_ref resolver,
461 symbol_resolution_context &context, bool demangle = true) noexcept {
462 return remote_symbol_view(addr, resolver, context, demangle);
463}
464
465} // namespace microfmt
Type-erased remote address-space access and remote string/object views.
Definition microfmt.hpp:881
Formattable view resolving and rendering a remote code address.
Definition symbol_resolver.hpp:327
constexpr symbol_resolver_ref resolver() const noexcept
Returns the symbol resolver handle.
Definition symbol_resolver.hpp:354
constexpr uintptr_t address() const noexcept
Returns the address being resolved.
Definition symbol_resolver.hpp:349
constexpr remote_symbol_view() noexcept=default
Constructs an empty (null) view.
constexpr symbol_resolution_context & context() const noexcept RELOCO_LIFETIMEBOUND
Returns caller-owned symbol-resolution temporaries.
Definition symbol_resolver.hpp:358
constexpr bool demangle() const noexcept
Reports whether symbol names are demangled.
Definition symbol_resolver.hpp:363
value_ref< context_type > context() &&=delete
typename traits_type::context_type context_type
Definition symbol_resolver.hpp:291
constexpr symbol_resolver_ref ref() const &noexcept RELOCO_LIFETIMEBOUND
Definition symbol_resolver.hpp:303
constexpr value_ref< const context_type > context() const &noexcept RELOCO_LIFETIMEBOUND
Definition symbol_resolver.hpp:299
constexpr value_ref< context_type > context() &noexcept RELOCO_LIFETIMEBOUND
Definition symbol_resolver.hpp:295
constexpr symbol_resolver(context_type context) noexcept
Definition symbol_resolver.hpp:293
value_ref< const context_type > context() const &&=delete
static constexpr symbol_resolver_ref ref() noexcept
Definition symbol_resolver.hpp:317
Type-erased, two-word handle to a symbol resolver.
Definition symbol_resolver.hpp:132
constexpr symbol_resolver_ref(Tag, Context &&)=delete
constexpr symbol_resolver_ref() noexcept=default
Constructs an empty (invalid) handle.
static symbol_resolver_ref make(Context &&)=delete
constexpr symbol_resolver_ref(Tag, const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a handle for a stateful resolver tag.
Definition symbol_resolver.hpp:171
static constexpr symbol_resolver_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept
Creates a handle for a stateful resolver tag.
Definition symbol_resolver.hpp:199
bool resolve(uintptr_t addr, span< char > scratch, raw_resolved_symbol &raw_storage, resolved_symbol_info &out_info) const noexcept
Resolves an address using caller-owned intermediate storage.
Definition symbol_resolver.hpp:227
bool resolve(uintptr_t addr, symbol_resolution_context &context) const noexcept
Resolves an address and computes the derived offsets.
Definition symbol_resolver.hpp:214
static constexpr symbol_resolver_ref make() noexcept
Creates a handle for a stateless resolver tag.
Definition symbol_resolver.hpp:185
Definition symbol_resolver.hpp:286
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Zero-allocation Itanium (GCC/Clang) name demangler.
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
constexpr auto as_demangled(microfmt::string_view sym) noexcept
Wraps a symbol for demangled formatting.
Definition demangle.hpp:523
constexpr auto make_remote_symbol(uintptr_t addr, symbol_resolver_ref resolver, symbol_resolution_context &context, bool demangle=true) noexcept
Convenience factory building a remote_symbol_view.
Definition symbol_resolver.hpp:460
void format(const remote_symbol_view &view, const sink &out) const noexcept
Resolves and renders the remote address.
Definition symbol_resolver.hpp:404
constexpr void parse(format_parse_context &ctx) noexcept
Parses the leading # mode flag.
Definition symbol_resolver.hpp:392
Definition microfmt.hpp:985
Raw resolution result as returned by a resolver backend.
Definition symbol_resolver.hpp:26
Derived symbol info with precomputed relative offsets.
Definition symbol_resolver.hpp:53
constexpr bool has_symbol() const noexcept
Reports whether a symbol name is available.
Definition symbol_resolver.hpp:88
constexpr bool has_image() const noexcept
Reports whether an owning image is known.
Definition symbol_resolver.hpp:94
Caller-owned temporary storage for one symbol resolution.
Definition symbol_resolver.hpp:100
constexpr symbol_resolution_context(char(&string_scratch)[N]) noexcept
Definition symbol_resolver.hpp:105
constexpr symbol_resolution_context(span< char > string_scratch RELOCO_LIFETIMEBOUND={}) noexcept
Definition symbol_resolver.hpp:101
Virtual table of symbol-resolution operations.
Definition symbol_resolver.hpp:137
Static customization point for symbol resolver backends.
Definition symbol_resolver.hpp:120