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 #define REGSRC_NONE 0x0f000000 /* just a message */
139 /* Where to find the values. */
141 #define REGF_WDMASK 0xf0000000
142 #define REGF_WDSHIFT 28
143 /* If we're to print a scalar, this is zero; otherwise, log_2 of the vector
144 * register width, in bits.
147 /*----- x86 and AMD64 -----------------------------------------------------*/
149 #if CPUFAM_X86 || CPUFAM_AMD64
151 #define REGIX_FLAGS 0
163 # define REGIX_GPLIM 11
168 # define REGIX_R10 13
169 # define REGIX_R11 14
170 # define REGIX_R12 15
171 # define REGIX_R13 16
172 # define REGIX_R14 17
173 # define REGIX_R15 18
174 # define REGIX_GPLIM 19
183 #define REGIX_SEGLIM 6
185 #define REGIX_FPFLAGS 255
190 typedef union gp32 gpreg;
193 typedef union gp64 gpreg;
197 gpreg gp[REGIX_GPLIM];
198 uint16 seg[REGIX_SEGLIM];
203 #if FLT_RADIX == 2 && LDBL_MANT_DIG == 64
206 unsigned char _pad[16];
209 union xmm { SIMD_COMMON(128); };
210 union ymm { SIMD_COMMON(256); };
211 union vreg { union xmm v128[2]; union ymm v256; };
221 unsigned short fpu_cs;
222 unsigned short _res1;
224 unsigned short fpu_ds;
225 unsigned short _res2;
228 unsigned long long fpu_ip;
229 unsigned long long fpu_dp;
232 unsigned int mxcsr_mask;
234 union stmmx stmmx[8];
238 unsigned char _pad0[8*16];
244 unsigned char _pad1[96];
250 unsigned char _pad0[8*16];
260 struct xsave_avx *avx;
265 .extern regdump_gpsave
266 // Save general-purpose registers at r/esp; flags, r/eax, and, on
267 // x86, ebx, should have been filled in already, together with the
268 // focus address, in `REGIX_ADDR', if relevant. Return required
269 // extended save area size in ecx, and leave r/ebp pointing to the
272 .extern regdump_xtsave
273 // Save extended registers at r/esp, leaving r/ebp pointing to the
276 .extern regdump_xtrstr
277 // Restore extended registers from register map in r/ebp, leaving
278 // r/ebp pointing to general-purpose save area.
280 .extern regdump_gprstr
281 // Restore general-purpose registers, except r/esp, from save area
286 regmap_avx = 2*WORDSZ
287 regmap_size = 3*WORDSZ
289 #define REGDEF_GPX86_COMMON(rn, ix) \
290 regsrc.e##rn = REGSRC_GP | ix; \
291 regty.e##rn = REGF_32; \
292 regfmt.e##rn = REGF_HEX; \
293 regsrc.r##rn = REGSRC_GP | ix; \
294 regty.r##rn = REGF_64; \
295 regfmt.r##rn = REGF_HEX
297 #define REGDEF_GPX86_ABCD(rn, RN) \
298 regsrc.rn##hl = (4 << REGF_WDSHIFT) | REGSRC_GP | REGIX_##RN##X; \
299 regty.rn##hl = REGF_8; \
300 regfmt.rn##hl = REGF_HEX; \
301 regsrc.rn##l = REGSRC_GP | REGIX_##RN##X; \
302 regty.rn##l = REGF_8; \
303 regfmt.rn##l = REGF_HEX; \
304 regsrc.rn##x = REGSRC_GP | REGIX_##RN##X; \
305 regty.rn##x = REGF_16; \
306 regfmt.rn##x = REGF_HEX; \
307 REGDEF_GPX86_COMMON(rn##x, REGIX_##RN##X)
308 REGDEF_GPX86_ABCD(a, A)
309 REGDEF_GPX86_ABCD(b, B)
310 REGDEF_GPX86_ABCD(c, C)
311 REGDEF_GPX86_ABCD(d, D)
313 regsrc.eflags = REGSRC_GP | REGIX_FLAGS
314 regty.eflags = REGF_32
318 regsrc.rflags = REGSRC_GP | REGIX_FLAGS
319 regty.rflags = REGF_64
323 #define REGDEF_GPX86_XP(rn, RN) \
324 regsrc.rn##l = REGSRC_GP | REGIX_##RN; \
325 regty.rn##l = REGF_8; \
326 regfmt.rn##l = REGF_HEX; \
327 regsrc.rn = REGSRC_GP | REGIX_##RN; \
328 regty.rn = REGF_16; \
329 regfmt.rn = REGF_HEX; \
330 REGDEF_GPX86_COMMON(rn, REGIX_##RN)
331 REGDEF_GPX86_XP(ip, IP)
332 REGDEF_GPX86_XP(si, SI)
333 REGDEF_GPX86_XP(di, DI)
334 REGDEF_GPX86_XP(bp, BP)
335 REGDEF_GPX86_XP(sp, SP)
338 # define REGDEF_GPAMD64(i) \
339 regsrc.r##i##b = REGSRC_GP | REGIX_R##i; \
340 regty.r##i##b = REGF_8; \
341 regfmt.r##i##b = REGF_HEX; \
342 regsrc.r##i##w = REGSRC_GP | REGIX_R##i; \
343 regty.r##i##w = REGF_16; \
344 regfmt.r##i##w = REGF_HEX; \
345 regsrc.r##i##d = REGSRC_GP | REGIX_R##i; \
346 regty.r##i##d = REGF_32; \
347 regfmt.r##i##d = REGF_HEX; \
348 regsrc.r##i = REGSRC_GP | REGIX_R##i; \
349 regty.r##i = REGF_64; \
350 regfmt.r##i = REGF_HEX;
351 DOHI8(REGDEF_GPAMD64)
354 #define REGDEF_SEG(rn, RN) \
355 regsrc.rn = REGSRC_SEG | REGIX_##RN; \
356 regty.rn = REGF_16; \
365 #define REGDEF_STMMX(i) \
366 regsrc.st##i = REGSRC_STMMX | i; \
367 regty.st##i = REGF_80; \
368 regfmt.st##i = REGF_FLT; \
369 regsrc.mm##i = (6 << REGF_WDSHIFT) | REGSRC_STMMX | i; \
370 regty.mm##i = REGF_16; \
371 regfmt.mm##i = REGF_HEX;
374 #define REGDEF_SIMD(i) \
375 regsrc.xmm##i = (7 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
376 regty.xmm##i = REGF_32; \
377 regfmt.xmm##i = REGF_HEX; \
378 regsrc.ymm##i = (8 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
379 regty.ymm##i = REGF_32; \
380 regfmt.ymm##i = REGF_HEX;
386 REGDUMP_GPSIZE = REGIX_GPLIM*WORDSZ + REGIX_SEGLIM*2
388 # if CPUFAM_AMD64 && ABI_SYSV
389 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ + 128
391 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ
394 .macro _saveregs addr=nil
395 // Save the registers, leaving r/ebp pointing to the register map.
397 // Stash r/eax. This is bletcherous: hope we don't get a signal in
398 // the next few instructions.
399 mov [SP - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], AX
401 .ifnes "\addr", "nil"
402 // Collect the focus address for the following dump, leaving it in
403 // the `addr' slot of the dump.
405 mov [SP - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], AX
408 // Make space for the register save area. On AMD64 with System/V
409 // ABI, also skip the red zone. Use `lea' here to preserve the
411 lea SP, [SP - REGDUMP_SPADJ]
413 // Save flags and general-purpose registers. On 32-bit x86, we save
414 // ebx here and establish a GOT pointer here for the benefit of the
415 // PLT-indirect calls made later on.
418 mov [SP + 4*REGIX_BX], ebx
421 callext F(regdump_gpsave)
423 // Make space for the extended registers.
425 callext F(regdump_xtsave)
427 // Prepare for calling back into C. On 32-bit x86, leave space for
428 // the arguments and set up the GOT pointer; on AMD64 Windows, leave
429 // the `shadow space' for the called-function's arguments. Also,
430 // forcibly align the stack pointer to a 16-byte boundary.
440 // Restore registers.
442 // We assume r/ebp still points to the register map.
443 callext F(regdump_xtrstr)
445 callext F(regdump_gprstr)
447 lea SP, [SP + REGDUMP_SPADJ]
472 mov AX, [BP + regmap_gp]
474 mov eax, [eax + REGIX_ADDR*WORDSZ]
477 mov rdi, [rax + REGIX_ADDR*WORDSZ]
479 mov rcx, [rax + REGIX_ADDR*WORDSZ]
486 mov dword ptr [SP + 4], 0
494 lea eax, [INTADDR(.L$_reglbl$\@)]
497 lea rsi, [INTADDR(.L$_reglbl$\@)]
499 lea rdx, [INTADDR(.L$_reglbl$\@)]
510 mov dword ptr [SP + 8], \arg
522 /*----- ARM32 -------------------------------------------------------------*/
527 extern unsigned regdump__flags;
532 #define REGIX_CPSR 16
533 #define REGIX_ADDR 17
534 #define REGIX_GPLIM 18
536 #define REGIX_FPSCR 255
540 union neon64 { SIMD_COMMON(64); };
541 union neon128 { SIMD_COMMON(128); };
543 struct gpsave { union gp32 r[REGIX_GPLIM]; };
562 .extern regdump_gpsave
563 // Save general-purpose registers at r13; r12 and r14 should have
564 // been filled in already, along with the focus address in
565 // `REGIX_ADDR', if relevant. Return required extended save area
566 // size in r0, leave r4 pointing to the save area, and set r6 to
567 // the focus address.
569 .extern regdump_xtsave
570 // Save extended registers at r13, leaving r5 pointing to the
573 .extern regdump_xtrstr
574 // Restore extended registers from register map at r5.
576 .extern regdump_gprstr
577 // Restore general-purpose registers, except r13 and r14, from save
584 #define REGDEF_GP(i) \
585 regsrc.r##i = REGSRC_GP | i; \
586 regty.r##i = REGF_32; \
587 regfmt.r##i = REGF_HEX;
590 regsrc.cpsr = REGSRC_GP | REGIX_CPSR
594 #define REGDEF_NEONS(i) \
595 regsrc.s##i = REGSRC_FP | i; \
596 regty.s##i = REGF_32; \
597 regfmt.s##i = REGF_FLT;
600 #define REGDEF_NEOND(i) \
601 regsrc.d##i = (6 << REGF_WDSHIFT) | REGSRC_FP | i; \
602 regty.d##i = REGF_32; \
603 regfmt.d##i = REGF_HEX;
606 #define REGDEF_NEONQ(i) \
607 regsrc.q##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
608 regty.q##i = REGF_32; \
609 regfmt.q##i = REGF_HEX;
612 regsrc.fpscr = REGSRC_FP | REGIX_FPSCR
613 regty.fpscr = REGF_32
616 REGDUMP_GPSIZE = 4*REGIX_GPLIM
617 REGDUMP_FPSIZE_D16 = 8 + 16*8
618 REGDUMP_FPSIZE_D32 = 8 + 32*8
620 .macro _saveregs base=nil, off=#0
621 // Save the registers, leaving r4 pointing to the register map.
623 // Stash r14. This is bletcherous: hope we don't get a signal in
624 // the next few instructions.
625 str r14, [r13, #-REGDUMP_GPSIZE + 14*4]
627 .ifnes "\base,\off", "nil,#0"
628 // Collect the focus address for the following dump, leaving it in
629 // the `addr' slot of the dump.
630 .ifeqs "\base", "nil"
635 str r14, [r13, #-REGDUMP_GPSIZE + 4*REGIX_ADDR]
638 // Make space for the register save area.
639 sub r13, r13, #REGDUMP_GPSIZE
641 // Save flags and general-purpose registers.
643 str r14, [r13, #4*REGIX_CPSR]
644 str r12, [r13, #4*12]
647 // Make space for the extended registers.
651 // Prepare for calling back into C.
659 // Restore registers.
661 // We assume r4 still points to the register map.
665 ldr r14, [r13, #14*4]
666 add r13, r13, #REGDUMP_GPSIZE
682 adrl r1, .L$_reglbl$\@
691 movw r2, #(\arg)&0xffff
692 movt r2, #((\arg) >> 16)&0xffff
699 /*----- ARM64 -------------------------------------------------------------*/
703 #define REGIX_NZCV 32
705 #define REGIX_ADDR 34
706 #define REGIX_GPLIM 36
708 #define REGIX_FPFLAGS 255
712 union v128 { SIMD_COMMON(128); };
714 struct gpsave { union gp64 r[REGIX_GPLIM]; };
728 .extern regdump_gpsave
729 // Save general-purpose registers at sp; x16, x17, and x30, should
730 // have been filled in already, along with the focus address in
731 // `REGIX_ADDR', if relevant. Return required extended save area
732 // size in x0, leave x20 pointing to the save area, and set x22 to
733 // the focus address.
735 .extern regdump_xtsave
736 // Save extended registers at sp, leaving x21 pointing to the
739 .extern regdump_xtrstr
740 // Restore extended registers from register map at x21.
742 .extern regdump_gprstr
743 // Restore general-purpose registers, except sp and x30, from save
750 #define REGDEF_GP(i) \
751 regsrc.x##i = REGSRC_GP | i; \
752 regty.x##i = REGF_64; \
753 regfmt.x##i = REGF_HEX; \
754 regsrc.w##i = REGSRC_GP | i; \
755 regty.w##i = REGF_32; \
756 regfmt.w##i = REGF_HEX;
759 regsrc.sp = REGSRC_GP | 31
763 regsrc.pc = REGSRC_GP | REGIX_PC
767 regsrc.nzcv = REGSRC_GP | REGIX_NZCV
771 #define REGDEF_FP(i) \
772 regsrc.b##i = REGSRC_FP | i; \
773 regty.b##i = REGF_8; \
774 regfmt.b##i = REGF_HEX; \
775 regsrc.h##i = REGSRC_FP | i; \
776 regty.h##i = REGF_16; \
777 regfmt.h##i = REGF_HEX; \
778 regsrc.s##i = REGSRC_FP | i; \
779 regty.s##i = REGF_32; \
780 regfmt.s##i = REGF_FLT; \
781 regsrc.d##i = REGSRC_FP | i; \
782 regty.d##i = REGF_64; \
783 regfmt.d##i = REGF_FLT; \
784 regsrc.v##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
785 regty.v##i = REGF_32; \
786 regfmt.v##i = REGF_HEX;
789 regsrc.fpflags = REGSRC_FP | REGIX_FPFLAGS
790 regty.fpflags = REGF_32
793 REGDUMP_GPSIZE = 8*REGIX_GPLIM
794 REGDUMP_FPSIZE = 16 + 16 + 32*16
796 .macro _saveregs base=nil, off=#0
797 // Save the registers, leaving x20 pointing to the register map.
799 // Stash x30. This is bletcherous: hope we don't get a signal in
800 // the next few instructions.
801 str x30, [sp, #-REGDUMP_GPSIZE + 30*8]
803 .ifnes "\base,\off", "nil,#0"
804 // Collect the focus address for the following dump, leaving it in
805 // the `addr' slot of the dump.
806 .ifeqs "\base", "nil"
811 str x30, [sp, #-REGDUMP_GPSIZE + 8*REGIX_ADDR]
814 // Make space for the register save area.
815 sub sp, sp, #REGDUMP_GPSIZE
817 // Save flags and general-purpose registers. The PLT linkage code
818 // makes free with x8--x17, so we must save those here.
820 str x30, [sp, #8*REGIX_NZCV]
821 stp x8, x9, [sp, #64]
822 stp x10, x11, [sp, #80]
823 stp x12, x13, [sp, #96]
824 stp x14, x15, [sp, #112]
825 stp x16, x17, [sp, #128]
828 // Make space for the extended registers.
834 // Restore registers.
836 // We assume x21 still points to the register map.
841 add sp, sp, #REGDUMP_GPSIZE
857 adr x1, .L$_reglbl$\@
866 movz w2, #(\arg)&0xffff
867 movk w2, #((\arg) >> 16)&0xffff, lsl #16
874 /*----- Functions provided ------------------------------------------------*/
876 /* --- @regdump_init@ --- *
882 * Use: Performs one-time initialization for register dumping. In
883 * particular, this performs CPU feature detection on platforms
884 * where that is a difficult task: without it, registers
885 * corresponding to optional architectural features can be
886 * neither printed nor preserved by the register-dump machinery.
890 extern void regdump_init(void);
893 /* --- @regdump@ --- *
895 * Arguments: @const void *base@ = pointer to base structure, corresponding
896 * to the @REGF_SRCMASK@ part of @f@
897 * @const char *lbl@ = label to print
898 * @uint32 f@ = format control word; see @REGF_...@
902 * Use: Dump a register value, or chunk of memory.
904 * This function is not usually called directly; instead, use
905 * the `reg' or `mem' assembler macros.
909 extern void regdump(const void *base, const char *lbl, uint32 f);
914 /* --- @regdump_gp@, @regdump_fp@, @regdump_simd@ --- *
916 * Arguments: @const struct regmap *map@ = pointer to register map
920 * Use: Dump the general-purpose/floating-point/SIMD registers.
922 * This function is not usually called directly; instead, use
923 * the `regdump' assembler macro.
927 extern void regdump_gp(const struct regmap */*map*/);
928 extern void regdump_fp(const struct regmap */*map*/);
929 extern void regdump_simd(const struct regmap */*map*/);
936 /* --- @regdump_freshline@ --- *
942 * Use: Begin a fresh line of output.
946 extern void regdump_freshline(void);
948 .extern regdump_freshline
951 /*----- Main user interface macros ----------------------------------------*/
957 callext F(regdump_freshline)
965 _regfmt REGSRC_NONE | (1 << REGF_WDSHIFT)
970 .macro reg lbl, rn, fmt=0
974 .L$reg.fmt$\@ = regsrc.\rn | \fmt | \
975 (((\fmt®F_TYMASK) == 0)®ty.\rn) | \
976 (((\fmt®F_FMTMASK) == 0)®fmt.\rn)
977 _regfmt .L$reg.fmt$\@
982 .macro mem lbl, addr, fmt=0
986 .L$mem.fmt$\@ = REGSRC_ABS | \fmt | \
987 (((\fmt®F_TYMASK) == 0)®F_32) | \
988 (((\fmt®F_FMTMASK) == 0)®F_HEX)
989 _regfmt .L$mem.fmt$\@
994 .macro regdump gp=nil, fp=nil, simd=nil
998 callext F(regdump_gp)
1002 callext F(regdump_fp)
1004 .ifnes "\simd", "nil"
1006 callext F(regdump_simd)
1013 /*----- That's all, folks -------------------------------------------------*/