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 -------------------------------------------------*/