[PATCH v4 3/3] Add support for recording xsavec x86 instruction

Pierre Marsais
2018-10-06T00:16:24Z

This patch add support of the xsavec instruction. The logic is highly
similar to the logic of xsave. The "init optimization" is not handled in
this patch.

The values of X86_XSAVEC_* were obtained by testing for AVX and MPX, by
reading Intel x86 manuals, and looking at
arch/x86/include/asm/fpu/types.h in Linux source.

gdb/ChangeLog:
2018-10-05 Pierre Marsais <pierre.marsais@lse.epita.fr>

* common/x86-xstate.h (X86_XSAVEC_AVX_SIZE,
X86_XSAVEC_BNDREGS_SIZE, X86_XSAVEC_BNDCFG_SIZE,
X86_XSAVEC_K_SIZE, X86_XSAVEC_ZMM_H_SIZE, X86_XSAVEC_ZMM_SIZE,
X86_XSAVEC_PKRU_SIZE, X86_XSAVEC_SIZE(XCR0)): New macros.
* i386-tdep.c: (i386_process_record): Handle xsavec instruction.

gdb/testsuite/ChangeLog:
2018-10-05 Pierre Marsais <pierre.marsais@lse.epita.fr>

* gdb.reverse/i386-xsave-reverse.c (xsavec_test): New function.
(main): Use xsavec_test.
* gdb.reverse/i386-xsave-reverse.exp: Test for xsavec
instruction.
---
gdb/common/x86-xstate.h | 17 +++++++
gdb/i386-tdep.c | 44 +++++++++++++++++++
.../gdb.reverse/i386-xsave-reverse.c | 9 ++++
.../gdb.reverse/i386-xsave-reverse.exp | 33 ++++++++++++--
4 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/gdb/common/x86-xstate.h b/gdb/common/x86-xstate.h
index 51e5c3c785..8a5b167610 100644
--- a/gdb/common/x86-xstate.h
+++ b/gdb/common/x86-xstate.h
@@ -72,6 +72,23 @@
(HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \
(HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE))))

+#define X86_XSAVEC_AVX_SIZE 256
+#define X86_XSAVEC_BNDREGS_SIZE 64
+#define X86_XSAVEC_BNDCFG_SIZE 64
+#define X86_XSAVEC_K_SIZE 64
+#define X86_XSAVEC_ZMM_H_SIZE 512
+#define X86_XSAVEC_ZMM_SIZE 1024
+#define X86_XSAVEC_PKRU_SIZE 8
+
+#define X86_XSAVEC_SIZE(XCR0) \
+ (((XCR0) & X86_XSTATE_AVX ? X86_XSAVEC_AVX_SIZE : 0 ) + \
+ ((XCR0) & X86_XSTATE_BNDREGS ? X86_XSAVEC_BNDREGS_SIZE : 0) + \
+ ((XCR0) & X86_XSTATE_BNDCFG ? X86_XSAVEC_BNDCFG_SIZE : 0) + \
+ ((XCR0) & X86_XSTATE_K ? X86_XSAVEC_K_SIZE : 0) + \
+ ((XCR0) & X86_XSTATE_ZMM_H ? X86_XSAVEC_ZMM_H_SIZE : 0) + \
+ ((XCR0) & X86_XSTATE_ZMM ? X86_XSAVEC_ZMM_SIZE : 0) + \
+ ((XCR0) & X86_XSTATE_PKRU ? X86_XSAVEC_PKRU_SIZE : 0))
+
/* Initial value for fctrl register, as defined in the X86 manual, and
confirmed in the (Linux) kernel source. When the x87 floating point
feature is not enabled in an inferior we use this as the value of the
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 90c78e0bbc..c97a4978aa 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -5491,6 +5491,50 @@ i386_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
return -1;
I386_RECORD_FULL_ARCH_LIST_ADD_REG (X86_RECORD_EFLAGS_REGNUM);
break;
+ case 4: /* xsavec */
+ {
+ if (ir.mod == 3) {
+ opcode = (opcode << 8) | ir.modrm;
+ goto no_support;
+ }
+
+ ULONGEST rfbm_eax, rfbm_edx;
+
+ regcache_raw_read_unsigned (regcache, I386_EAX_REGNUM, &rfbm_eax);
+ rfbm_eax &= (1ULL << 32) - 1;
+
+ regcache_raw_read_unsigned (regcache, I386_EDX_REGNUM, &rfbm_edx);
+ rfbm_edx &= (1ULL << 32) - 1;
+
+ uint64_t rfbm = tdep->xcr0 & (rfbm_eax | (rfbm_edx << 32));
+
+ if (rfbm & ~X86_XSTATE_ALL_MASK) {
+ printf_unfiltered (_("Process record does not support XSAVEC "
+ "instruction with RFBM=%" PRIx64 ".\n"),
+ rfbm);
+ opcode = (opcode << 8) | ir.modrm;
+ goto no_support;
+ }
+
+ uint64_t tmpu64;
+ if (i386_record_lea_modrm_addr (&ir, &tmpu64))
+ return -1;
+
+ uint64_t legacy_size = 160; /* x87 xsave size */
+ if (rfbm & X86_XSTATE_SSE) {
+ if (ir.regmap[X86_RECORD_R8_REGNUM])
+ legacy_size = 416; /* 64-bit sse xsave size */
+ else
+ legacy_size = 288; /* 32-bit sse xsave size */
+ }
+ if (record_full_arch_list_add_mem (tmpu64, legacy_size))
+ return -1;
+
+ uint64_t size = X86_XSAVEC_SIZE (rfbm);
+ if (record_full_arch_list_add_mem (tmpu64 + 512, size))
+ return -1;
+ }
+ break;
case 6: /* rdrand */
case 7: /* rdseed */
if (ir.mod != 3)
diff --git a/gdb/testsuite/gdb.reverse/i386-xsave-reverse.c b/gdb/testsuite/gdb.reverse/i386-xsave-reverse.c
index 18453405a6..965ca60133 100644
--- a/gdb/testsuite/gdb.reverse/i386-xsave-reverse.c
+++ b/gdb/testsuite/gdb.reverse/i386-xsave-reverse.c
@@ -44,10 +44,19 @@ void xsave_no_sse_test (void) {
asm ("xsave %0":"=m"(buf): "a"((1 << 1) ^ ~0L), "d"(~0L));
} /* end xsave_no_sse_test */

