--- /dev/null
+/* -*-c-*-
+ *
+ * Register dump and debugging support
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef CATACOMB_REGDUMP_H
+#define CATACOMB_REGDUMP_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#ifndef ENABLE_ASM_DEBUG
+# error "Assembler-level debug disabled by `configure' script."
+#endif
+
+#if __ASSEMBLER__
+# include "asm-common.h"
+#else
+# include <float.h>
+# include <mLib/bits.h>
+#endif
+
+/*----- Random utilities --------------------------------------------------*/
+
+#define DO8(_) \
+ _(0) _(1) _(2) _(3) _(4) _(5) _(6) _(7)
+#define DOHI8(_) \
+ _(8) _(9) _(10) _(11) _(12) _(13) _(14) _(15)
+
+#define DO16(_) DO8(_) DOHI8(_)
+
+#define DO32(_) \
+ DO16(_) \
+ _(16) _(17) _(18) _(19) _(20) _(21) _(22) _(23) \
+ _(24) _(25) _(26) _(27) _(28) _(29) _(30) _(31)
+
+/*----- Common data structures --------------------------------------------*/
+
+#if !__ASSEMBLER__
+
+/* The following are good on our assembler targets. */
+typedef signed char int8;
+typedef short int16;
+typedef int int32;
+#if LONG_MAX >> 31 > 0x7fffffff
+ typedef long int64;
+#else
+ typedef long long int64;
+#endif
+typedef float float32;
+typedef double float64;
+typedef long double float80;
+
+#if CPUFAM_X86 || CPUFAM_ARMEL
+# define PTR32 void *p;
+# define PTR64
+#endif
+#if CPUFAM_AMD64 || CPUFAM_ARM64
+# define PTR32
+# define PTR64 void *p;
+#endif
+
+#define SIMD_COMMON(wd) \
+ uint8 u8[wd/8]; \
+ int8 i8[wd/8]; \
+ uint16 u16[wd/16]; \
+ int16 i16[wd/16]; \
+ uint32 u32[wd/32]; \
+ int32 i32[wd/32]; \
+ uint64 u64[wd/64]; \
+ int64 i64[wd/64]; \
+ float32 f32[wd/32]; \
+ float64 f64[wd/64]
+
+union gp32 { uint32 u32; int32 i32; PTR32 };
+union gp64 { uint64 u64; int64 i64; PTR64 };
+
+#endif
+
+/*----- Format word layout ------------------------------------------------*/
+
+#define REGF_IXMASK 0x000000ff
+#define REGF_IXSHIFT 0
+/* The index into the vector indicated by `REGF_SRCMASK', if applicable. */
+
+#define REGF_FMTMASK 0x0000ff00
+#define REGF_FMTSHIFT 8
+#define REGF_HEX 0x00000100
+#define REGF_CHR 0x00000200
+#define REGF_FLT 0x00000400
+#define REGF_UNSGN 0x00000800
+#define REGF_SGN 0x00001000
+/* How to format the value(s) found. */
+
+#define REGF_TYMASK 0x00ff0000
+#define REGF_TYSHIFT 16
+#define REGF_80 0x00010000
+#define REGF_64 0x00020000
+#define REGF_32 0x00040000
+#define REGF_16 0x00080000
+#define REGF_8 0x00100000
+/* Size of the value(s) to dump. */
+
+#define REGF_SRCMASK 0x0f000000
+#define REGSRC_ABS 0x01000000 /* absolute address */
+#define REGSRC_GP 0x02000000 /* general-purpose register */
+#define REGSRC_FP 0x03000000 /* floating-point register */
+#define REGSRC_SIMD 0x04000000 /* SIMD vector register */
+#define REGSRC_STMMX 0x05000000 /* x86-specific: x87/MMX register */
+#define REGSRC_SEG 0x06000000 /* x86-specific: segment register */
+/* Where to find the values. */
+
+#define REGF_WDMASK 0xf0000000
+#define REGF_WDSHIFT 28
+/* If we're to print a scalar, this is zero; otherwise, log_2 of the vector
+ * register width, in bits.
+ */
+
+/*----- x86 and AMD64 -----------------------------------------------------*/
+
+#if CPUFAM_X86 || CPUFAM_AMD64
+
+#define REGIX_FLAGS 0
+#define REGIX_IP 1
+#define REGIX_ADDR 2
+#define REGIX_AX 3
+#define REGIX_BX 4
+#define REGIX_CX 5
+#define REGIX_DX 6
+#define REGIX_SI 7
+#define REGIX_DI 8
+#define REGIX_BP 9
+#define REGIX_SP 10
+#if CPUFAM_X86
+# define REGIX_GPLIM 11
+#endif
+#if CPUFAM_AMD64
+# define REGIX_R8 11
+# define REGIX_R9 12
+# define REGIX_R10 13
+# define REGIX_R11 14
+# define REGIX_R12 15
+# define REGIX_R13 16
+# define REGIX_R14 17
+# define REGIX_R15 18
+# define REGIX_GPLIM 19
+#endif
+
+#define REGIX_CS 0
+#define REGIX_DS 1
+#define REGIX_SS 2
+#define REGIX_ES 3
+#define REGIX_FS 4
+#define REGIX_GS 5
+#define REGIX_SEGLIM 6
+
+#define REGIX_FPFLAGS 255
+
+#if !__ASSEMBLER__
+
+#if CPUFAM_X86
+typedef union gp32 gpreg;
+#endif
+#if CPUFAM_AMD64
+typedef union gp64 gpreg;
+#endif
+
+struct gpsave {
+ gpreg gp[REGIX_GPLIM];
+ uint16 seg[REGIX_SEGLIM];
+};
+
+union stmmx {
+ SIMD_COMMON(64);
+#if FLT_RADIX == 2 && LDBL_MANT_DIG == 64
+ long double f80;
+#endif
+unsigned char _pad[16];
+};
+
+union xmm { SIMD_COMMON(128); };
+union ymm { SIMD_COMMON(256); };
+union vreg { union xmm v128[2]; union ymm v256; };
+
+struct fxsave {
+ unsigned short fcw;
+ unsigned short fsw;
+ unsigned char ftw;
+ unsigned char _res0;
+ unsigned short fop;
+#if CPUFAM_X86
+ unsigned int fpu_ip;
+ unsigned short fpu_cs;
+ unsigned short _res1;
+ unsigned int fpu_dp;
+ unsigned short fpu_ds;
+ unsigned short _res2;
+#endif
+#if CPUFAM_AMD64
+ unsigned long long fpu_ip;
+ unsigned long long fpu_dp;
+#endif
+ unsigned int mxcsr;
+ unsigned int mxcsr_mask;
+
+ union stmmx stmmx[8];
+
+#if CPUFAM_X86
+ union xmm xmm[8];
+ unsigned char _pad0[8*16];
+#endif
+#if CPUFAM_AMD64
+ union xmm xmm[16];
+#endif
+
+ unsigned char _pad1[96];
+};
+
+struct xsave_avx {
+#if CPUFAM_X86
+ union xmm ymmh[8];
+ unsigned char _pad0[8*16];
+#endif
+#if CPUFAM_AMD64
+ union xmm ymmh[16];
+#endif
+};
+
+struct regmap {
+ struct gpsave *gp;
+ struct fxsave *fx;
+ struct xsave_avx *avx;
+};
+
+#else
+
+ .extern regdump_gpsave
+ .extern regdump_xtsave
+ .extern regdump_xtrstr
+ .extern regdump_gprstr
+
+ regmap_gp = 0*WORDSZ
+ regmap_fx = 1*WORDSZ
+ regmap_avx = 2*WORDSZ
+ regmap_size = 3*WORDSZ
+
+#define REGDEF_GPX86_COMMON(rn, RN) \
+ regsrc.e##rn = REGSRC_GP | REGIX_##RN; \
+ regty.e##rn = REGF_32; \
+ regfmt.e##rn = REGF_HEX; \
+ regsrc.r##rn = REGSRC_GP | REGIX_##RN; \
+ regty.r##rn = REGF_64; \
+ regfmt.r##rn = REGF_HEX
+
+#define REGDEF_GPX86_ABCD(rn, RN) \
+ regsrc.rn##hl = (4 << REGF_WDSHIFT) | REGSRC_GP | REGIX_##RN##X; \
+ regty.rn##hl = REGF_8; \
+ regfmt.rn##hl = REGF_HEX; \
+ regsrc.rn##l = REGSRC_GP | REGIX_##RN##X; \
+ regty.rn##l = REGF_8; \
+ regfmt.rn##l = REGF_HEX; \
+ regsrc.rn##x = REGSRC_GP | REGIX_##RN##X; \
+ regty.rn##x = REGF_16; \
+ regfmt.rn##x = REGF_HEX; \
+ REGDEF_GPX86_COMMON(rn##x, RN##X)
+REGDEF_GPX86_ABCD(a, A)
+REGDEF_GPX86_ABCD(b, B)
+REGDEF_GPX86_ABCD(c, C)
+REGDEF_GPX86_ABCD(d, D)
+
+ regsrc.eflags = REGSRC_GP | REGIX_FLAGS
+ regty.eflags = REGF_32
+ regty.eflags = 0
+
+#if CPUFAM_AMD64
+ regsrc.rflags = REGSRC_GP | REGIX_FLAGS
+ regty.rflags = REGF_64
+ regty.rflags = 0
+#endif
+
+#define REGDEF_GPX86_XP(rn, RN) \
+ regsrc.rn##l = REGSRC_GP | REGIX_##RN; \
+ regty.rn##l = REGF_8; \
+ regfmt.rn##l = REGF_HEX; \
+ regsrc.rn = REGSRC_GP | REGIX_##RN; \
+ regty.rn = REGF_16; \
+ regfmt.rn = REGF_HEX; \
+ REGDEF_GPX86_COMMON(rn, RN)
+REGDEF_GPX86_XP(ip, IP)
+REGDEF_GPX86_XP(si, SI)
+REGDEF_GPX86_XP(di, DI)
+REGDEF_GPX86_XP(bp, BP)
+REGDEF_GPX86_XP(sp, SP)
+
+#if CPUFAM_AMD64
+# define REGDEF_GPAMD64(i) \
+ regsrc.r##i##b = REGSRC_GP | REGIX_R##i; \
+ regty.r##i##b = REGF_8; \
+ regfmt.r##i##b = REGF_HEX; \
+ regsrc.r##i##w = REGSRC_GP | REGIX_R##i; \
+ regty.r##i##w = REGF_16; \
+ regfmt.r##i##w = REGF_HEX; \
+ regsrc.r##i##d = REGSRC_GP | REGIX_R##i; \
+ regty.r##i##d = REGF_32; \
+ regfmt.r##i##d = REGF_HEX; \
+ regsrc.r##i = REGSRC_GP | REGIX_R##i; \
+ regty.r##i = REGF_64; \
+ regfmt.r##i = REGF_HEX;
+ DOHI8(REGDEF_GPAMD64)
+#endif
+
+#define REGDEF_SEG(rn, RN) \
+ regsrc.rn = REGSRC_SEG | REGIX_##RN; \
+ regty.rn = REGF_16; \
+ regfmt.rn = REGF_HEX
+REGDEF_SEG(ss, SS)
+REGDEF_SEG(cs, CS)
+REGDEF_SEG(ds, DS)
+REGDEF_SEG(es, ES)
+REGDEF_SEG(fs, FS)
+REGDEF_SEG(gs, GS)
+
+#define REGDEF_STMMX(i) \
+ regsrc.st##i = REGSRC_STMMX | i; \
+ regty.st##i = REGF_80; \
+ regfmt.st##i = REGF_FLT; \
+ regsrc.mm##i = (6 << REGF_WDSHIFT) | REGSRC_STMMX | i; \
+ regty.mm##i = REGF_16; \
+ regfmt.mm##i = REGF_HEX;
+DO8(REGDEF_STMMX)
+
+#define REGDEF_SIMD(i) \
+ regsrc.xmm##i = (7 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
+ regty.xmm##i = REGF_32; \
+ regfmt.xmm##i = REGF_HEX; \
+ regsrc.ymm##i = (8 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
+ regty.ymm##i = REGF_32; \
+ regfmt.ymm##i = REGF_HEX;
+DO8(REGDEF_SIMD)
+#if CPUFAM_AMD64
+ DOHI8(REGDEF_SIMD)
+#endif
+
+ REGDUMP_GPSIZE = REGIX_GPLIM*WORDSZ + REGIX_SEGLIM*2
+
+# if CPUFAM_AMD64 && ABI_SYSV
+ REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ + 128
+# else
+ REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ
+# endif
+
+.macro _saveregs addr=nil
+ // Save the registers, leaving r/ebp pointing to the register map.
+
+ // Stash r/eax. This is bletcherous: hope we don't get a signal in
+ // the next few instructions.
+ mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], R_a(r)
+
+ .ifnes "\addr", "nil"
+ // Collect the effective address for the following dump, leaving it
+ // in the `addr' slot of the dump.
+ lea R_a(r), \addr
+ mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], R_a(r)
+ .endif
+
+ // Make space for the register save area. On AMD64 with System/V
+ // ABI, also skip the red zone. Use `lea' here to preserve the
+ // flags.
+ lea R_sp(r), [R_sp(r) - REGDUMP_SPADJ]
+
+ // Save flags and general-purpose registers. On 32-bit x86, we save
+ // ebx here and establish a GOT pointer here for the benefit of the
+ // PLT-indirect calls made later on.
+ pushf
+# if CPUFAM_X86
+ mov [esp + 4*REGIX_BX], ebx
+ ldgot
+# endif
+ callext regdump_gpsave
+
+ // Make space for the extended registers.
+ sub R_sp(r), R_c(r)
+ callext regdump_xtsave
+
+ // Prepare for calling back into C. On 32-bit x86, leave space for
+ // the arguments and set up the GOT pointer; on AMD64 Windows, leave
+ // the `shadow space' for the called-function's arguments. Also,
+ // forcibly align the stack pointer to a 16-byte boundary.
+# if CPUFAM_X86
+ sub esp, 16
+# elif ABI_WIN
+ sub rsp, 32
+# endif
+ and R_sp(r), ~15
+.endm
+
+.macro _rstrregs
+ // Restore registers.
+
+ // We assume r/ebp still points to the register map.
+ callext regdump_xtrstr
+ mov R_sp(r), R_bp(r)
+ callext regdump_gprstr
+ popf
+ lea R_sp(r), [R_sp(r) + REGDUMP_SPADJ]
+.endm
+
+.macro _regbase
+# if CPUFAM_X86
+ mov [esp + 0], ebp
+# elif ABI_SYSV
+ mov rdi, rbp
+# elif ABI_WIN
+ mov rcx, rbp
+# endif
+.endm
+
+.macro _membase
+ mov R_a(r), [R_bp(r) + regmap_gp]
+# if CPUFAM_X86
+ mov eax, [eax + REGIX_ADDR*WORDSZ]
+ mov [esp + 0], eax
+# elif ABI_SYSV
+ mov rdi, [rax + REGIX_ADDR*WORDSZ]
+# elif ABI_WIN
+ mov rcx, [rax + REGIX_ADDR*WORDSZ]
+# endif
+.endm
+
+.macro _reglbl msg
+ .ifeqs "\msg", ""
+# if CPUFAM_X86
+ mov dword ptr [esp + 4], 0
+# elif ABI_SYSV
+ xor esi, esi
+# elif ABI_WIN
+ xor edx, edx
+# endif
+ .else
+# if CPUFAM_X86
+ lea eax, [INTADDR(.L$_reglbl$\@)]
+ mov [esp + 4], eax
+# elif ABI_SYSV
+ lea rsi, [INTADDR(.L$_reglbl$\@)]
+# elif ABI_WIN
+ lea rdx, [INTADDR(.L$_reglbl$\@)]
+# endif
+ _LIT
+.L$_reglbl$\@:
+ .asciz "\msg"
+ _ENDLIT
+ .endif
+.endm
+
+.macro _regfmt arg
+# if CPUFAM_X86
+ mov dword ptr [esp + 8], \arg
+# elif ABI_SYSV
+ mov edx, \arg
+# elif ABI_WIN
+ mov r8d, \arg
+# endif
+.endm
+
+#endif
+
+#endif
+
+/*----- ARM32 -------------------------------------------------------------*/
+
+#if CPUFAM_ARMEL
+
+#if !__ASSEMBLER__
+extern unsigned regdump__flags;
+#endif
+#define REGF_VFP 1u
+#define REGF_D32 2u
+
+#define REGIX_CPSR 16
+#define REGIX_ADDR 17
+#define REGIX_GPLIM 18
+
+#define REGIX_FPSCR 255
+
+#if !__ASSEMBLER__
+
+union neon64 { SIMD_COMMON(64); };
+union neon128 { SIMD_COMMON(128); };
+
+struct gpsave { union gp32 r[REGIX_GPLIM]; };
+
+struct fpsave {
+ unsigned fpscr;
+ unsigned _pad0;
+ union {
+ float32 s[32];
+ union neon64 d[32];
+ union neon128 q[16];
+ } u;
+};
+
+struct regmap {
+ struct gpsave *gp;
+ struct fpsave *fp;
+};
+
+#else
+
+ .extern regdump_gpsave
+ .extern regdump_xtsave
+ .extern regdump_xtrstr
+ .extern regdump_gprstr
+
+ regmap_gp = 0
+ regmap_fp = 4
+ regmap_size = 8
+
+#define REGDEF_GP(i) \
+ regsrc.r##i = REGSRC_GP | i; \
+ regty.r##i = REGF_32; \
+ regfmt.r##i = REGF_HEX;
+DO16(REGDEF_GP)
+
+ regsrc.cpsr = REGSRC_GP | REGIX_CPSR
+ regty.cpsr = REGF_32
+ regfmt.cpsr = 0
+
+#define REGDEF_NEONS(i) \
+ regsrc.s##i = REGSRC_FP | i; \
+ regty.s##i = REGF_32; \
+ regfmt.s##i = REGF_FLT;
+DO32(REGDEF_NEONS)
+
+#define REGDEF_NEOND(i) \
+ regsrc.d##i = (6 << REGF_WDSHIFT) | REGSRC_FP | i; \
+ regty.d##i = REGF_32; \
+ regfmt.d##i = REGF_HEX;
+DO32(REGDEF_NEOND)
+
+#define REGDEF_NEONQ(i) \
+ regsrc.q##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
+ regty.q##i = REGF_32; \
+ regfmt.q##i = REGF_HEX;
+DO16(REGDEF_NEONQ)
+
+ regsrc.fpscr = REGSRC_FP | REGIX_FPSCR
+ regty.fpscr = REGF_32
+ regfmt.fpscr = 0
+
+ REGDUMP_GPSIZE = 4*REGIX_GPLIM
+ REGDUMP_FPSIZE_D16 = 8 + 16*8
+ REGDUMP_FPSIZE_D32 = 8 + 32*8
+
+.macro _saveregs base=nil, off=#0
+ // Save the registers, leaving r4 pointing to the register map.
+
+ // Stash r14. This is bletcherous: hope we don't get a signal in
+ // the next few instructions.
+ str r14, [r13, #-REGDUMP_GPSIZE + 14*4]
+
+ .ifnes "\base,\off", "nil,#0"
+ // Collect the effective address for the following dump, leaving it
+ // in the `addr' slot of the dump.
+ .ifeqs "\base", "nil"
+ adrl r14, \off
+ .else
+ add r14, \base, \off
+ .endif
+ str r14, [r13, #-REGDUMP_GPSIZE + 4*REGIX_ADDR]
+ .endif
+
+ // Make space for the register save area.
+ sub r13, r13, #REGDUMP_GPSIZE
+
+ // Save flags and general-purpose registers.
+ str r12, [r13, #4*12]
+ bl regdump_gpsave
+
+ // Make space for the extended registers.
+ sub r13, r13, r0
+ bl regdump_xtsave
+
+ // Prepare for calling back into C.
+ ldgot
+ mov r0, r13
+ bic r0, r0, #15
+ mov r13, r0
+.endm
+
+.macro _rstrregs
+ // Restore registers.
+
+ // We assume r4 still points to the register map.
+ bl regdump_xtrstr
+ mov r13, r4
+ bl regdump_gprstr
+ ldr r14, [r13, #14*4]
+ add r13, r13, #REGDUMP_GPSIZE
+.endm
+
+.macro _regbase
+ mov r0, r5
+.endm
+
+.macro _membase
+ mov r0, r6
+.endm
+
+.macro _reglbl msg
+ adrl r1, .L$_reglbl$\@
+ _LIT
+.L$_reglbl$\@:
+ .asciz "\msg"
+ .balign 4
+ _ENDLIT
+.endm
+
+.macro _regfmt arg
+ movw r2, #\arg&0xffff
+ movt r2, #(\arg >> 16)&0xffff
+.endm
+
+#endif
+
+#endif
+
+/*----- ARM64 -------------------------------------------------------------*/
+
+#if CPUFAM_ARM64
+
+#define REGIX_NZCV 32
+#define REGIX_PC 33
+#define REGIX_ADDR 34
+#define REGIX_GPLIM 36
+
+#define REGIX_FPFLAGS 255
+
+#if !__ASSEMBLER__
+
+union v128 { SIMD_COMMON(128); };
+
+struct gpsave { union gp64 r[REGIX_GPLIM]; };
+
+struct fpsave {
+ unsigned fpsr, fpcr;
+ union v128 v[32];
+};
+
+struct regmap {
+ struct gpsave *gp;
+ struct fpsave *fp;
+};
+
+#else
+
+ .extern regdump_gpsave
+ .extern regdump_xtsave
+ .extern regdump_xtrstr
+ .extern regdump_gprstr
+
+ regmap_gp = 0
+ regmap_fp = 8
+ regmap_size = 16
+
+#define REGDEF_GP(i) \
+ regsrc.x##i = REGSRC_GP | i; \
+ regty.x##i = REGF_64; \
+ regfmt.x##i = REGF_HEX; \
+ regsrc.w##i = REGSRC_GP | i; \
+ regty.w##i = REGF_32; \
+ regfmt.w##i = REGF_HEX;
+DO32(REGDEF_GP)
+
+ regsrc.sp = REGSRC_GP | 31
+ regty.sp = REGF_64
+ regfmt.sp = REGF_HEX
+
+ regsrc.pc = REGSRC_GP | REGIX_PC
+ regty.pc = REGF_64
+ regfmt.pc = REGF_HEX
+
+ regsrc.nzcv = REGSRC_GP | REGIX_NZCV
+ regty.nzcv = REGF_32
+ regfmt.nzcv = 0
+
+#define REGDEF_FP(i) \
+ regsrc.b##i = REGSRC_FP | i; \
+ regty.b##i = REGF_8; \
+ regfmt.b##i = REGF_HEX; \
+ regsrc.h##i = REGSRC_FP | i; \
+ regty.h##i = REGF_16; \
+ regfmt.h##i = REGF_HEX; \
+ regsrc.s##i = REGSRC_FP | i; \
+ regty.s##i = REGF_32; \
+ regfmt.s##i = REGF_FLT; \
+ regsrc.d##i = REGSRC_FP | i; \
+ regty.d##i = REGF_64; \
+ regfmt.d##i = REGF_FLT; \
+ regsrc.v##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
+ regty.v##i = REGF_32; \
+ regfmt.v##i = REGF_HEX;
+DO32(REGDEF_FP)
+
+ regsrc.fpflags = REGSRC_FP | REGIX_FPFLAGS
+ regty.fpflags = REGF_32
+ regfmt.fpflags = 0
+
+ REGDUMP_GPSIZE = 8*REGIX_GPLIM
+ REGDUMP_FPSIZE = 16 + 16 + 32*16
+
+.macro _saveregs base=nil, off=#0
+ // Save the registers, leaving x20 pointing to the register map.
+
+ // Stash x30. This is bletcherous: hope we don't get a signal in
+ // the next few instructions.
+ str x30, [sp, #-REGDUMP_GPSIZE + 30*8]
+
+ .ifnes "\base,\off", "nil,#0"
+ // Collect the effective address for the following dump, leaving it
+ // in the `addr' slot of the dump.
+ .ifeqs "\base", "nil"
+ adr x30, \off
+ .else
+ add x30, \base, \off
+ .endif
+ str x30, [sp, #-REGDUMP_GPSIZE + 8*REGIX_ADDR]
+ .endif
+
+ // Make space for the register save area.
+ sub sp, sp, #REGDUMP_GPSIZE
+
+ // Save flags and general-purpose registers.
+ stp x16, x17, [sp, #8*16]
+ bl regdump_gpsave
+
+ // Make space for the extended registers.
+ sub sp, sp, x0
+ bl regdump_xtsave
+.endm
+
+.macro _rstrregs
+ // Restore registers.
+
+ // We assume x21 still points to the register map.
+ bl regdump_xtrstr
+ mov sp, x20
+ bl regdump_gprstr
+ ldr x30, [sp, #30*8]
+ add sp, sp, #REGDUMP_GPSIZE
+.endm
+
+.macro _regbase
+ mov x0, x21
+.endm
+
+.macro _membase
+ mov x0, x22
+.endm
+
+.macro _reglbl msg
+ adr x1, .L$_reglbl$\@
+ _LIT
+.L$_reglbl$\@:
+ .asciz "\msg"
+ .balign 4
+ _ENDLIT
+.endm
+
+.macro _regfmt arg
+ movz w2, #\arg&0xffff
+ movk w2, #(\arg >> 16)&0xffff, lsl #16
+.endm
+
+#endif
+
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @regdump_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Performs one-time initialization for register dumping. In
+ * particular, this performs CPU feature detection on platforms
+ * where that is a difficult task: without it, registers
+ * corresponding to optional architectural features can be
+ * neither printed nor preserved by the register-dump machinery.
+ */
+
+#if !__ASSEMBLER__
+extern void regdump_init(void);
+#endif
+
+/* --- @regdump@ --- *
+ *
+ * Arguments: @const void *base@ = pointer to base structure, corresponding
+ * to the @REGF_SRCMASK@ part of @f@
+ * @const char *lbl@ = label to print
+ * @uint32 f@ = format control word; see @REGF_...@
+ *
+ * Returns: ---
+ *
+ * Use: Dump a register value, or chunk of memory.
+ *
+ * This function is not usually called directly; instead, use
+ * the `reg' or `mem' assembler macros.
+ */
+
+#if !__ASSEMBLER__
+extern void regdump(const void *base, const char *lbl, uint32 f);
+#else
+ .extern regdump
+#endif
+
+/* --- @regdump_gp@, @regdump_fp@, @regdump_simd@ --- *
+ *
+ * Arguments: @const struct regmap *map@ = pointer to register map
+ *
+ * Returns: ---
+ *
+ * Use: Dump the general-purpose/floating-point/SIMD registers.
+ *
+ * This function is not usually called directly; instead, use
+ * the `regdump' assembler macro.
+ */
+
+#if !__ASSEMBLER__
+extern void regdump_gp(const struct regmap */*map*/);
+extern void regdump_fp(const struct regmap */*map*/);
+extern void regdump_simd(const struct regmap */*map*/);
+#else
+ .extern regdump_gp
+ .extern regdump_fp
+ .extern regdump_simd
+#endif
+
+/* --- @regdump_freshline@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Begin a fresh line of output.
+ */
+
+#if !__ASSEMBLER__
+extern void regdump_freshline(void);
+#else
+ .extern regdump_freshline
+#endif
+
+/*----- Main user interface macros ----------------------------------------*/
+
+#if __ASSEMBLER__
+
+.macro terpri
+ _saveregs
+ callext regdump_freshline
+ _rstrregs
+.endm
+
+.macro reg lbl, rn, fmt=0
+ _saveregs
+ _regbase
+ _reglbl "\lbl"
+ .L$reg.fmt$\@ = regsrc.\rn | \fmt | \
+ (((\fmt®F_TYMASK) == 0)®ty.\rn) | \
+ (((\fmt®F_FMTMASK) == 0)®fmt.\rn)
+ _regfmt .L$reg.fmt$\@
+ callext regdump
+ _rstrregs
+.endm
+
+.macro mem lbl, addr, fmt=0
+ _saveregs \addr
+ _membase
+ _reglbl "\lbl"
+ .L$mem.fmt$\@ = REGSRC_ABS | \fmt | \
+ (((\fmt®F_TYMASK) == 0)®F_32) | \
+ (((\fmt®F_FMTMASK) == 0)®F_HEX)
+ _regfmt .L$mem.fmt$\@
+ callext regdump
+ _rstrregs
+.endm
+
+.macro regdump gp=nil, fp=nil, simd=nil
+ _saveregs
+ .ifnes "\gp", "nil"
+ _regbase
+ callext regdump_gp
+ .endif
+ .ifnes "\fp", "nil"
+ _regbase
+ callext regdump_fp
+ .endif
+ .ifnes "\simd", "nil"
+ _regbase
+ callext regdump_simd
+ .endif
+ _rstrregs
+.endm
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif