3 * Register dump and debugging support
5 * (c) 2019 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
12 * Catacomb is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * Catacomb is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 #ifndef CATACOMB_REGDUMP_H
29 #define CATACOMB_REGDUMP_H
35 /*----- Header files ------------------------------------------------------*/
39 #ifndef ENABLE_ASM_DEBUG
40 # error "Assembler-level debug disabled by `configure' script."
44 # include "asm-common.h"
47 # include <mLib/bits.h>
50 /*----- Random utilities --------------------------------------------------*/
53 _(0) _(1) _(2) _(3) _(4) _(5) _(6) _(7)
55 _(8) _(9) _(10) _(11) _(12) _(13) _(14) _(15)
57 #define DO16(_) DO8(_) DOHI8(_)
61 _(16) _(17) _(18) _(19) _(20) _(21) _(22) _(23) \
62 _(24) _(25) _(26) _(27) _(28) _(29) _(30) _(31)
64 /*----- Common data structures --------------------------------------------*/
68 /* The following are good on our assembler targets. */
69 typedef signed char int8;
72 #if LONG_MAX >> 31 > 0x7fffffff
75 typedef long long int64;
77 typedef float float32;
78 typedef double float64;
79 typedef long double float80;
81 #if CPUFAM_X86 || CPUFAM_ARMEL
82 # define PTR32 void *p;
85 #if CPUFAM_AMD64 || CPUFAM_ARM64
87 # define PTR64 void *p;
90 #define SIMD_COMMON(wd) \
102 union gp32 { uint32 u32; int32 i32; PTR32 };
103 union gp64 { uint64 u64; int64 i64; PTR64 };
107 /*----- Format word layout ------------------------------------------------*/
109 #define REGF_IXMASK 0x000000ff
110 #define REGF_IXSHIFT 0
111 /* The index into the vector indicated by `REGF_SRCMASK', if applicable. */
113 #define REGF_FMTMASK 0x0000ff00
114 #define REGF_FMTSHIFT 8
115 #define REGF_HEX 0x00000100
116 #define REGF_CHR 0x00000200
117 #define REGF_FLT 0x00000400
118 #define REGF_UNSGN 0x00000800
119 #define REGF_SGN 0x00001000
120 /* How to format the value(s) found. */
122 #define REGF_TYMASK 0x00ff0000
123 #define REGF_TYSHIFT 16
124 #define REGF_80 0x00010000
125 #define REGF_64 0x00020000
126 #define REGF_32 0x00040000
127 #define REGF_16 0x00080000
128 #define REGF_8 0x00100000
129 /* Size of the value(s) to dump. */
131 #define REGF_SRCMASK 0x0f000000
132 #define REGSRC_ABS 0x01000000 /* absolute address */
133 #define REGSRC_GP 0x02000000 /* general-purpose register */
134 #define REGSRC_FP 0x03000000 /* floating-point register */
135 #define REGSRC_SIMD 0x04000000 /* SIMD vector register */
136 #define REGSRC_STMMX 0x05000000 /* x86-specific: x87/MMX register */
137 #define REGSRC_SEG 0x06000000 /* x86-specific: segment register */
138 /* Where to find the values. */
140 #define REGF_WDMASK 0xf0000000
141 #define REGF_WDSHIFT 28
142 /* If we're to print a scalar, this is zero; otherwise, log_2 of the vector
143 * register width, in bits.
146 /*----- x86 and AMD64 -----------------------------------------------------*/
148 #if CPUFAM_X86 || CPUFAM_AMD64
150 #define REGIX_FLAGS 0
162 # define REGIX_GPLIM 11
167 # define REGIX_R10 13
168 # define REGIX_R11 14
169 # define REGIX_R12 15
170 # define REGIX_R13 16
171 # define REGIX_R14 17
172 # define REGIX_R15 18
173 # define REGIX_GPLIM 19
182 #define REGIX_SEGLIM 6
184 #define REGIX_FPFLAGS 255
189 typedef union gp32 gpreg;
192 typedef union gp64 gpreg;
196 gpreg gp[REGIX_GPLIM];
197 uint16 seg[REGIX_SEGLIM];
202 #if FLT_RADIX == 2 && LDBL_MANT_DIG == 64
205 unsigned char _pad[16];
208 union xmm { SIMD_COMMON(128); };
209 union ymm { SIMD_COMMON(256); };
210 union vreg { union xmm v128[2]; union ymm v256; };
220 unsigned short fpu_cs;
221 unsigned short _res1;
223 unsigned short fpu_ds;
224 unsigned short _res2;
227 unsigned long long fpu_ip;
228 unsigned long long fpu_dp;
231 unsigned int mxcsr_mask;
233 union stmmx stmmx[8];
237 unsigned char _pad0[8*16];
243 unsigned char _pad1[96];
249 unsigned char _pad0[8*16];
259 struct xsave_avx *avx;
264 .extern regdump_gpsave
265 .extern regdump_xtsave
266 .extern regdump_xtrstr
267 .extern regdump_gprstr
271 regmap_avx = 2*WORDSZ
272 regmap_size = 3*WORDSZ
274 #define REGDEF_GPX86_COMMON(rn, RN) \
275 regsrc.e##rn = REGSRC_GP | REGIX_##RN; \
276 regty.e##rn = REGF_32; \
277 regfmt.e##rn = REGF_HEX; \
278 regsrc.r##rn = REGSRC_GP | REGIX_##RN; \
279 regty.r##rn = REGF_64; \
280 regfmt.r##rn = REGF_HEX
282 #define REGDEF_GPX86_ABCD(rn, RN) \
283 regsrc.rn##hl = (4 << REGF_WDSHIFT) | REGSRC_GP | REGIX_##RN##X; \
284 regty.rn##hl = REGF_8; \
285 regfmt.rn##hl = REGF_HEX; \
286 regsrc.rn##l = REGSRC_GP | REGIX_##RN##X; \
287 regty.rn##l = REGF_8; \
288 regfmt.rn##l = REGF_HEX; \
289 regsrc.rn##x = REGSRC_GP | REGIX_##RN##X; \
290 regty.rn##x = REGF_16; \
291 regfmt.rn##x = REGF_HEX; \
292 REGDEF_GPX86_COMMON(rn##x, RN##X)
293 REGDEF_GPX86_ABCD(a, A)
294 REGDEF_GPX86_ABCD(b, B)
295 REGDEF_GPX86_ABCD(c, C)
296 REGDEF_GPX86_ABCD(d, D)
298 regsrc.eflags = REGSRC_GP | REGIX_FLAGS
299 regty.eflags = REGF_32
303 regsrc.rflags = REGSRC_GP | REGIX_FLAGS
304 regty.rflags = REGF_64
308 #define REGDEF_GPX86_XP(rn, RN) \
309 regsrc.rn##l = REGSRC_GP | REGIX_##RN; \
310 regty.rn##l = REGF_8; \
311 regfmt.rn##l = REGF_HEX; \
312 regsrc.rn = REGSRC_GP | REGIX_##RN; \
313 regty.rn = REGF_16; \
314 regfmt.rn = REGF_HEX; \
315 REGDEF_GPX86_COMMON(rn, RN)
316 REGDEF_GPX86_XP(ip, IP)
317 REGDEF_GPX86_XP(si, SI)
318 REGDEF_GPX86_XP(di, DI)
319 REGDEF_GPX86_XP(bp, BP)
320 REGDEF_GPX86_XP(sp, SP)
323 # define REGDEF_GPAMD64(i) \
324 regsrc.r##i##b = REGSRC_GP | REGIX_R##i; \
325 regty.r##i##b = REGF_8; \
326 regfmt.r##i##b = REGF_HEX; \
327 regsrc.r##i##w = REGSRC_GP | REGIX_R##i; \
328 regty.r##i##w = REGF_16; \
329 regfmt.r##i##w = REGF_HEX; \
330 regsrc.r##i##d = REGSRC_GP | REGIX_R##i; \
331 regty.r##i##d = REGF_32; \
332 regfmt.r##i##d = REGF_HEX; \
333 regsrc.r##i = REGSRC_GP | REGIX_R##i; \
334 regty.r##i = REGF_64; \
335 regfmt.r##i = REGF_HEX;
336 DOHI8(REGDEF_GPAMD64)
339 #define REGDEF_SEG(rn, RN) \
340 regsrc.rn = REGSRC_SEG | REGIX_##RN; \
341 regty.rn = REGF_16; \
350 #define REGDEF_STMMX(i) \
351 regsrc.st##i = REGSRC_STMMX | i; \
352 regty.st##i = REGF_80; \
353 regfmt.st##i = REGF_FLT; \
354 regsrc.mm##i = (6 << REGF_WDSHIFT) | REGSRC_STMMX | i; \
355 regty.mm##i = REGF_16; \
356 regfmt.mm##i = REGF_HEX;
359 #define REGDEF_SIMD(i) \
360 regsrc.xmm##i = (7 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
361 regty.xmm##i = REGF_32; \
362 regfmt.xmm##i = REGF_HEX; \
363 regsrc.ymm##i = (8 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
364 regty.ymm##i = REGF_32; \
365 regfmt.ymm##i = REGF_HEX;
371 REGDUMP_GPSIZE = REGIX_GPLIM*WORDSZ + REGIX_SEGLIM*2
373 # if CPUFAM_AMD64 && ABI_SYSV
374 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ + 128
376 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ
379 .macro _saveregs addr=nil
380 // Save the registers, leaving r/ebp pointing to the register map.
382 // Stash r/eax. This is bletcherous: hope we don't get a signal in
383 // the next few instructions.
384 mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], R_a(r)
386 .ifnes "\addr", "nil"
387 // Collect the effective address for the following dump, leaving it
388 // in the `addr' slot of the dump.
390 mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], R_a(r)
393 // Make space for the register save area. On AMD64 with System/V
394 // ABI, also skip the red zone. Use `lea' here to preserve the
396 lea R_sp(r), [R_sp(r) - REGDUMP_SPADJ]
398 // Save flags and general-purpose registers. On 32-bit x86, we save
399 // ebx here and establish a GOT pointer here for the benefit of the
400 // PLT-indirect calls made later on.
403 mov [esp + 4*REGIX_BX], ebx
406 callext regdump_gpsave
408 // Make space for the extended registers.
410 callext regdump_xtsave
412 // Prepare for calling back into C. On 32-bit x86, leave space for
413 // the arguments and set up the GOT pointer; on AMD64 Windows, leave
414 // the `shadow space' for the called-function's arguments. Also,
415 // forcibly align the stack pointer to a 16-byte boundary.
425 // Restore registers.
427 // We assume r/ebp still points to the register map.
428 callext regdump_xtrstr
430 callext regdump_gprstr
432 lea R_sp(r), [R_sp(r) + REGDUMP_SPADJ]
446 mov R_a(r), [R_bp(r) + regmap_gp]
448 mov eax, [eax + REGIX_ADDR*WORDSZ]
451 mov rdi, [rax + REGIX_ADDR*WORDSZ]
453 mov rcx, [rax + REGIX_ADDR*WORDSZ]
460 mov dword ptr [esp + 4], 0
468 lea eax, [INTADDR(.L$_reglbl$\@)]
471 lea rsi, [INTADDR(.L$_reglbl$\@)]
473 lea rdx, [INTADDR(.L$_reglbl$\@)]
484 mov dword ptr [esp + 8], \arg
496 /*----- ARM32 -------------------------------------------------------------*/
501 extern unsigned regdump__flags;
506 #define REGIX_CPSR 16
507 #define REGIX_ADDR 17
508 #define REGIX_GPLIM 18
510 #define REGIX_FPSCR 255
514 union neon64 { SIMD_COMMON(64); };
515 union neon128 { SIMD_COMMON(128); };
517 struct gpsave { union gp32 r[REGIX_GPLIM]; };
536 .extern regdump_gpsave
537 .extern regdump_xtsave
538 .extern regdump_xtrstr
539 .extern regdump_gprstr
545 #define REGDEF_GP(i) \
546 regsrc.r##i = REGSRC_GP | i; \
547 regty.r##i = REGF_32; \
548 regfmt.r##i = REGF_HEX;
551 regsrc.cpsr = REGSRC_GP | REGIX_CPSR
555 #define REGDEF_NEONS(i) \
556 regsrc.s##i = REGSRC_FP | i; \
557 regty.s##i = REGF_32; \
558 regfmt.s##i = REGF_FLT;
561 #define REGDEF_NEOND(i) \
562 regsrc.d##i = (6 << REGF_WDSHIFT) | REGSRC_FP | i; \
563 regty.d##i = REGF_32; \
564 regfmt.d##i = REGF_HEX;
567 #define REGDEF_NEONQ(i) \
568 regsrc.q##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
569 regty.q##i = REGF_32; \
570 regfmt.q##i = REGF_HEX;
573 regsrc.fpscr = REGSRC_FP | REGIX_FPSCR
574 regty.fpscr = REGF_32
577 REGDUMP_GPSIZE = 4*REGIX_GPLIM
578 REGDUMP_FPSIZE_D16 = 8 + 16*8
579 REGDUMP_FPSIZE_D32 = 8 + 32*8
581 .macro _saveregs base=nil, off=#0
582 // Save the registers, leaving r4 pointing to the register map.
584 // Stash r14. This is bletcherous: hope we don't get a signal in
585 // the next few instructions.
586 str r14, [r13, #-REGDUMP_GPSIZE + 14*4]
588 .ifnes "\base,\off", "nil,#0"
589 // Collect the effective address for the following dump, leaving it
590 // in the `addr' slot of the dump.
591 .ifeqs "\base", "nil"
596 str r14, [r13, #-REGDUMP_GPSIZE + 4*REGIX_ADDR]
599 // Make space for the register save area.
600 sub r13, r13, #REGDUMP_GPSIZE
602 // Save flags and general-purpose registers.
603 str r12, [r13, #4*12]
606 // Make space for the extended registers.
610 // Prepare for calling back into C.
618 // Restore registers.
620 // We assume r4 still points to the register map.
624 ldr r14, [r13, #14*4]
625 add r13, r13, #REGDUMP_GPSIZE
637 adrl r1, .L$_reglbl$\@
646 movw r2, #\arg&0xffff
647 movt r2, #(\arg >> 16)&0xffff
654 /*----- ARM64 -------------------------------------------------------------*/
658 #define REGIX_NZCV 32
660 #define REGIX_ADDR 34
661 #define REGIX_GPLIM 36
663 #define REGIX_FPFLAGS 255
667 union v128 { SIMD_COMMON(128); };
669 struct gpsave { union gp64 r[REGIX_GPLIM]; };
683 .extern regdump_gpsave
684 .extern regdump_xtsave
685 .extern regdump_xtrstr
686 .extern regdump_gprstr
692 #define REGDEF_GP(i) \
693 regsrc.x##i = REGSRC_GP | i; \
694 regty.x##i = REGF_64; \
695 regfmt.x##i = REGF_HEX; \
696 regsrc.w##i = REGSRC_GP | i; \
697 regty.w##i = REGF_32; \
698 regfmt.w##i = REGF_HEX;
701 regsrc.sp = REGSRC_GP | 31
705 regsrc.pc = REGSRC_GP | REGIX_PC
709 regsrc.nzcv = REGSRC_GP | REGIX_NZCV
713 #define REGDEF_FP(i) \
714 regsrc.b##i = REGSRC_FP | i; \
715 regty.b##i = REGF_8; \
716 regfmt.b##i = REGF_HEX; \
717 regsrc.h##i = REGSRC_FP | i; \
718 regty.h##i = REGF_16; \
719 regfmt.h##i = REGF_HEX; \
720 regsrc.s##i = REGSRC_FP | i; \
721 regty.s##i = REGF_32; \
722 regfmt.s##i = REGF_FLT; \
723 regsrc.d##i = REGSRC_FP | i; \
724 regty.d##i = REGF_64; \
725 regfmt.d##i = REGF_FLT; \
726 regsrc.v##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
727 regty.v##i = REGF_32; \
728 regfmt.v##i = REGF_HEX;
731 regsrc.fpflags = REGSRC_FP | REGIX_FPFLAGS
732 regty.fpflags = REGF_32
735 REGDUMP_GPSIZE = 8*REGIX_GPLIM
736 REGDUMP_FPSIZE = 16 + 16 + 32*16
738 .macro _saveregs base=nil, off=#0
739 // Save the registers, leaving x20 pointing to the register map.
741 // Stash x30. This is bletcherous: hope we don't get a signal in
742 // the next few instructions.
743 str x30, [sp, #-REGDUMP_GPSIZE + 30*8]
745 .ifnes "\base,\off", "nil,#0"
746 // Collect the effective address for the following dump, leaving it
747 // in the `addr' slot of the dump.
748 .ifeqs "\base", "nil"
753 str x30, [sp, #-REGDUMP_GPSIZE + 8*REGIX_ADDR]
756 // Make space for the register save area.
757 sub sp, sp, #REGDUMP_GPSIZE
759 // Save flags and general-purpose registers.
760 stp x16, x17, [sp, #8*16]
763 // Make space for the extended registers.
769 // Restore registers.
771 // We assume x21 still points to the register map.
776 add sp, sp, #REGDUMP_GPSIZE
788 adr x1, .L$_reglbl$\@
797 movz w2, #\arg&0xffff
798 movk w2, #(\arg >> 16)&0xffff, lsl #16
805 /*----- Functions provided ------------------------------------------------*/
807 /* --- @regdump_init@ --- *
813 * Use: Performs one-time initialization for register dumping. In
814 * particular, this performs CPU feature detection on platforms
815 * where that is a difficult task: without it, registers
816 * corresponding to optional architectural features can be
817 * neither printed nor preserved by the register-dump machinery.
821 extern void regdump_init(void);
824 /* --- @regdump@ --- *
826 * Arguments: @const void *base@ = pointer to base structure, corresponding
827 * to the @REGF_SRCMASK@ part of @f@
828 * @const char *lbl@ = label to print
829 * @uint32 f@ = format control word; see @REGF_...@
833 * Use: Dump a register value, or chunk of memory.
835 * This function is not usually called directly; instead, use
836 * the `reg' or `mem' assembler macros.
840 extern void regdump(const void *base, const char *lbl, uint32 f);
845 /* --- @regdump_gp@, @regdump_fp@, @regdump_simd@ --- *
847 * Arguments: @const struct regmap *map@ = pointer to register map
851 * Use: Dump the general-purpose/floating-point/SIMD registers.
853 * This function is not usually called directly; instead, use
854 * the `regdump' assembler macro.
858 extern void regdump_gp(const struct regmap */*map*/);
859 extern void regdump_fp(const struct regmap */*map*/);
860 extern void regdump_simd(const struct regmap */*map*/);
867 /* --- @regdump_freshline@ --- *
873 * Use: Begin a fresh line of output.
877 extern void regdump_freshline(void);
879 .extern regdump_freshline
882 /*----- Main user interface macros ----------------------------------------*/
888 callext regdump_freshline
892 .macro reg lbl, rn, fmt=0
896 .L$reg.fmt$\@ = regsrc.\rn | \fmt | \
897 (((\fmt®F_TYMASK) == 0)®ty.\rn) | \
898 (((\fmt®F_FMTMASK) == 0)®fmt.\rn)
899 _regfmt .L$reg.fmt$\@
904 .macro mem lbl, addr, fmt=0
908 .L$mem.fmt$\@ = REGSRC_ABS | \fmt | \
909 (((\fmt®F_TYMASK) == 0)®F_32) | \
910 (((\fmt®F_FMTMASK) == 0)®F_HEX)
911 _regfmt .L$mem.fmt$\@
916 .macro regdump gp=nil, fp=nil, simd=nil
926 .ifnes "\simd", "nil"
935 /*----- That's all, folks -------------------------------------------------*/