+void xsavec_test (void) {
+ uint32_t xsave_buf_sze = get_xsave_buffer_size ();
+ char buf[xsave_buf_sze] __attribute__ ((aligned (64)));
+ memset (buf, 0, xsave_buf_sze);
+
+ asm ("xsavec %0":"=m"(buf) : "a"(~0L), "d"(~0L));
+} /* end xsavec_test */
+
int
main ()
{
xsave_test ();
xsave_no_sse_test ();
+ xsavec_test ();
return 0; /* end of main */
}
diff --git a/gdb/testsuite/gdb.reverse/i386-xsave-reverse.exp b/gdb/testsuite/gdb.reverse/i386-xsave-reverse.exp
index f3c0f93bfc..d8fa64cdae 100644
--- a/gdb/testsuite/gdb.reverse/i386-xsave-reverse.exp
+++ b/gdb/testsuite/gdb.reverse/i386-xsave-reverse.exp
@@ -38,9 +38,10 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
return -1
}

-set end_of_main [gdb_get_line_number " end of main "]
-set end_xsave_test [gdb_get_line_number " end xsave_test "]
-set end_xsave_no_sse_test [gdb_get_line_number " end xsave_no_sse_test "]
+set end_of_main [gdb_get_line_number " end of main "]
+set end_xsave_test [gdb_get_line_number " end xsave_test "]
+set end_xsave_no_sse_test [gdb_get_line_number " end xsave_no_sse_test "]
+set end_xsavec_test [gdb_get_line_number " end xsavec_test "]

if ![runto main] then {
fail "run to main"
@@ -100,3 +101,29 @@ gdb_test "reverse-step" "xsave.*" "reverse-step to xsave"

gdb_test "print buf" ".* = '\\\\000' <repeats .* times>" \
"verify xsave buffer after reverse xsave (no sse)"
+
+#xsavec_test
+
+gdb_test "continue" \
+ " end xsave_no_sse_test .*" \
+ "continue to end of xsave_no_sse_test"
+
+gdb_test "break $end_xsavec_test" \
+ "Breakpoint $decimal at .* line $end_xsavec_test\." \
+ "set breakpoint at end of xsavec_test"
+
+set test "continue to end of xsavec_test"
+gdb_test_multiple "continue" $test {
+ -re " end xsavec_test .*\r\n$gdb_prompt $" {
+ pass $test
+ }
+ -re " Illegal instruction.*\r\n$gdb_prompt $" {
+ untested i386-xsavec-no-sse-reverse
+ return -1
+ }
+}
+
+gdb_test "reverse-step" "xsavec.*" "reverse-step to xsavec"
+
+gdb_test "print buf" ".* = '\\\\000' <repeats .* times>" \
+ "verify xsave buffer after reverse xsavec"
--
2.19.0