microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
ucontext_adapter.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
7#pragma once
8
9#if !defined(__linux__) && !defined(__FreeBSD__)
10#error "microfmt/inspector/ucontext_adapter.hpp only supports Linux and FreeBSD"
11#endif
12
13#include <cstdint>
14#include <cstring>
15#include <type_traits>
16#include <ucontext.h>
17
18#include "../microfmt.hpp"
19#include "dwarf_registers.hpp"
20#include "register_context.hpp"
21
22namespace microfmt::detail {
23
38inline bool ucontext_read_register(const void *ctx, address_space_ref, uint32_t dwarf_reg_index, void *out_value,
39 size_t value_size) noexcept {
40 if (!ctx || !out_value || value_size == 0 || value_size > sizeof(uint64_t))
41 return false;
42
43 const auto &uc = *static_cast<const ucontext_t *>(ctx);
44 uint64_t value = 0;
45 bool found = false;
46
47#if defined(__linux__)
48
49#if defined(__x86_64__)
50 // DWARF x86_64 register numbers do not match glibc's REG_* gregs[] index
51 // order, so each register is mapped explicitly (System V ABI, see
52 // dwarf_registers.hpp).
53 switch (dwarf_reg_index) {
55 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RAX]);
56 found = true;
57 break;
59 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RDX]);
60 found = true;
61 break;
63 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RCX]);
64 found = true;
65 break;
67 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RBX]);
68 found = true;
69 break;
71 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RSI]);
72 found = true;
73 break;
75 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RDI]);
76 found = true;
77 break;
79 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RBP]);
80 found = true;
81 break;
83 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RSP]);
84 found = true;
85 break;
87 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R8]);
88 found = true;
89 break;
91 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R9]);
92 found = true;
93 break;
95 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R10]);
96 found = true;
97 break;
99 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R11]);
100 found = true;
101 break;
103 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R12]);
104 found = true;
105 break;
107 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R13]);
108 found = true;
109 break;
111 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R14]);
112 found = true;
113 break;
115 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_R15]);
116 found = true;
117 break;
119 value = static_cast<uint64_t>(uc.uc_mcontext.gregs[REG_RIP]);
120 found = true;
121 break;
122 default:
123 break;
124 }
125
126#elif defined(__i386__)
127 // DWARF x86 register numbers likewise do not match glibc's REG_* order.
128 switch (dwarf_reg_index) {
129 case dwarf::x86::eax:
130 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EAX]));
131 found = true;
132 break;
133 case dwarf::x86::ecx:
134 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_ECX]));
135 found = true;
136 break;
137 case dwarf::x86::edx:
138 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EDX]));
139 found = true;
140 break;
141 case dwarf::x86::ebx:
142 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EBX]));
143 found = true;
144 break;
145 case dwarf::x86::esp:
146 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_ESP]));
147 found = true;
148 break;
149 case dwarf::x86::ebp:
150 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EBP]));
151 found = true;
152 break;
153 case dwarf::x86::esi:
154 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_ESI]));
155 found = true;
156 break;
157 case dwarf::x86::edi:
158 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EDI]));
159 found = true;
160 break;
161 case dwarf::x86::eip:
162 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.gregs[REG_EIP]));
163 found = true;
164 break;
165 default:
166 break;
167 }
168
169#elif defined(__aarch64__)
170 // uc_mcontext.regs[0..30] holds x0-x30 directly; sp/pc are named fields.
171 // This layout coincides with the DWARF AArch64 register numbering.
172 if (dwarf_reg_index <= 30) {
173 value = uc.uc_mcontext.regs[dwarf_reg_index];
174 found = true;
175 } else if (dwarf_reg_index == dwarf::aarch64::sp) {
176 value = uc.uc_mcontext.sp;
177 found = true;
178 } else if (dwarf_reg_index == dwarf::aarch64::pc) {
179 value = uc.uc_mcontext.pc;
180 found = true;
181 }
182
183#elif defined(__arm__)
184 // arm_r0..arm_r10, arm_fp, arm_ip, arm_sp, arm_lr, arm_pc map 1:1 onto
185 // DWARF ARM32 r0-r15; arm_cpsr maps to dwarf::arm32::cpsr, needed to
186 // detect the live ARM/Thumb instruction-set state (CPSR.T) when resolving
187 // which register (R11 vs. R7) actually holds the frame pointer.
188 switch (dwarf_reg_index) {
189 case dwarf::arm32::r0:
190 value = uc.uc_mcontext.arm_r0;
191 found = true;
192 break;
193 case dwarf::arm32::r1:
194 value = uc.uc_mcontext.arm_r1;
195 found = true;
196 break;
197 case dwarf::arm32::r2:
198 value = uc.uc_mcontext.arm_r2;
199 found = true;
200 break;
201 case dwarf::arm32::r3:
202 value = uc.uc_mcontext.arm_r3;
203 found = true;
204 break;
205 case dwarf::arm32::r4:
206 value = uc.uc_mcontext.arm_r4;
207 found = true;
208 break;
209 case dwarf::arm32::r5:
210 value = uc.uc_mcontext.arm_r5;
211 found = true;
212 break;
213 case dwarf::arm32::r6:
214 value = uc.uc_mcontext.arm_r6;
215 found = true;
216 break;
217 case dwarf::arm32::r7:
218 value = uc.uc_mcontext.arm_r7;
219 found = true;
220 break;
221 case dwarf::arm32::r8:
222 value = uc.uc_mcontext.arm_r8;
223 found = true;
224 break;
225 case dwarf::arm32::r9:
226 value = uc.uc_mcontext.arm_r9;
227 found = true;
228 break;
230 value = uc.uc_mcontext.arm_r10;
231 found = true;
232 break;
234 value = uc.uc_mcontext.arm_fp;
235 found = true;
236 break;
238 value = uc.uc_mcontext.arm_ip;
239 found = true;
240 break;
242 value = uc.uc_mcontext.arm_sp;
243 found = true;
244 break;
246 value = uc.uc_mcontext.arm_lr;
247 found = true;
248 break;
250 value = uc.uc_mcontext.arm_pc;
251 found = true;
252 break;
254 value = uc.uc_mcontext.arm_cpsr;
255 found = true;
256 break;
257 default:
258 break;
259 }
260
261#elif defined(__riscv) && (__riscv_xlen == 64)
262 // glibc's mcontext_t.__gregs[0] holds pc; __gregs[1..31] hold x1-x31.
263 // x0 ("zero") is architecturally hardwired to zero and is never saved.
264 if (dwarf_reg_index == dwarf::riscv::zero) {
265 value = 0;
266 found = true;
267 } else if (dwarf_reg_index >= 1 && dwarf_reg_index <= 31) {
268 value = static_cast<uint64_t>(uc.uc_mcontext.__gregs[dwarf_reg_index]);
269 found = true;
270 } else if (dwarf_reg_index == dwarf::riscv::pc) {
271 value = static_cast<uint64_t>(uc.uc_mcontext.__gregs[0]);
272 found = true;
273 }
274#endif
275
276#elif defined(__FreeBSD__)
277
278#if defined(__amd64__) || defined(__x86_64__)
279 switch (dwarf_reg_index) {
281 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rax);
282 found = true;
283 break;
285 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rdx);
286 found = true;
287 break;
289 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rcx);
290 found = true;
291 break;
293 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rbx);
294 found = true;
295 break;
297 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rsi);
298 found = true;
299 break;
301 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rdi);
302 found = true;
303 break;
305 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rbp);
306 found = true;
307 break;
309 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rsp);
310 found = true;
311 break;
313 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r8);
314 found = true;
315 break;
317 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r9);
318 found = true;
319 break;
321 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r10);
322 found = true;
323 break;
325 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r11);
326 found = true;
327 break;
329 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r12);
330 found = true;
331 break;
333 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r13);
334 found = true;
335 break;
337 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r14);
338 found = true;
339 break;
341 value = static_cast<uint64_t>(uc.uc_mcontext.mc_r15);
342 found = true;
343 break;
345 value = static_cast<uint64_t>(uc.uc_mcontext.mc_rip);
346 found = true;
347 break;
348 default:
349 break;
350 }
351
352#elif defined(__i386__)
353 switch (dwarf_reg_index) {
354 case dwarf::x86::eax:
355 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_eax));
356 found = true;
357 break;
358 case dwarf::x86::ecx:
359 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_ecx));
360 found = true;
361 break;
362 case dwarf::x86::edx:
363 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_edx));
364 found = true;
365 break;
366 case dwarf::x86::ebx:
367 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_ebx));
368 found = true;
369 break;
370 case dwarf::x86::esp:
371 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_esp));
372 found = true;
373 break;
374 case dwarf::x86::ebp:
375 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_ebp));
376 found = true;
377 break;
378 case dwarf::x86::esi:
379 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_esi));
380 found = true;
381 break;
382 case dwarf::x86::edi:
383 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_edi));
384 found = true;
385 break;
386 case dwarf::x86::eip:
387 value = static_cast<uint64_t>(static_cast<uint32_t>(uc.uc_mcontext.mc_eip));
388 found = true;
389 break;
390 default:
391 break;
392 }
393
394#elif defined(__aarch64__)
395 // struct gpregs { register_t gp_x[30]; gp_lr; gp_sp; gp_elr; gp_spsr; };
396 // x30 (lr) is a dedicated field, NOT gp_x[30] (which would be OOB: gp_x
397 // only holds x0-x29).
398 if (dwarf_reg_index <= 29) {
399 value = static_cast<uint64_t>(uc.uc_mcontext.mc_gpregs.gp_x[dwarf_reg_index]);
400 found = true;
401 } else if (dwarf_reg_index == 30) {
402 value = static_cast<uint64_t>(uc.uc_mcontext.mc_gpregs.gp_lr);
403 found = true;
404 } else if (dwarf_reg_index == dwarf::aarch64::sp) {
405 value = static_cast<uint64_t>(uc.uc_mcontext.mc_gpregs.gp_sp);
406 found = true;
407 } else if (dwarf_reg_index == dwarf::aarch64::pc) {
408 value = static_cast<uint64_t>(uc.uc_mcontext.mc_gpregs.gp_elr);
409 found = true;
410 }
411
412#elif defined(__arm__)
413 // struct mcontext_t { __gregset_t __gregs[17]; ... }; _REG_R0.._REG_R15
414 // are 0..15, matching DWARF ARM32 r0-r15 directly; _REG_CPSR is 16 (no
415 // DWARF equivalent handled here).
416 if (dwarf_reg_index <= 15) {
417 value = static_cast<uint64_t>(uc.uc_mcontext.__gregs[dwarf_reg_index]);
418 found = true;
419 }
420
421#elif defined(__riscv) && (__riscv_xlen == 64)
422 // struct gpregs { gp_ra; gp_sp; gp_gp; gp_tp; gp_t[7]; gp_s[12]; gp_a[8];
423 // gp_sepc; gp_sstatus; }; x0 ("zero") is hardwired and never saved.
424 const auto &gp = uc.uc_mcontext.mc_gpregs;
425 if (dwarf_reg_index == dwarf::riscv::zero) {
426 value = 0;
427 found = true;
428 } else if (dwarf_reg_index == dwarf::riscv::ra) {
429 value = static_cast<uint64_t>(gp.gp_ra);
430 found = true;
431 } else if (dwarf_reg_index == dwarf::riscv::sp) {
432 value = static_cast<uint64_t>(gp.gp_sp);
433 found = true;
434 } else if (dwarf_reg_index == dwarf::riscv::gp) {
435 value = static_cast<uint64_t>(gp.gp_gp);
436 found = true;
437 } else if (dwarf_reg_index == dwarf::riscv::tp) {
438 value = static_cast<uint64_t>(gp.gp_tp);
439 found = true;
440 } else if (dwarf_reg_index >= dwarf::riscv::t0 && dwarf_reg_index <= dwarf::riscv::t2) {
441 value = static_cast<uint64_t>(gp.gp_t[dwarf_reg_index - dwarf::riscv::t0]);
442 found = true;
443 } else if (dwarf_reg_index >= dwarf::riscv::s0 && dwarf_reg_index <= dwarf::riscv::s1) {
444 value = static_cast<uint64_t>(gp.gp_s[dwarf_reg_index - dwarf::riscv::s0]);
445 found = true;
446 } else if (dwarf_reg_index >= dwarf::riscv::a0 && dwarf_reg_index <= dwarf::riscv::a7) {
447 value = static_cast<uint64_t>(gp.gp_a[dwarf_reg_index - dwarf::riscv::a0]);
448 found = true;
449 } else if (dwarf_reg_index >= dwarf::riscv::s2 && dwarf_reg_index <= dwarf::riscv::s11) {
450 value = static_cast<uint64_t>(gp.gp_s[2 + (dwarf_reg_index - dwarf::riscv::s2)]);
451 found = true;
452 } else if (dwarf_reg_index >= dwarf::riscv::t3 && dwarf_reg_index <= dwarf::riscv::t6) {
453 value = static_cast<uint64_t>(gp.gp_t[3 + (dwarf_reg_index - dwarf::riscv::t3)]);
454 found = true;
455 } else if (dwarf_reg_index == dwarf::riscv::pc) {
456 value = static_cast<uint64_t>(gp.gp_sepc);
457 found = true;
458 }
459#endif
460
461#endif
462
463 if (!found)
464 return false;
465 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
466
467 std::memcpy(out_value, &value, value_size);
468
469 RELOCO_END_UNSAFE_BUFFER_USAGE;
470
471 return true;
472}
473
487inline bool ucontext_write_register(void *ctx, address_space_ref, uint32_t dwarf_reg_index, const void *in_value,
488 size_t value_size) noexcept {
489 if (!ctx || !in_value || value_size == 0 || value_size > sizeof(uint64_t))
490 return false;
491
492 uint64_t value = 0;
493 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
494
495 std::memcpy(&value, in_value, value_size);
496 auto &uc = *static_cast<ucontext_t *>(ctx);
497
498 RELOCO_END_UNSAFE_BUFFER_USAGE;
499
500 bool found = false;
501
502#if defined(__linux__)
503
504#if defined(__x86_64__)
505 switch (dwarf_reg_index) {
507 uc.uc_mcontext.gregs[REG_RAX] = static_cast<greg_t>(value);
508 found = true;
509 break;
511 uc.uc_mcontext.gregs[REG_RDX] = static_cast<greg_t>(value);
512 found = true;
513 break;
515 uc.uc_mcontext.gregs[REG_RCX] = static_cast<greg_t>(value);
516 found = true;
517 break;
519 uc.uc_mcontext.gregs[REG_RBX] = static_cast<greg_t>(value);
520 found = true;
521 break;
523 uc.uc_mcontext.gregs[REG_RSI] = static_cast<greg_t>(value);
524 found = true;
525 break;
527 uc.uc_mcontext.gregs[REG_RDI] = static_cast<greg_t>(value);
528 found = true;
529 break;
531 uc.uc_mcontext.gregs[REG_RBP] = static_cast<greg_t>(value);
532 found = true;
533 break;
535 uc.uc_mcontext.gregs[REG_RSP] = static_cast<greg_t>(value);
536 found = true;
537 break;
539 uc.uc_mcontext.gregs[REG_R8] = static_cast<greg_t>(value);
540 found = true;
541 break;
543 uc.uc_mcontext.gregs[REG_R9] = static_cast<greg_t>(value);
544 found = true;
545 break;
547 uc.uc_mcontext.gregs[REG_R10] = static_cast<greg_t>(value);
548 found = true;
549 break;
551 uc.uc_mcontext.gregs[REG_R11] = static_cast<greg_t>(value);
552 found = true;
553 break;
555 uc.uc_mcontext.gregs[REG_R12] = static_cast<greg_t>(value);
556 found = true;
557 break;
559 uc.uc_mcontext.gregs[REG_R13] = static_cast<greg_t>(value);
560 found = true;
561 break;
563 uc.uc_mcontext.gregs[REG_R14] = static_cast<greg_t>(value);
564 found = true;
565 break;
567 uc.uc_mcontext.gregs[REG_R15] = static_cast<greg_t>(value);
568 found = true;
569 break;
571 uc.uc_mcontext.gregs[REG_RIP] = static_cast<greg_t>(value);
572 found = true;
573 break;
574 default:
575 break;
576 }
577
578#elif defined(__i386__)
579 switch (dwarf_reg_index) {
580 case dwarf::x86::eax:
581 uc.uc_mcontext.gregs[REG_EAX] = static_cast<greg_t>(static_cast<uint32_t>(value));
582 found = true;
583 break;
584 case dwarf::x86::ecx:
585 uc.uc_mcontext.gregs[REG_ECX] = static_cast<greg_t>(static_cast<uint32_t>(value));
586 found = true;
587 break;
588 case dwarf::x86::edx:
589 uc.uc_mcontext.gregs[REG_EDX] = static_cast<greg_t>(static_cast<uint32_t>(value));
590 found = true;
591 break;
592 case dwarf::x86::ebx:
593 uc.uc_mcontext.gregs[REG_EBX] = static_cast<greg_t>(static_cast<uint32_t>(value));
594 found = true;
595 break;
596 case dwarf::x86::esp:
597 uc.uc_mcontext.gregs[REG_ESP] = static_cast<greg_t>(static_cast<uint32_t>(value));
598 found = true;
599 break;
600 case dwarf::x86::ebp:
601 uc.uc_mcontext.gregs[REG_EBP] = static_cast<greg_t>(static_cast<uint32_t>(value));
602 found = true;
603 break;
604 case dwarf::x86::esi:
605 uc.uc_mcontext.gregs[REG_ESI] = static_cast<greg_t>(static_cast<uint32_t>(value));
606 found = true;
607 break;
608 case dwarf::x86::edi:
609 uc.uc_mcontext.gregs[REG_EDI] = static_cast<greg_t>(static_cast<uint32_t>(value));
610 found = true;
611 break;
612 case dwarf::x86::eip:
613 uc.uc_mcontext.gregs[REG_EIP] = static_cast<greg_t>(static_cast<uint32_t>(value));
614 found = true;
615 break;
616 default:
617 break;
618 }
619
620#elif defined(__aarch64__)
621 if (dwarf_reg_index <= 30) {
622 uc.uc_mcontext.regs[dwarf_reg_index] = value;
623 found = true;
624 } else if (dwarf_reg_index == dwarf::aarch64::sp) {
625 uc.uc_mcontext.sp = value;
626 found = true;
627 } else if (dwarf_reg_index == dwarf::aarch64::pc) {
628 uc.uc_mcontext.pc = value;
629 found = true;
630 }
631
632#elif defined(__arm__)
633 switch (dwarf_reg_index) {
634 case dwarf::arm32::r0:
635 uc.uc_mcontext.arm_r0 = static_cast<uint32_t>(value);
636 found = true;
637 break;
638 case dwarf::arm32::r1:
639 uc.uc_mcontext.arm_r1 = static_cast<uint32_t>(value);
640 found = true;
641 break;
642 case dwarf::arm32::r2:
643 uc.uc_mcontext.arm_r2 = static_cast<uint32_t>(value);
644 found = true;
645 break;
646 case dwarf::arm32::r3:
647 uc.uc_mcontext.arm_r3 = static_cast<uint32_t>(value);
648 found = true;
649 break;
650 case dwarf::arm32::r4:
651 uc.uc_mcontext.arm_r4 = static_cast<uint32_t>(value);
652 found = true;
653 break;
654 case dwarf::arm32::r5:
655 uc.uc_mcontext.arm_r5 = static_cast<uint32_t>(value);
656 found = true;
657 break;
658 case dwarf::arm32::r6:
659 uc.uc_mcontext.arm_r6 = static_cast<uint32_t>(value);
660 found = true;
661 break;
662 case dwarf::arm32::r7:
663 uc.uc_mcontext.arm_r7 = static_cast<uint32_t>(value);
664 found = true;
665 break;
666 case dwarf::arm32::r8:
667 uc.uc_mcontext.arm_r8 = static_cast<uint32_t>(value);
668 found = true;
669 break;
670 case dwarf::arm32::r9:
671 uc.uc_mcontext.arm_r9 = static_cast<uint32_t>(value);
672 found = true;
673 break;
675 uc.uc_mcontext.arm_r10 = static_cast<uint32_t>(value);
676 found = true;
677 break;
679 uc.uc_mcontext.arm_fp = static_cast<uint32_t>(value);
680 found = true;
681 break;
683 uc.uc_mcontext.arm_ip = static_cast<uint32_t>(value);
684 found = true;
685 break;
687 uc.uc_mcontext.arm_sp = static_cast<uint32_t>(value);
688 found = true;
689 break;
691 uc.uc_mcontext.arm_lr = static_cast<uint32_t>(value);
692 found = true;
693 break;
695 uc.uc_mcontext.arm_pc = static_cast<uint32_t>(value);
696 found = true;
697 break;
699 uc.uc_mcontext.arm_cpsr = static_cast<uint32_t>(value);
700 found = true;
701 break;
702 default:
703 break;
704 }
705
706#elif defined(__riscv) && (__riscv_xlen == 64)
707 // x0 ("zero") is hardwired and cannot be written; silently accept the
708 // request as a no-op success, matching hardware semantics.
709 if (dwarf_reg_index == dwarf::riscv::zero) {
710 found = true;
711 } else if (dwarf_reg_index >= 1 && dwarf_reg_index <= 31) {
712 uc.uc_mcontext.__gregs[dwarf_reg_index] = static_cast<unsigned long>(value);
713 found = true;
714 } else if (dwarf_reg_index == dwarf::riscv::pc) {
715 uc.uc_mcontext.__gregs[0] = static_cast<unsigned long>(value);
716 found = true;
717 }
718#endif
719
720#elif defined(__FreeBSD__)
721
722#if defined(__amd64__) || defined(__x86_64__)
723 switch (dwarf_reg_index) {
725 uc.uc_mcontext.mc_rax = static_cast<decltype(uc.uc_mcontext.mc_rax)>(value);
726 found = true;
727 break;
729 uc.uc_mcontext.mc_rdx = static_cast<decltype(uc.uc_mcontext.mc_rdx)>(value);
730 found = true;
731 break;
733 uc.uc_mcontext.mc_rcx = static_cast<decltype(uc.uc_mcontext.mc_rcx)>(value);
734 found = true;
735 break;
737 uc.uc_mcontext.mc_rbx = static_cast<decltype(uc.uc_mcontext.mc_rbx)>(value);
738 found = true;
739 break;
741 uc.uc_mcontext.mc_rsi = static_cast<decltype(uc.uc_mcontext.mc_rsi)>(value);
742 found = true;
743 break;
745 uc.uc_mcontext.mc_rdi = static_cast<decltype(uc.uc_mcontext.mc_rdi)>(value);
746 found = true;
747 break;
749 uc.uc_mcontext.mc_rbp = static_cast<decltype(uc.uc_mcontext.mc_rbp)>(value);
750 found = true;
751 break;
753 uc.uc_mcontext.mc_rsp = static_cast<decltype(uc.uc_mcontext.mc_rsp)>(value);
754 found = true;
755 break;
757 uc.uc_mcontext.mc_r8 = static_cast<decltype(uc.uc_mcontext.mc_r8)>(value);
758 found = true;
759 break;
761 uc.uc_mcontext.mc_r9 = static_cast<decltype(uc.uc_mcontext.mc_r9)>(value);
762 found = true;
763 break;
765 uc.uc_mcontext.mc_r10 = static_cast<decltype(uc.uc_mcontext.mc_r10)>(value);
766 found = true;
767 break;
769 uc.uc_mcontext.mc_r11 = static_cast<decltype(uc.uc_mcontext.mc_r11)>(value);
770 found = true;
771 break;
773 uc.uc_mcontext.mc_r12 = static_cast<decltype(uc.uc_mcontext.mc_r12)>(value);
774 found = true;
775 break;
777 uc.uc_mcontext.mc_r13 = static_cast<decltype(uc.uc_mcontext.mc_r13)>(value);
778 found = true;
779 break;
781 uc.uc_mcontext.mc_r14 = static_cast<decltype(uc.uc_mcontext.mc_r14)>(value);
782 found = true;
783 break;
785 uc.uc_mcontext.mc_r15 = static_cast<decltype(uc.uc_mcontext.mc_r15)>(value);
786 found = true;
787 break;
789 uc.uc_mcontext.mc_rip = static_cast<decltype(uc.uc_mcontext.mc_rip)>(value);
790 found = true;
791 break;
792 default:
793 break;
794 }
795
796#elif defined(__i386__)
797 switch (dwarf_reg_index) {
798 case dwarf::x86::eax:
799 uc.uc_mcontext.mc_eax = static_cast<decltype(uc.uc_mcontext.mc_eax)>(static_cast<uint32_t>(value));
800 found = true;
801 break;
802 case dwarf::x86::ecx:
803 uc.uc_mcontext.mc_ecx = static_cast<decltype(uc.uc_mcontext.mc_ecx)>(static_cast<uint32_t>(value));
804 found = true;
805 break;
806 case dwarf::x86::edx:
807 uc.uc_mcontext.mc_edx = static_cast<decltype(uc.uc_mcontext.mc_edx)>(static_cast<uint32_t>(value));
808 found = true;
809 break;
810 case dwarf::x86::ebx:
811 uc.uc_mcontext.mc_ebx = static_cast<decltype(uc.uc_mcontext.mc_ebx)>(static_cast<uint32_t>(value));
812 found = true;
813 break;
814 case dwarf::x86::esp:
815 uc.uc_mcontext.mc_esp = static_cast<decltype(uc.uc_mcontext.mc_esp)>(static_cast<uint32_t>(value));
816 found = true;
817 break;
818 case dwarf::x86::ebp:
819 uc.uc_mcontext.mc_ebp = static_cast<decltype(uc.uc_mcontext.mc_ebp)>(static_cast<uint32_t>(value));
820 found = true;
821 break;
822 case dwarf::x86::esi:
823 uc.uc_mcontext.mc_esi = static_cast<decltype(uc.uc_mcontext.mc_esi)>(static_cast<uint32_t>(value));
824 found = true;
825 break;
826 case dwarf::x86::edi:
827 uc.uc_mcontext.mc_edi = static_cast<decltype(uc.uc_mcontext.mc_edi)>(static_cast<uint32_t>(value));
828 found = true;
829 break;
830 case dwarf::x86::eip:
831 uc.uc_mcontext.mc_eip = static_cast<decltype(uc.uc_mcontext.mc_eip)>(static_cast<uint32_t>(value));
832 found = true;
833 break;
834 default:
835 break;
836 }
837
838#elif defined(__aarch64__)
839 if (dwarf_reg_index <= 29) {
840 uc.uc_mcontext.mc_gpregs.gp_x[dwarf_reg_index] =
841 static_cast<std::remove_reference_t<decltype(uc.uc_mcontext.mc_gpregs.gp_x[0])>>(value);
842 found = true;
843 } else if (dwarf_reg_index == 30) {
844 uc.uc_mcontext.mc_gpregs.gp_lr = static_cast<decltype(uc.uc_mcontext.mc_gpregs.gp_lr)>(value);
845 found = true;
846 } else if (dwarf_reg_index == dwarf::aarch64::sp) {
847 uc.uc_mcontext.mc_gpregs.gp_sp = static_cast<decltype(uc.uc_mcontext.mc_gpregs.gp_sp)>(value);
848 found = true;
849 } else if (dwarf_reg_index == dwarf::aarch64::pc) {
850 uc.uc_mcontext.mc_gpregs.gp_elr = static_cast<decltype(uc.uc_mcontext.mc_gpregs.gp_elr)>(value);
851 found = true;
852 }
853
854#elif defined(__arm__)
855 if (dwarf_reg_index <= 15) {
856 uc.uc_mcontext.__gregs[dwarf_reg_index] = static_cast<__greg_t>(value);
857 found = true;
858 }
859
860#elif defined(__riscv) && (__riscv_xlen == 64)
861 auto &gp = uc.uc_mcontext.mc_gpregs;
862 if (dwarf_reg_index == dwarf::riscv::zero) {
863 // x0 ("zero") is hardwired and cannot be written; accept as a no-op.
864 found = true;
865 } else if (dwarf_reg_index == dwarf::riscv::ra) {
866 gp.gp_ra = static_cast<decltype(gp.gp_ra)>(value);
867 found = true;
868 } else if (dwarf_reg_index == dwarf::riscv::sp) {
869 gp.gp_sp = static_cast<decltype(gp.gp_sp)>(value);
870 found = true;
871 } else if (dwarf_reg_index == dwarf::riscv::gp) {
872 gp.gp_gp = static_cast<decltype(gp.gp_gp)>(value);
873 found = true;
874 } else if (dwarf_reg_index == dwarf::riscv::tp) {
875 gp.gp_tp = static_cast<decltype(gp.gp_tp)>(value);
876 found = true;
877 } else if (dwarf_reg_index >= dwarf::riscv::t0 && dwarf_reg_index <= dwarf::riscv::t2) {
878 gp.gp_t[dwarf_reg_index - dwarf::riscv::t0] = static_cast<std::remove_reference_t<decltype(gp.gp_t[0])>>(value);
879 found = true;
880 } else if (dwarf_reg_index >= dwarf::riscv::s0 && dwarf_reg_index <= dwarf::riscv::s1) {
881 gp.gp_s[dwarf_reg_index - dwarf::riscv::s0] = static_cast<std::remove_reference_t<decltype(gp.gp_s[0])>>(value);
882 found = true;
883 } else if (dwarf_reg_index >= dwarf::riscv::a0 && dwarf_reg_index <= dwarf::riscv::a7) {
884 gp.gp_a[dwarf_reg_index - dwarf::riscv::a0] = static_cast<std::remove_reference_t<decltype(gp.gp_a[0])>>(value);
885 found = true;
886 } else if (dwarf_reg_index >= dwarf::riscv::s2 && dwarf_reg_index <= dwarf::riscv::s11) {
887 gp.gp_s[2 + (dwarf_reg_index - dwarf::riscv::s2)] =
888 static_cast<std::remove_reference_t<decltype(gp.gp_s[0])>>(value);
889 found = true;
890 } else if (dwarf_reg_index >= dwarf::riscv::t3 && dwarf_reg_index <= dwarf::riscv::t6) {
891 gp.gp_t[3 + (dwarf_reg_index - dwarf::riscv::t3)] =
892 static_cast<std::remove_reference_t<decltype(gp.gp_t[0])>>(value);
893 found = true;
894 } else if (dwarf_reg_index == dwarf::riscv::pc) {
895 gp.gp_sepc = static_cast<decltype(gp.gp_sepc)>(value);
896 found = true;
897 }
898#endif
899
900#endif
901
902 return found;
903}
904
905} // namespace microfmt::detail
906
907namespace microfmt {
908
922[[nodiscard]] inline register_context_ref
923make_ucontext_register_context_ref(const ucontext_t &uctx RELOCO_LIFETIMEBOUND, address_space_ref space,
924 span<std::byte> scratch RELOCO_LIFETIMEBOUND) noexcept {
925 return make_read_only_register_context_ref<&detail::ucontext_read_register>(uctx, space, scratch);
926}
927
944[[nodiscard]] inline register_context_ref
945make_mutable_ucontext_register_context_ref(ucontext_t &uctx RELOCO_LIFETIMEBOUND, address_space_ref space,
946 span<std::byte> scratch RELOCO_LIFETIMEBOUND) noexcept {
947 return make_register_context_ref<&detail::ucontext_read_register, &detail::ucontext_write_register>(uctx, space,
948 scratch);
949}
950
951} // namespace microfmt
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Definition bintime.hpp:22
bool ucontext_read_register(const void *ctx, address_space_ref, uint32_t dwarf_reg_index, void *out_value, size_t value_size) noexcept
Reads a general-purpose register out of a POSIX ucontext_t by its DWARF register index (see microfmt:...
Definition ucontext_adapter.hpp:38
bool ucontext_write_register(void *ctx, address_space_ref, uint32_t dwarf_reg_index, const void *in_value, size_t value_size) noexcept
Writes a general-purpose register into a POSIX ucontext_t by its DWARF register index (see microfmt::...
Definition ucontext_adapter.hpp:487
@ sp
Definition dwarf_registers.hpp:263
@ pc
Definition dwarf_registers.hpp:264
@ r13
Definition dwarf_registers.hpp:503
@ r4
Definition dwarf_registers.hpp:494
@ r15
Definition dwarf_registers.hpp:505
@ r11
Definition dwarf_registers.hpp:501
@ r6
Definition dwarf_registers.hpp:496
@ r9
Definition dwarf_registers.hpp:499
@ r2
Definition dwarf_registers.hpp:492
@ r8
Definition dwarf_registers.hpp:498
@ r5
Definition dwarf_registers.hpp:495
@ r1
Definition dwarf_registers.hpp:491
@ r3
Definition dwarf_registers.hpp:493
@ r14
Definition dwarf_registers.hpp:504
@ cpsr
Definition dwarf_registers.hpp:550
@ r7
Definition dwarf_registers.hpp:497
@ r10
Definition dwarf_registers.hpp:500
@ r12
Definition dwarf_registers.hpp:502
@ r0
Definition dwarf_registers.hpp:490
@ t2
Definition dwarf_registers.hpp:723
@ s2
Definition dwarf_registers.hpp:746
@ ra
Definition dwarf_registers.hpp:711
@ a7
Definition dwarf_registers.hpp:744
@ gp
Definition dwarf_registers.hpp:715
@ tp
Definition dwarf_registers.hpp:717
@ s0
Definition dwarf_registers.hpp:725
@ s11
Definition dwarf_registers.hpp:764
@ t6
Definition dwarf_registers.hpp:776
@ s1
Definition dwarf_registers.hpp:728
@ sp
Definition dwarf_registers.hpp:713
@ pc
Definition dwarf_registers.hpp:813
@ t3
Definition dwarf_registers.hpp:767
@ t0
Definition dwarf_registers.hpp:719
@ zero
Definition dwarf_registers.hpp:709
@ a0
Definition dwarf_registers.hpp:730
@ r13
Definition dwarf_registers.hpp:65
@ r14
Definition dwarf_registers.hpp:66
@ rbx
Definition dwarf_registers.hpp:55
@ r10
Definition dwarf_registers.hpp:62
@ r9
Definition dwarf_registers.hpp:61
@ rsi
Definition dwarf_registers.hpp:56
@ r15
Definition dwarf_registers.hpp:67
@ rbp
Definition dwarf_registers.hpp:58
@ rdx
Definition dwarf_registers.hpp:53
@ rip
Definition dwarf_registers.hpp:68
@ rcx
Definition dwarf_registers.hpp:54
@ rsp
Definition dwarf_registers.hpp:59
@ rdi
Definition dwarf_registers.hpp:57
@ rax
Definition dwarf_registers.hpp:52
@ r8
Definition dwarf_registers.hpp:60
@ r12
Definition dwarf_registers.hpp:64
@ r11
Definition dwarf_registers.hpp:63
@ esp
Definition dwarf_registers.hpp:172
@ ecx
Definition dwarf_registers.hpp:169
@ edi
Definition dwarf_registers.hpp:175
@ ebx
Definition dwarf_registers.hpp:171
@ esi
Definition dwarf_registers.hpp:174
@ eip
Definition dwarf_registers.hpp:176
@ eax
Definition dwarf_registers.hpp:168
@ edx
Definition dwarf_registers.hpp:170
@ ebp
Definition dwarf_registers.hpp:173
Definition assert.hpp:19
register_context_ref make_mutable_ucontext_register_context_ref(ucontext_t &uctx RELOCO_LIFETIMEBOUND, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND) noexcept
Creates a mutable, type-erased register_context_ref backed by a POSIX ucontext_t, supporting both reg...
Definition ucontext_adapter.hpp:945
register_context_ref make_ucontext_register_context_ref(const ucontext_t &uctx RELOCO_LIFETIMEBOUND, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND) noexcept
Creates a read-only, type-erased register_context_ref backed by a POSIX ucontext_t (e....
Definition ucontext_adapter.hpp:923