gdb/x86: Handle kernels using compact xsave format
authorAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 3 May 2018 16:46:14 +0000 (17:46 +0100)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Tue, 8 May 2018 17:03:46 +0000 (18:03 +0100)
For GNU/Linux on x86-64, if the target is using the xsave format for
passing the floating-point information from the inferior then there
currently exists a bug relating to the x87 control registers, and the
mxcsr register.

The xsave format allows different floating-point features to be lazily
enabled, a bit in the xsave format tells GDB which floating-point
features have been enabled, and which have not.

Currently in GDB, when reading the floating point state, we check the
xsave bit flags, if the feature is enabled then we read the feature
from the xsave buffer, and if the feature is not enabled, then we
supply the default value from within GDB.

Within GDB, when writing the floating point state, we first fetch the
xsave state from the target and then, for any feature that is not yet
enabled, we write the default values into the xsave buffer.  Next we
compare the regcache value with the value in the xsave buffer, and, if
the value has changed we update the value in the xsave buffer, and
mark the feature enabled in the xsave bit flags.

The problem then, is that the x87 control registers were not following
this pattern.  We assumed that these registers were always written out
by the kernel, and we always wrote them out to the xsave buffer (but
didn't enabled the feature).  The result of this is that if the kernel
had not yet enabled the x87 feature then within GDB we would see
random values for the x87 floating point control registers, and if the
user tried to modify one of these register, that modification would be
lost.

Finally, the mxcsr register was also broken in the same way as the x87
control registers.  The added complexity with this case is that the
mxcsr register is part of both the avx and sse floating point feature
set.  When reading or writing this register we need to check that at
least one of these features is enabled.

This bug was present in native GDB, and within gdbserver.  Both are
fixed with this commit.

gdb/ChangeLog:

* common/x86-xstate.h (I387_FCTRL_INIT_VAL): New constant.
(I387_MXCSR_INIT_VAL): New constant.
* amd64-tdep.c (amd64_supply_xsave): Only read state from xsave
buffer if it was supplied by the inferior.
* i387-tdep.c (i387_supply_fsave): Use I387_MXCSR_INIT_VAL.
(i387_xsave_get_clear_bv): New function.
(i387_supply_xsave): Only read x87 control registers from the
xsave buffer if the feature is enabled, and the state will have
been written, otherwise, provide a suitable default.
(i387_collect_xsave): Pre-clear all registers in xsave buffer,
including x87 control registers.  Update control registers if they
have changed from the default value, and mark features as enabled
as required.
* i387-tdep.h (i387_xsave_get_clear_bv): Declare.

gdb/gdbserver/ChangeLog:

* i387-fp.c (i387_cache_to_xsave): Only write x87 control
registers to the cache if their values have changed.
(i387_xsave_to_cache): Provide default values for x87 control
registers when these features are available, but disabled.
* regcache.c (supply_register_by_name_zeroed): New function.
* regcache.h (supply_register_by_name_zeroed): Declare new
function.

gdb/testsuite/ChangeLog:

* gdb.arch/amd64-init-x87-values.S: New file.
* gdb.arch/amd64-init-x87-values.exp: New file.

12 files changed:
gdb/ChangeLog
gdb/amd64-tdep.c
gdb/common/x86-xstate.h
gdb/gdbserver/ChangeLog
gdb/gdbserver/i387-fp.c
gdb/gdbserver/regcache.c
gdb/gdbserver/regcache.h
gdb/i387-tdep.c
gdb/i387-tdep.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/amd64-init-x87-values.S [new file with mode: 0644]
gdb/testsuite/gdb.arch/amd64-init-x87-values.exp [new file with mode: 0644]

index 32038c594713bec296c43ffe935f2f4f3561f27d..8314945f1c7358579bf96741e90f5393cb71ee8d 100644 (file)
@@ -1,3 +1,20 @@
+2018-05-08  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * common/x86-xstate.h (I387_FCTRL_INIT_VAL): New constant.
+       (I387_MXCSR_INIT_VAL): New constant.
+       * amd64-tdep.c (amd64_supply_xsave): Only read state from xsave
+       buffer if it was supplied by the inferior.
+       * i387-tdep.c (i387_supply_fsave): Use I387_MXCSR_INIT_VAL.
+       (i387_xsave_get_clear_bv): New function.
+       (i387_supply_xsave): Only read x87 control registers from the
+       xsave buffer if the feature is enabled, and the state will have
+       been written, otherwise, provide a suitable default.
+       (i387_collect_xsave): Pre-clear all registers in xsave buffer,
+       including x87 control registers.  Update control registers if they
+       have changed from the default value, and mark features as enabled
+       as required.
+       * i387-tdep.h (i387_xsave_get_clear_bv): Declare.
+
 2018-05-08  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * spu-tdep.c (info_spu_event_command): Fix output formatting.
index 1fea26409ae93678784a13f2fb09909eea5c7404..d555465c2f9646fd829652741c44ce917d8b602b 100644 (file)
@@ -3372,13 +3372,23 @@ amd64_supply_xsave (struct regcache *regcache, int regnum,
       && gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 64)
     {
       const gdb_byte *regs = (const gdb_byte *) xsave;
+      static const gdb_byte zero[I386_MAX_REGISTER_SIZE] = { 0 };
+      ULONGEST clear_bv;
 
-      if (regnum == -1 || regnum == I387_FISEG_REGNUM (tdep))
-       regcache_raw_supply (regcache, I387_FISEG_REGNUM (tdep),
-                            regs + 12);
-      if (regnum == -1 || regnum == I387_FOSEG_REGNUM (tdep))
-       regcache_raw_supply (regcache, I387_FOSEG_REGNUM (tdep),
-                            regs + 20);
+      clear_bv = i387_xsave_get_clear_bv (gdbarch, xsave);
+
+      /* If the FISEG and FOSEG registers have not been initialised yet
+        (their CLEAR_BV bit is set) then their default values of zero will
+        have already been setup by I387_SUPPLY_XSAVE.  */
+      if (!(clear_bv & X86_XSTATE_X87))
+       {
+         if (regnum == -1 || regnum == I387_FISEG_REGNUM (tdep))
+           regcache_raw_supply (regcache, I387_FISEG_REGNUM (tdep),
+                                regs + 12);
+         if (regnum == -1 || regnum == I387_FOSEG_REGNUM (tdep))
+           regcache_raw_supply (regcache, I387_FOSEG_REGNUM (tdep),
+                                regs + 20);
+       }
     }
 }
 
index 5a165b3f6cb106b3c6d69cfe6da8b1cb7e37bbc5..51e5c3c785a912a91c62b590ad45f3db028d49c0 100644 (file)
       (HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \
        (HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE))))
 
+/* 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
+   fcrtl register.  */
+
+#define I387_FCTRL_INIT_VAL 0x037f
+
+/* Initial value for mxcsr register.  When the avx and sse floating point
+   features are not enabled in an inferior we use this as the value of the
+   mxcsr register.  */
+
+#define I387_MXCSR_INIT_VAL 0x1f80
+
 #endif /* X86_XSTATE_H */
index 74613598a282a97dbc74e65fe09dcae19ee86385..9cd72a89f719226569b63a4d1081f94de1faae31 100644 (file)
@@ -1,3 +1,13 @@
+2018-05-08  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * i387-fp.c (i387_cache_to_xsave): Only write x87 control
+       registers to the cache if their values have changed.
+       (i387_xsave_to_cache): Provide default values for x87 control
+       registers when these features are available, but disabled.
+       * regcache.c (supply_register_by_name_zeroed): New function.
+       * regcache.h (supply_register_by_name_zeroed): Declare new
+       function.
+
 2018-05-07  Tom Tromey  <tom@tromey.com>
 
        * configure: Rebuild.
index f1f7d844537dd6e01cda9d0f143ac89e00be96b1..bee1af9d8c6a8683ae689f8cc481ed5ba146e74b 100644 (file)
@@ -294,17 +294,31 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
   if (clear_bv)
     {
       if ((clear_bv & X86_XSTATE_X87))
-       for (i = 0; i < 8; i++)
-         memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+       {
+         for (i = 0; i < 8; i++)
+           memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+
+         fp->fioff = 0;
+         fp->fooff = 0;
+         fp->fctrl = I387_FCTRL_INIT_VAL;
+         fp->fstat = 0;
+         fp->ftag = 0;
+         fp->fiseg = 0;
+         fp->foseg = 0;
+         fp->fop = 0;
+       }
 
       if ((clear_bv & X86_XSTATE_SSE))
-       for (i = 0; i < num_xmm_registers; i++) 
+       for (i = 0; i < num_xmm_registers; i++)
          memset (((char *) &fp->xmm_space[0]) + i * 16, 0, 16);
 
       if ((clear_bv & X86_XSTATE_AVX))
-       for (i = 0; i < num_xmm_registers; i++) 
+       for (i = 0; i < num_xmm_registers; i++)
          memset (((char *) &fp->ymmh_space[0]) + i * 16, 0, 16);
 
+      if ((clear_bv & X86_XSTATE_SSE) && (clear_bv & X86_XSTATE_AVX))
+       memset (((char *) &fp->mxcsr), 0, 4);
+
       if ((clear_bv & X86_XSTATE_BNDREGS))
        for (i = 0; i < num_mpx_bnd_registers; i++)
          memset (((char *) &fp->mpx_bnd_space[0]) + i * 16, 0, 16);
@@ -523,43 +537,93 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
        }
     }
 
-  /* Update the corresponding bits in xstate_bv if any SSE/AVX
-     registers are changed.  */
-  fp->xstate_bv |= xstate_bv;
+  if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
+    {
+      collect_register_by_name (regcache, "mxcsr", raw);
+      if (memcmp (raw, &fp->mxcsr, 4) != 0)
+       {
+         if (((fp->xstate_bv | xstate_bv)
+              & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
+           xstate_bv |= X86_XSTATE_SSE;
+         memcpy (&fp->mxcsr, raw, 4);
+       }
+    }
 
-  collect_register_by_name (regcache, "fioff", &fp->fioff);
-  collect_register_by_name (regcache, "fooff", &fp->fooff);
-  collect_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+  if (x86_xcr0 & X86_XSTATE_X87)
+    {
+      collect_register_by_name (regcache, "fioff", raw);
+      if (memcmp (raw, &fp->fioff, 4) != 0)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         memcpy (&fp->fioff, raw, 4);
+       }
 
-  /* This one's 11 bits... */
-  collect_register_by_name (regcache, "fop", &val2);
-  fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
+      collect_register_by_name (regcache, "fooff", raw);
+      if (memcmp (raw, &fp->fooff, 4) != 0)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         memcpy (&fp->fooff, raw, 4);
+       }
 
-  /* Some registers are 16-bit.  */
-  collect_register_by_name (regcache, "fctrl", &val);
-  fp->fctrl = val;
+      /* This one's 11 bits... */
+      collect_register_by_name (regcache, "fop", &val2);
+      val2 = (val2 & 0x7FF) | (fp->fop & 0xF800);
+      if (fp->fop != val2)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->fop = val2;
+       }
 
-  collect_register_by_name (regcache, "fstat", &val);
-  fp->fstat = val;
+      /* Some registers are 16-bit.  */
+      collect_register_by_name (regcache, "fctrl", &val);
+      if (fp->fctrl != val)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->fctrl = val;
+       }
 
-  /* Convert to the simplifed tag form stored in fxsave data.  */
-  collect_register_by_name (regcache, "ftag", &val);
-  val &= 0xFFFF;
-  val2 = 0;
-  for (i = 7; i >= 0; i--)
-    {
-      int tag = (val >> (i * 2)) & 3;
+      collect_register_by_name (regcache, "fstat", &val);
+      if (fp->fstat != val)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->fstat = val;
+       }
 
-      if (tag != 3)
-       val2 |= (1 << i);
-    }
-  fp->ftag = val2;
+      /* Convert to the simplifed tag form stored in fxsave data.  */
+      collect_register_by_name (regcache, "ftag", &val);
+      val &= 0xFFFF;
+      val2 = 0;
+      for (i = 7; i >= 0; i--)
+       {
+         int tag = (val >> (i * 2)) & 3;
 
-  collect_register_by_name (regcache, "fiseg", &val);
-  fp->fiseg = val;
+         if (tag != 3)
+           val2 |= (1 << i);
+       }
+      if (fp->ftag != val2)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->ftag = val2;
+       }
 
-  collect_register_by_name (regcache, "foseg", &val);
-  fp->foseg = val;
+      collect_register_by_name (regcache, "fiseg", &val);
+      if (fp->fiseg != val)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->fiseg = val;
+       }
+
+      collect_register_by_name (regcache, "foseg", &val);
+      if (fp->foseg != val)
+       {
+         xstate_bv |= X86_XSTATE_X87;
+         fp->foseg = val;
+       }
+    }
+
+  /* Update the corresponding bits in xstate_bv if any SSE/AVX
+     registers are changed.  */
+  fp->xstate_bv |= xstate_bv;
 }
 
 static int
@@ -844,39 +908,67 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
        }
     }
 
-  supply_register_by_name (regcache, "fioff", &fp->fioff);
-  supply_register_by_name (regcache, "fooff", &fp->fooff);
-  supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+  if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
+      == (X86_XSTATE_SSE | X86_XSTATE_AVX))
+    {
+      unsigned int default_mxcsr = I387_MXCSR_INIT_VAL;
+      supply_register_by_name (regcache, "mxcsr", &default_mxcsr);
+    }
+  else
+    supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
 
-  /* Some registers are 16-bit.  */
-  val = fp->fctrl & 0xFFFF;
-  supply_register_by_name (regcache, "fctrl", &val);
+  if ((clear_bv & X86_XSTATE_X87) != 0)
+    {
+      supply_register_by_name_zeroed (regcache, "fioff");
+      supply_register_by_name_zeroed (regcache, "fooff");
 
-  val = fp->fstat & 0xFFFF;
-  supply_register_by_name (regcache, "fstat", &val);
+      val = I387_FCTRL_INIT_VAL;
+      supply_register_by_name (regcache, "fctrl", &val);
 
-  /* Generate the form of ftag data that GDB expects.  */
-  top = (fp->fstat >> 11) & 0x7;
-  val = 0;
-  for (i = 7; i >= 0; i--)
-    {
-      int tag;
-      if (fp->ftag & (1 << i))
-       tag = i387_ftag (fxp, (i + 8 - top) % 8);
-      else
-       tag = 3;
-      val |= tag << (2 * i);
+      supply_register_by_name_zeroed (regcache, "fstat");
+
+      val = 0xFFFF;
+      supply_register_by_name (regcache, "ftag", &val);
+
+      supply_register_by_name_zeroed (regcache, "fiseg");
+      supply_register_by_name_zeroed (regcache, "foseg");
+      supply_register_by_name_zeroed (regcache, "fop");
     }
-  supply_register_by_name (regcache, "ftag", &val);
+  else
+    {
+      supply_register_by_name (regcache, "fioff", &fp->fioff);
+      supply_register_by_name (regcache, "fooff", &fp->fooff);
 
-  val = fp->fiseg & 0xFFFF;
-  supply_register_by_name (regcache, "fiseg", &val);
+      /* Some registers are 16-bit.  */
+      val = fp->fctrl & 0xFFFF;
+      supply_register_by_name (regcache, "fctrl", &val);
 
-  val = fp->foseg & 0xFFFF;
-  supply_register_by_name (regcache, "foseg", &val);
+      val = fp->fstat & 0xFFFF;
+      supply_register_by_name (regcache, "fstat", &val);
 
-  val = (fp->fop) & 0x7FF;
-  supply_register_by_name (regcache, "fop", &val);
+      /* Generate the form of ftag data that GDB expects.  */
+      top = (fp->fstat >> 11) & 0x7;
+      val = 0;
+      for (i = 7; i >= 0; i--)
+       {
+         int tag;
+         if (fp->ftag & (1 << i))
+           tag = i387_ftag (fxp, (i + 8 - top) % 8);
+         else
+           tag = 3;
+         val |= tag << (2 * i);
+       }
+      supply_register_by_name (regcache, "ftag", &val);
+
+      val = fp->fiseg & 0xFFFF;
+      supply_register_by_name (regcache, "fiseg", &val);
+
+      val = fp->foseg & 0xFFFF;
+      supply_register_by_name (regcache, "foseg", &val);
+
+      val = (fp->fop) & 0x7FF;
+      supply_register_by_name (regcache, "fop", &val);
+    }
 }
 
 /* Default to SSE.  */
index cbdf766df2c2b95f605198c617ffead9f9ac3775..0718b9f9a04d58577f71ab14887e18dc05fee018 100644 (file)
@@ -346,6 +346,19 @@ supply_register_zeroed (struct regcache *regcache, int n)
 #endif
 }
 
+#ifndef IN_PROCESS_AGENT
+
+/* Supply register called NAME with value zero to REGCACHE.  */
+
+void
+supply_register_by_name_zeroed (struct regcache *regcache,
+                               const char *name)
+{
+  supply_register_zeroed (regcache, find_regno (regcache->tdesc, name));
+}
+
+#endif
+
 /* Supply the whole register set whose contents are stored in BUF, to
    REGCACHE.  If BUF is NULL, all the registers' values are recorded
    as unavailable.  */
index 6ff13084b0f168482c7f23e0ea3e348bfac8e687..2c0df648f6d439517b56844d00b1bf0a1c9eafd1 100644 (file)
@@ -107,6 +107,9 @@ void supply_register_zeroed (struct regcache *regcache, int n);
 void supply_register_by_name (struct regcache *regcache,
                              const char *name, const void *buf);
 
+void supply_register_by_name_zeroed (struct regcache *regcache,
+                                    const char *name);
+
 void supply_regblock (struct regcache *regcache, const void *buf);
 
 void collect_register (struct regcache *regcache, int n, void *buf);
index 19387929c59671a90a8d3e6fedd90b16c9ebb5f7..aca70c186f930fd53dfea1f1272ab33d74d80d27 100644 (file)
@@ -479,7 +479,7 @@ i387_supply_fsave (struct regcache *regcache, int regnum, const void *fsave)
     {
       gdb_byte buf[4];
 
-      store_unsigned_integer (buf, 4, byte_order, 0x1f80);
+      store_unsigned_integer (buf, 4, byte_order, I387_MXCSR_INIT_VAL);
       regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep), buf);
     }
 }
@@ -892,6 +892,27 @@ static int xsave_pkeys_offset[] =
 #define XSAVE_PKEYS_ADDR(tdep, xsave, regnum) \
   (xsave + xsave_pkeys_offset[regnum - I387_PKRU_REGNUM (tdep)])
 
+
+/* Extract from XSAVE a bitset of the features that are available on the
+   target, but which have not yet been enabled.  */
+
+ULONGEST
+i387_xsave_get_clear_bv (struct gdbarch *gdbarch, const void *xsave)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  const gdb_byte *regs = (const gdb_byte *) xsave;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  /* Get `xstat_bv'.  The supported bits in `xstat_bv' are 8 bytes.  */
+  ULONGEST xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+                                                8, byte_order);
+
+  /* Clear part in vector registers if its bit in xstat_bv is zero.  */
+  ULONGEST clear_bv = (~(xstate_bv)) & tdep->xcr0;
+
+  return clear_bv;
+}
+
 /* Similar to i387_supply_fxsave, but use XSAVE extended state.  */
 
 void
@@ -899,6 +920,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
                   const void *xsave)
 {
   struct gdbarch *gdbarch = regcache->arch ();
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   const gdb_byte *regs = (const gdb_byte *) xsave;
   int i;
@@ -956,20 +978,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
   else
     regclass = none;
 
-  if (regclass != none)
-    {
-      /* Get `xstat_bv'.  The supported bits in `xstat_bv' are 8 bytes.  */
-      enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      ULONGEST xstate_bv = 0;
-
-      xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
-                                           8, byte_order);
-
-      /* Clear part in vector registers if its bit in xstat_bv is zero.  */
-      clear_bv = (~(xstate_bv)) & tdep->xcr0;
-    }
-  else
-    clear_bv = X86_XSTATE_ALL_MASK;
+  clear_bv = i387_xsave_get_clear_bv (gdbarch, xsave);
 
   /* With the delayed xsave mechanism, in between the program
      starting, and the program accessing the vector registers for the
@@ -1246,10 +1255,30 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
   for (i = I387_FCTRL_REGNUM (tdep); i < I387_XMM0_REGNUM (tdep); i++)
     if (regnum == -1 || regnum == i)
       {
+       if (clear_bv & X86_XSTATE_X87)
+         {
+           if (i == I387_FCTRL_REGNUM (tdep))
+             {
+               gdb_byte buf[4];
+
+               store_unsigned_integer (buf, 4, byte_order,
+                                       I387_FCTRL_INIT_VAL);
+               regcache_raw_supply (regcache, i, buf);
+             }
+           else if (i == I387_FTAG_REGNUM (tdep))
+             {
+               gdb_byte buf[4];
+
+               store_unsigned_integer (buf, 4, byte_order, 0xffff);
+               regcache_raw_supply (regcache, i, buf);
+             }
+           else
+             regcache_raw_supply (regcache, i, zero);
+         }
        /* Most of the FPU control registers occupy only 16 bits in
           the xsave extended state.  Give those a special treatment.  */
-       if (i != I387_FIOFF_REGNUM (tdep)
-           && i != I387_FOOFF_REGNUM (tdep))
+       else if (i != I387_FIOFF_REGNUM (tdep)
+                && i != I387_FOOFF_REGNUM (tdep))
          {
            gdb_byte val[4];
 
@@ -1257,7 +1286,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
            val[2] = val[3] = 0;
            if (i == I387_FOP_REGNUM (tdep))
              val[1] &= ((1 << 3) - 1);
-           else if (i== I387_FTAG_REGNUM (tdep))
+           else if (i == I387_FTAG_REGNUM (tdep))
              {
                /* The fxsave area contains a simplified version of
                   the tag word.  We have to look at the actual 80-bit
@@ -1291,13 +1320,26 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
              }
            regcache_raw_supply (regcache, i, val);
          }
-       else 
+       else
          regcache_raw_supply (regcache, i, FXSAVE_ADDR (tdep, regs, i));
       }
 
   if (regnum == I387_MXCSR_REGNUM (tdep) || regnum == -1)
-    regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep),
-                        FXSAVE_MXCSR_ADDR (regs));
+    {
+      /* The MXCSR register is placed into the xsave buffer if either the
+        AVX or SSE features are enabled.  */
+      if ((clear_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE))
+         == (X86_XSTATE_AVX | X86_XSTATE_SSE))
+       {
+         gdb_byte buf[4];
+
+         store_unsigned_integer (buf, 4, byte_order, I387_MXCSR_INIT_VAL);
+         regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep), buf);
+       }
+      else
+       regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep),
+                            FXSAVE_MXCSR_ADDR (regs));
+    }
 }
 
 /* Similar to i387_collect_fxsave, but use XSAVE extended state.  */
@@ -1307,22 +1349,24 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
                    void *xsave, int gcore)
 {
   struct gdbarch *gdbarch = regcache->arch ();
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  gdb_byte *regs = (gdb_byte *) xsave;
+  gdb_byte *p, *regs = (gdb_byte *) xsave;
+  gdb_byte raw[I386_MAX_REGISTER_SIZE];
+  ULONGEST initial_xstate_bv, clear_bv, xstate_bv = 0;
   int i;
   enum
     {
-      none = 0x0,
-      check = 0x1,
-      x87 = 0x2 | check,
-      sse = 0x4 | check,
-      avxh = 0x8 | check,
-      mpx  = 0x10 | check,
-      avx512_k = 0x20 | check,
-      avx512_zmm_h = 0x40 | check,
-      avx512_ymmh_avx512 = 0x80 | check,
-      avx512_xmm_avx512 = 0x100 | check,
-      pkeys = 0x200 | check,
+      x87_ctrl_or_mxcsr = 0x1,
+      x87 = 0x2,
+      sse = 0x4,
+      avxh = 0x8,
+      mpx  = 0x10,
+      avx512_k = 0x20,
+      avx512_zmm_h = 0x40,
+      avx512_ymmh_avx512 = 0x80,
+      avx512_xmm_avx512 = 0x100,
+      pkeys = 0x200,
       all = x87 | sse | avxh | mpx | avx512_k | avx512_zmm_h
            | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
     } regclass;
@@ -1359,8 +1403,12 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
   else if (regnum >= I387_ST0_REGNUM (tdep)
           && regnum < I387_FCTRL_REGNUM (tdep))
     regclass = x87;
+  else if ((regnum >= I387_FCTRL_REGNUM (tdep)
+           && regnum < I387_XMM0_REGNUM (tdep))
+          || regnum == I387_MXCSR_REGNUM (tdep))
+    regclass = x87_ctrl_or_mxcsr;
   else
-    regclass = none;
+    internal_error (__FILE__, __LINE__, _("invalid i387 regnum %d"), regnum);
 
   if (gcore)
     {
@@ -1373,361 +1421,387 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
       memcpy (XSAVE_XSTATE_BV_ADDR (regs), &tdep->xcr0, 8);
     }
 
-  if ((regclass & check))
+  /* The supported bits in `xstat_bv' are 8 bytes.  */
+  initial_xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+                                               8, byte_order);
+  clear_bv = (~(initial_xstate_bv)) & tdep->xcr0;
+
+  /* The XSAVE buffer was filled lazily by the kernel.  Only those
+     features that are enabled were written into the buffer, disabled
+     features left the buffer uninitialised.  In order to identify if any
+     registers have changed we will be comparing the register cache
+     version to the version in the XSAVE buffer, it is important then that
+     at this point we initialise to the default values any features in
+     XSAVE that are not yet initialised.
+
+     This could be made more efficient, we know which features (from
+     REGNUM) we will be potentially updating, and could limit ourselves to
+     only clearing that feature.  However, the extra complexity does not
+     seem justified at this point.  */
+  if (clear_bv)
     {
-      gdb_byte raw[I386_MAX_REGISTER_SIZE];
-      ULONGEST initial_xstate_bv, clear_bv, xstate_bv = 0;
-      gdb_byte *p;
-      enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-
-      /* The supported bits in `xstat_bv' are 8 bytes.  */
-      initial_xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
-                                                   8, byte_order);
-      clear_bv = (~(initial_xstate_bv)) & tdep->xcr0;
+      if ((clear_bv & X86_XSTATE_PKRU))
+       for (i = I387_PKRU_REGNUM (tdep);
+            i < I387_PKEYSEND_REGNUM (tdep); i++)
+         memset (XSAVE_PKEYS_ADDR (tdep, regs, i), 0, 4);
 
-      /* Clear register set if its bit in xstat_bv is zero.  */
-      if (clear_bv)
-       {
-         if ((clear_bv & X86_XSTATE_PKRU))
-           for (i = I387_PKRU_REGNUM (tdep);
-                i < I387_PKEYSEND_REGNUM (tdep); i++)
-             memset (XSAVE_PKEYS_ADDR (tdep, regs, i), 0, 4);
+      if ((clear_bv & X86_XSTATE_BNDREGS))
+       for (i = I387_BND0R_REGNUM (tdep);
+            i < I387_BNDCFGU_REGNUM (tdep); i++)
+         memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 16);
 
-         if ((clear_bv & X86_XSTATE_BNDREGS))
-           for (i = I387_BND0R_REGNUM (tdep);
-                i < I387_BNDCFGU_REGNUM (tdep); i++)
-             memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 16);
+      if ((clear_bv & X86_XSTATE_BNDCFG))
+       for (i = I387_BNDCFGU_REGNUM (tdep);
+            i < I387_MPXEND_REGNUM (tdep); i++)
+         memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 8);
 
-         if ((clear_bv & X86_XSTATE_BNDCFG))
-           for (i = I387_BNDCFGU_REGNUM (tdep);
-                i < I387_MPXEND_REGNUM (tdep); i++)
-             memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 8);
+      if ((clear_bv & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
+       for (i = I387_ZMM0H_REGNUM (tdep);
+            i < I387_ZMMENDH_REGNUM (tdep); i++)
+         memset (XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i), 0, 32);
 
-         if ((clear_bv & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
-           for (i = I387_ZMM0H_REGNUM (tdep);
-               i < I387_ZMMENDH_REGNUM (tdep); i++)
-             memset (XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i), 0, 32);
+      if ((clear_bv & X86_XSTATE_K))
+       for (i = I387_K0_REGNUM (tdep);
+            i < I387_KEND_REGNUM (tdep); i++)
+         memset (XSAVE_AVX512_K_ADDR (tdep, regs, i), 0, 8);
 
-         if ((clear_bv & X86_XSTATE_K))
-           for (i = I387_K0_REGNUM (tdep);
-               i < I387_KEND_REGNUM (tdep); i++)
-             memset (XSAVE_AVX512_K_ADDR (tdep, regs, i), 0, 8);
+      if ((clear_bv & X86_XSTATE_ZMM))
+       {
+         for (i = I387_YMM16H_REGNUM (tdep);
+              i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
+           memset (XSAVE_YMM_AVX512_ADDR (tdep, regs, i), 0, 16);
+         for (i = I387_XMM16_REGNUM (tdep);
+              i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+           memset (XSAVE_XMM_AVX512_ADDR (tdep, regs, i), 0, 16);
+       }
 
-         if ((clear_bv & X86_XSTATE_ZMM))
-           {
-             for (i = I387_YMM16H_REGNUM (tdep);
-                 i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
-               memset (XSAVE_YMM_AVX512_ADDR (tdep, regs, i), 0, 16);
-             for (i = I387_XMM16_REGNUM (tdep);
-                 i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
-               memset (XSAVE_XMM_AVX512_ADDR (tdep, regs, i), 0, 16);
-           }
+      if ((clear_bv & X86_XSTATE_AVX))
+       for (i = I387_YMM0H_REGNUM (tdep);
+            i < I387_YMMENDH_REGNUM (tdep); i++)
+         memset (XSAVE_AVXH_ADDR (tdep, regs, i), 0, 16);
 
-         if ((clear_bv & X86_XSTATE_AVX))
-           for (i = I387_YMM0H_REGNUM (tdep);
-                i < I387_YMMENDH_REGNUM (tdep); i++)
-             memset (XSAVE_AVXH_ADDR (tdep, regs, i), 0, 16);
+      if ((clear_bv & X86_XSTATE_SSE))
+       for (i = I387_XMM0_REGNUM (tdep);
+            i < I387_MXCSR_REGNUM (tdep); i++)
+         memset (FXSAVE_ADDR (tdep, regs, i), 0, 16);
+
+      /* The mxcsr register is written into the xsave buffer if either AVX
+        or SSE is enabled, so only clear it if both of those features
+        require clearing.  */
+      if ((clear_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE))
+         == (X86_XSTATE_AVX | X86_XSTATE_SSE))
+       store_unsigned_integer (FXSAVE_ADDR (tdep, regs, i), 2, byte_order,
+                               I387_MXCSR_INIT_VAL);
 
-         if ((clear_bv & X86_XSTATE_SSE))
-           for (i = I387_XMM0_REGNUM (tdep);
-                i < I387_MXCSR_REGNUM (tdep); i++)
-             memset (FXSAVE_ADDR (tdep, regs, i), 0, 16);
+      if ((clear_bv & X86_XSTATE_X87))
+       {
+         for (i = I387_ST0_REGNUM (tdep);
+              i < I387_FCTRL_REGNUM (tdep); i++)
+           memset (FXSAVE_ADDR (tdep, regs, i), 0, 10);
 
-         if ((clear_bv & X86_XSTATE_X87))
-           for (i = I387_ST0_REGNUM (tdep);
-                i < I387_FCTRL_REGNUM (tdep); i++)
-             memset (FXSAVE_ADDR (tdep, regs, i), 0, 10);
+         for (i = I387_FCTRL_REGNUM (tdep);
+              i < I387_XMM0_REGNUM (tdep); i++)
+           {
+             if (i == I387_FCTRL_REGNUM (tdep))
+               store_unsigned_integer (FXSAVE_ADDR (tdep, regs, i), 2,
+                                       byte_order, I387_FCTRL_INIT_VAL);
+             else
+               memset (FXSAVE_ADDR (tdep, regs, i), 0,
+                       regcache_register_size (regcache, i));
+           }
        }
+    }
 
-      if (regclass == all)
-       {
-         /* Check if any PKEYS registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_PKRU))
-           for (i = I387_PKRU_REGNUM (tdep);
-                i < I387_PKEYSEND_REGNUM (tdep); i++)
+  if (regclass == all)
+    {
+      /* Check if any PKEYS registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_PKRU))
+       for (i = I387_PKRU_REGNUM (tdep);
+            i < I387_PKEYSEND_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_PKEYS_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 4) != 0)
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_PKEYS_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 4) != 0)
-                 {
-                   xstate_bv |= X86_XSTATE_PKRU;
-                   memcpy (p, raw, 4);
-                 }
+               xstate_bv |= X86_XSTATE_PKRU;
+               memcpy (p, raw, 4);
              }
+         }
 
-         /* Check if any ZMMH registers are changed.  */
-         if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
-           for (i = I387_ZMM0H_REGNUM (tdep);
-                i < I387_ZMMENDH_REGNUM (tdep); i++)
+      /* Check if any ZMMH registers are changed.  */
+      if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
+       for (i = I387_ZMM0H_REGNUM (tdep);
+            i < I387_ZMMENDH_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 32) != 0)
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 32) != 0)
-                 {
-                   xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
-                   memcpy (p, raw, 32);
-                 }
+               xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
+               memcpy (p, raw, 32);
              }
+         }
 
-         /* Check if any K registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_K))
-           for (i = I387_K0_REGNUM (tdep);
-                i < I387_KEND_REGNUM (tdep); i++)
+      /* Check if any K registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_K))
+       for (i = I387_K0_REGNUM (tdep);
+            i < I387_KEND_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_AVX512_K_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 8) != 0)
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_AVX512_K_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 8) != 0)
-                 {
-                   xstate_bv |= X86_XSTATE_K;
-                   memcpy (p, raw, 8);
-                 }
+               xstate_bv |= X86_XSTATE_K;
+               memcpy (p, raw, 8);
              }
+         }
 
-         /* Check if any XMM or upper YMM registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_ZMM))
+      /* Check if any XMM or upper YMM registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_ZMM))
+       {
+         for (i = I387_YMM16H_REGNUM (tdep);
+              i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
            {
-             for (i = I387_YMM16H_REGNUM (tdep);
-                  i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
+             regcache_raw_collect (regcache, i, raw);
+             p = XSAVE_YMM_AVX512_ADDR (tdep, regs, i);
+             if (memcmp (raw, p, 16) != 0)
                {
-                 regcache_raw_collect (regcache, i, raw);
-                 p = XSAVE_YMM_AVX512_ADDR (tdep, regs, i);
-                 if (memcmp (raw, p, 16) != 0)
-                   {
-                     xstate_bv |= X86_XSTATE_ZMM;
-                     memcpy (p, raw, 16);
-                   }
+                 xstate_bv |= X86_XSTATE_ZMM;
+                 memcpy (p, raw, 16);
                }
-             for (i = I387_XMM16_REGNUM (tdep);
-                  i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+           }
+         for (i = I387_XMM16_REGNUM (tdep);
+              i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+           {
+             regcache_raw_collect (regcache, i, raw);
+             p = XSAVE_XMM_AVX512_ADDR (tdep, regs, i);
+             if (memcmp (raw, p, 16) != 0)
                {
-                 regcache_raw_collect (regcache, i, raw);
-                 p = XSAVE_XMM_AVX512_ADDR (tdep, regs, i);
-                 if (memcmp (raw, p, 16) != 0)
-                   {
-                     xstate_bv |= X86_XSTATE_ZMM;
-                     memcpy (p, raw, 16);
-                   }
+                 xstate_bv |= X86_XSTATE_ZMM;
+                 memcpy (p, raw, 16);
                }
            }
+       }
 
-         /* Check if any upper YMM registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_AVX))
-           for (i = I387_YMM0H_REGNUM (tdep);
-                i < I387_YMMENDH_REGNUM (tdep); i++)
+      /* Check if any upper MPX registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_BNDREGS))
+       for (i = I387_BND0R_REGNUM (tdep);
+            i < I387_BNDCFGU_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_MPX_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 16))
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_AVXH_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 16))
-                 {
-                   xstate_bv |= X86_XSTATE_AVX;
-                   memcpy (p, raw, 16);
-                 }
+               xstate_bv |= X86_XSTATE_BNDREGS;
+               memcpy (p, raw, 16);
              }
-         /* Check if any upper MPX registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_BNDREGS))
-           for (i = I387_BND0R_REGNUM (tdep);
-                i < I387_BNDCFGU_REGNUM (tdep); i++)
+         }
+
+      /* Check if any upper MPX registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_BNDCFG))
+       for (i = I387_BNDCFGU_REGNUM (tdep);
+            i < I387_MPXEND_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_MPX_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 8))
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_MPX_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 16))
-                 {
-                   xstate_bv |= X86_XSTATE_BNDREGS;
-                   memcpy (p, raw, 16);
-                 }
+               xstate_bv |= X86_XSTATE_BNDCFG;
+               memcpy (p, raw, 8);
              }
+         }
 
-         /* Check if any upper MPX registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_BNDCFG))
-           for (i = I387_BNDCFGU_REGNUM (tdep);
-                i < I387_MPXEND_REGNUM (tdep); i++)
+      /* Check if any upper YMM registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_AVX))
+       for (i = I387_YMM0H_REGNUM (tdep);
+            i < I387_YMMENDH_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = XSAVE_AVXH_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 16))
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = XSAVE_MPX_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 8))
-                 {
-                   xstate_bv |= X86_XSTATE_BNDCFG;
-                   memcpy (p, raw, 8);
-                 }
+               xstate_bv |= X86_XSTATE_AVX;
+               memcpy (p, raw, 16);
              }
+         }
 
-         /* Check if any SSE registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_SSE))
-           for (i = I387_XMM0_REGNUM (tdep);
-                i < I387_MXCSR_REGNUM (tdep); i++)
+      /* Check if any SSE registers are changed.  */
+      if ((tdep->xcr0 & X86_XSTATE_SSE))
+       for (i = I387_XMM0_REGNUM (tdep);
+            i < I387_MXCSR_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = FXSAVE_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 16))
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = FXSAVE_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 16))
-                 {
-                   xstate_bv |= X86_XSTATE_SSE;
-                   memcpy (p, raw, 16);
-                 }
+               xstate_bv |= X86_XSTATE_SSE;
+               memcpy (p, raw, 16);
              }
+         }
 
-         /* Check if any X87 registers are changed.  */
-         if ((tdep->xcr0 & X86_XSTATE_X87))
-           for (i = I387_ST0_REGNUM (tdep);
-                i < I387_FCTRL_REGNUM (tdep); i++)
+      if ((tdep->xcr0 & X86_XSTATE_AVX) || (tdep->xcr0 & X86_XSTATE_SSE))
+       {
+         i = I387_MXCSR_REGNUM (tdep);
+         regcache_raw_collect (regcache, i, raw);
+         p = FXSAVE_ADDR (tdep, regs, i);
+         if (memcmp (raw, p, 4))
+           {
+             /* Now, we need to mark one of either SSE of AVX as enabled.
+                We could pick either.  What we do is check to see if one
+                of the features is already enabled, if it is then we leave
+                it at that, otherwise we pick SSE.  */
+             if ((xstate_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
+               xstate_bv |= X86_XSTATE_SSE;
+             memcpy (p, raw, 4);
+           }
+       }
+
+      /* Check if any X87 registers are changed.  Only the non-control
+        registers are handled here, the control registers are all handled
+        later on in this function.  */
+      if ((tdep->xcr0 & X86_XSTATE_X87))
+       for (i = I387_ST0_REGNUM (tdep);
+            i < I387_FCTRL_REGNUM (tdep); i++)
+         {
+           regcache_raw_collect (regcache, i, raw);
+           p = FXSAVE_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, 10))
              {
-               regcache_raw_collect (regcache, i, raw);
-               p = FXSAVE_ADDR (tdep, regs, i);
-               if (memcmp (raw, p, 10))
-                 {
-                   xstate_bv |= X86_XSTATE_X87;
-                   memcpy (p, raw, 10);
-                 }
+               xstate_bv |= X86_XSTATE_X87;
+               memcpy (p, raw, 10);
              }
-       }
-      else
-       {
-         /* Check if REGNUM is changed.  */
-         regcache_raw_collect (regcache, regnum, raw);
+         }
+    }
+  else
+    {
+      /* Check if REGNUM is changed.  */
+      regcache_raw_collect (regcache, regnum, raw);
 
-         switch (regclass)
+      switch (regclass)
+       {
+       default:
+         internal_error (__FILE__, __LINE__,
+                         _("invalid i387 regclass"));
+
+       case pkeys:
+         /* This is a PKEYS register.  */
+         p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 4) != 0)
            {
-           default:
-             internal_error (__FILE__, __LINE__,
-                             _("invalid i387 regclass"));
-
-           case pkeys:
-             /* This is a PKEYS register.  */
-             p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 4) != 0)
-               {
-                 xstate_bv |= X86_XSTATE_PKRU;
-                 memcpy (p, raw, 4);
-               }
-             break;
-
-           case avx512_zmm_h:
-             /* This is a ZMM register.  */
-             p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 32) != 0)
-               {
-                 xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
-                 memcpy (p, raw, 32);
-               }
-             break;
-           case avx512_k:
-             /* This is a AVX512 mask register.  */
-             p = XSAVE_AVX512_K_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 8) != 0)
-               {
-                 xstate_bv |= X86_XSTATE_K;
-                 memcpy (p, raw, 8);
-               }
-             break;
+             xstate_bv |= X86_XSTATE_PKRU;
+             memcpy (p, raw, 4);
+           }
+         break;
 
-           case avx512_ymmh_avx512:
-             /* This is an upper YMM16-31 register.  */
-             p = XSAVE_YMM_AVX512_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 16) != 0)
-               {
-                 xstate_bv |= X86_XSTATE_ZMM;
-                 memcpy (p, raw, 16);
-               }
-             break;
+       case avx512_zmm_h:
+         /* This is a ZMM register.  */
+         p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 32) != 0)
+           {
+             xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
+             memcpy (p, raw, 32);
+           }
+         break;
+       case avx512_k:
+         /* This is a AVX512 mask register.  */
+         p = XSAVE_AVX512_K_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 8) != 0)
+           {
+             xstate_bv |= X86_XSTATE_K;
+             memcpy (p, raw, 8);
+           }
+         break;
 
-           case avx512_xmm_avx512:
-             /* This is an upper XMM16-31 register.  */
-             p = XSAVE_XMM_AVX512_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 16) != 0)
-               {
-                 xstate_bv |= X86_XSTATE_ZMM;
-                 memcpy (p, raw, 16);
-               }
-             break;
+       case avx512_ymmh_avx512:
+         /* This is an upper YMM16-31 register.  */
+         p = XSAVE_YMM_AVX512_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 16) != 0)
+           {
+             xstate_bv |= X86_XSTATE_ZMM;
+             memcpy (p, raw, 16);
+           }
+         break;
 
-           case avxh:
-             /* This is an upper YMM register.  */
-             p = XSAVE_AVXH_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 16))
-               {
-                 xstate_bv |= X86_XSTATE_AVX;
-                 memcpy (p, raw, 16);
-               }
-             break;
+       case avx512_xmm_avx512:
+         /* This is an upper XMM16-31 register.  */
+         p = XSAVE_XMM_AVX512_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 16) != 0)
+           {
+             xstate_bv |= X86_XSTATE_ZMM;
+             memcpy (p, raw, 16);
+           }
+         break;
 
-           case mpx:
-             if (regnum < I387_BNDCFGU_REGNUM (tdep))
-               {
-                 regcache_raw_collect (regcache, regnum, raw);
-                 p = XSAVE_MPX_ADDR (tdep, regs, regnum);
-                 if (memcmp (raw, p, 16))
-                   {
-                     xstate_bv |= X86_XSTATE_BNDREGS;
-                     memcpy (p, raw, 16);
-                   }
-               }
-             else
-               {
-                 p = XSAVE_MPX_ADDR (tdep, regs, regnum);
-                 xstate_bv |= X86_XSTATE_BNDCFG;
-                 memcpy (p, raw, 8);
-               }
-             break;
+       case avxh:
+         /* This is an upper YMM register.  */
+         p = XSAVE_AVXH_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 16))
+           {
+             xstate_bv |= X86_XSTATE_AVX;
+             memcpy (p, raw, 16);
+           }
+         break;
 
-           case sse:
-             /* This is an SSE register.  */
-             p = FXSAVE_ADDR (tdep, regs, regnum);
+       case mpx:
+         if (regnum < I387_BNDCFGU_REGNUM (tdep))
+           {
+             regcache_raw_collect (regcache, regnum, raw);
+             p = XSAVE_MPX_ADDR (tdep, regs, regnum);
              if (memcmp (raw, p, 16))
                {
-                 xstate_bv |= X86_XSTATE_SSE;
+                 xstate_bv |= X86_XSTATE_BNDREGS;
                  memcpy (p, raw, 16);
                }
-             break;
+           }
+         else
+           {
+             p = XSAVE_MPX_ADDR (tdep, regs, regnum);
+             xstate_bv |= X86_XSTATE_BNDCFG;
+             memcpy (p, raw, 8);
+           }
+         break;
 
-           case x87:
-             /* This is an x87 register.  */
-             p = FXSAVE_ADDR (tdep, regs, regnum);
-             if (memcmp (raw, p, 10))
-               {
-                 xstate_bv |= X86_XSTATE_X87;
-                 memcpy (p, raw, 10);
-               }
-             break;
+       case sse:
+         /* This is an SSE register.  */
+         p = FXSAVE_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 16))
+           {
+             xstate_bv |= X86_XSTATE_SSE;
+             memcpy (p, raw, 16);
            }
-       }
+         break;
 
-      /* Update the corresponding bits in `xstate_bv' if any SSE/AVX
-        registers are changed.  */
-      if (xstate_bv)
-       {
-         /* The supported bits in `xstat_bv' are 8 bytes.  */
-         initial_xstate_bv |= xstate_bv;
-         store_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
-                                 8, byte_order,
-                                 initial_xstate_bv);
+       case x87:
+         /* This is an x87 register.  */
+         p = FXSAVE_ADDR (tdep, regs, regnum);
+         if (memcmp (raw, p, 10))
+           {
+             xstate_bv |= X86_XSTATE_X87;
+             memcpy (p, raw, 10);
+           }
+         break;
 
-         switch (regclass)
+       case x87_ctrl_or_mxcsr:
+         /* We only handle MXCSR here.  All other x87 control registers
+            are handled separately below.  */
+         if (regnum == I387_MXCSR_REGNUM (tdep))
            {
-           default:
-             internal_error (__FILE__, __LINE__,
-                             _("invalid i387 regclass"));
-
-           case all:
-             break;
-
-           case x87:
-           case sse:
-           case avxh:
-           case mpx:
-           case avx512_k:
-           case avx512_zmm_h:
-           case avx512_ymmh_avx512:
-           case avx512_xmm_avx512:
-           case pkeys:
-             /* Register REGNUM has been updated.  Return.  */
-             return;
+             p = FXSAVE_MXCSR_ADDR (regs);
+             if (memcmp (raw, p, 2))
+               {
+                 /* We're only setting MXCSR, so check the initial state
+                    to see if either of AVX or SSE are already enabled.
+                    If they are then we'll attribute this changed MXCSR to
+                    that feature.  If neither feature is enabled, then
+                    we'll attribute this change to the SSE feature.  */
+                 xstate_bv |= (initial_xstate_bv
+                               & (X86_XSTATE_AVX | X86_XSTATE_SSE));
+                 if ((xstate_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE)) == 0)
+                   xstate_bv |= X86_XSTATE_SSE;
+                 memcpy (p, raw, 2);
+               }
            }
        }
-      else
-       {
-         /* Return if REGNUM isn't changed.  */
-         if (regclass != all)
-           return;
-       }
     }
 
   /* Only handle x87 control registers.  */
@@ -1769,15 +1843,38 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
                      buf[0] |= (1 << fpreg);
                  }
              }
-           memcpy (FXSAVE_ADDR (tdep, regs, i), buf, 2);
+           p = FXSAVE_ADDR (tdep, regs, i);
+           if (memcmp (p, buf, 2))
+             {
+               xstate_bv |= X86_XSTATE_X87;
+               memcpy (p, buf, 2);
+             }
          }
        else
-         regcache_raw_collect (regcache, i, FXSAVE_ADDR (tdep, regs, i));
+         {
+           int regsize;
+
+           regcache_raw_collect (regcache, i, raw);
+           regsize = regcache_register_size (regcache, i);
+           p = FXSAVE_ADDR (tdep, regs, i);
+           if (memcmp (raw, p, regsize))
+             {
+               xstate_bv |= X86_XSTATE_X87;
+               memcpy (p, raw, regsize);
+             }
+         }
       }
 
-  if (regnum == I387_MXCSR_REGNUM (tdep) || regnum == -1)
-    regcache_raw_collect (regcache, I387_MXCSR_REGNUM (tdep),
-                         FXSAVE_MXCSR_ADDR (regs));
+  /* Update the corresponding bits in `xstate_bv' if any
+     registers are changed.  */
+  if (xstate_bv)
+    {
+      /* The supported bits in `xstat_bv' are 8 bytes.  */
+      initial_xstate_bv |= xstate_bv;
+      store_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+                             8, byte_order,
+                             initial_xstate_bv);
+    }
 }
 
 /* Recreate the FTW (tag word) valid bits from the 80-bit FP data in
index 86b16f37654af944df56146d851df8dc0f2c3219..b2fa75aa1222b195a8ff8c3697f48b2ae2c71755 100644 (file)
@@ -156,6 +156,12 @@ extern void i387_collect_fxsave (const struct regcache *regcache, int regnum,
 extern void i387_collect_xsave (const struct regcache *regcache,
                                int regnum, void *xsave, int gcore);
 
+/* Extract a bitset from XSAVE indicating which features are available in
+   the inferior, but not yet initialised.  */
+
+extern ULONGEST i387_xsave_get_clear_bv (struct gdbarch *gdbarch,
+                                        const void *xsave);
+
 /* Prepare the FPU stack in REGCACHE for a function return.  */
 
 extern void i387_return_value (struct gdbarch *gdbarch,
index d6db46916e60bb4d7fa389c9352d8c0394cfc36d..9fc54c0001c2042c7673cd022e42267ac699dcec 100644 (file)
@@ -1,3 +1,8 @@
+2018-05-08  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * gdb.arch/amd64-init-x87-values.S: New file.
+       * gdb.arch/amd64-init-x87-values.exp: New file.
+
 2018-05-08  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.base/watchpoint-unaligned.exp: Use skip_hw_watchpoint_tests.
diff --git a/gdb/testsuite/gdb.arch/amd64-init-x87-values.S b/gdb/testsuite/gdb.arch/amd64-init-x87-values.S
new file mode 100644 (file)
index 0000000..86043a8
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   This file is part of the gdb testsuite.
+
+   Test initial values of x87 control registers, before any x87
+   instructions have been executed in the inferior.  */
+
+        .global _start, main
+        .text
+_start:
+        nop
+
+main:
+        nop
+        fwait
+        nop
+        fld1
+        nop
diff --git a/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp b/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp
new file mode 100644 (file)
index 0000000..c17bb4e
--- /dev/null
@@ -0,0 +1,175 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the gdb testsuite.
+
+# Test initial values of x87 control registers, before any x87
+# instructions have been executed in the inferior.
+
+if ![is_amd64_regs_target] then {
+    return
+}
+
+standard_testfile .S
+
+set options [list debug \
+                additional_flags=-static \
+                additional_flags=-nostartfiles]
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} $options] } {
+    return -1
+}
+
+# Start the test file, and check the x87 control registers (and
+# mxcsr), we expect the default values in all registers.
+#
+# Step forward until the x87 unit is enabled, recheck the register
+# values; they should still have the default values.
+#
+# Finally, step forward until the x87 state has changed, and then
+# recheck the register state to view the changes.
+proc_with_prefix check_x87_regs_around_init {} {
+    global binfile
+
+    clean_restart ${binfile}
+
+    # Get things started.
+    if ![runto_main] then {
+       fail "can't run to main"
+       return 0
+    }
+
+    # Check initial values of x87 control registers.  The MXCSR isn't part
+    # of the x87 set, it belongs with SSE/AVX, however we test it here
+    # too, it should have its default value in both cases.
+    foreach {regname regvalue} { "fctrl" "0x37f" \
+                                "fstat" "0x0" \
+                                "ftag" "0xffff" \
+                                "fiseg" "0x0" \
+                                "fioff" "0x0" \
+                                "foseg" "0x0" \
+                                "fooff" "0x0" \
+                                "fop" "0x0" \
+                                "mxcsr" "0x1f80"} {
+       gdb_test "p/x \$${regname}" " = ${regvalue}"  "check initial value of \$${regname}"
+    }
+
+    # No x87 instructions have been executed yet.  Step up to FWAIT
+    # instruction.  Executing this instruction will enable the x87 unit,
+    # causing the kernel to place the default values into all registers.
+    # After this GDB will no longer supply the default values itself but
+    # will instread read the values out of the xsave buffer.
+    gdb_test "stepi" "fwait" "step to FWAIT instruction"
+    gdb_test "stepi" "nop" "step past FWAIT instruction"
+
+    # The x87 unit is now enabled, but the register values should be
+    # unchanged.
+    foreach {regname regvalue} { "fctrl" "0x37f" \
+                                "fstat" "0x0" \
+                                "ftag" "0xffff" \
+                                "fiseg" "0x0" \
+                                "fioff" "0x0" \
+                                "foseg" "0x0" \
+                                "fooff" "0x0" \
+                                "fop" "0x0" \
+                                "mxcsr" "0x1f80"} {
+       gdb_test "p/x \$${regname}" " = ${regvalue}"  "check post FWAIT value of \$${regname}"
+    }
+
+    # Now step to an x87 instruction that modifies some state.
+    gdb_test "stepi" "fld1" "step to FLD1 instruction"
+
+    # Grab the address of this instruction, it will appear in later
+    # results.
+    set addr [get_hexadecimal_valueof "\$pc" "0"]
+
+    # Step past the FLD1 instruction.
+    gdb_test "stepi" "nop" "step past FLD1 instruction"
+
+    # Check new values of x87 control registers (and MXCSR).
+    foreach {regname regvalue} [list "fctrl" "0x37f" \
+                                    "fstat" "0x3800" \
+                                    "ftag" "0x3fff" \
+                                    "fiseg" "0x0" \
+                                    "fioff" $addr \
+                                    "foseg" "0x0" \
+                                    "fooff" "0x0" \
+                                    "fop" "0x0" \
+                                    "mxcsr" "0x1f80" ] {
+       gdb_test "p/x \$${regname}" " = ${regvalue}"  "check post FLD1 value of \$${regname}"
+    }
+}
+
+# Start the test file, all FP features will be disabled.  Set a new
+# value into the MXCSR register, then step forward one instruction (a
+# nop that does not enable any FP features).  Finally check that the
+# mxcsr register still has the value we set.
+proc_with_prefix check_setting_mxcsr_before_enable {} {
+    global binfile
+
+    clean_restart ${binfile}
+
+    if ![runto_main] then {
+       fail "can't run to main"
+       return 0
+    }
+
+    gdb_test_no_output "set \$mxcsr=0x9f80" "set a new value for MXCSR"
+    gdb_test "stepi" "fwait" "step forward one instruction for mxcsr test"
+    gdb_test "p/x \$mxcsr" " = 0x9f80" "check new value of MXCSR is still in place"
+}
+
+# Start the test file, all FP features will be disabled.  Set new
+# values into the x87 control-registers, then step forward one
+# instruction (a nop that does not enable any FP features).  Finally
+# check that all the x87 control-registers still have the values we
+# set.
+proc_with_prefix check_setting_x87_regs_before_enable {} {
+    global binfile
+
+    clean_restart ${binfile}
+
+    if ![runto_main] then {
+       fail "can't run to main"
+       return 0
+    }
+
+    foreach {regname regvalue} [list "fctrl" "0x37f" \
+                                   "fstat" "0x3800" \
+                                   "ftag" "0x7777" \
+                                   "fiseg" "0x12" \
+                                   "fioff" "0x2418" \
+                                   "foseg" "0x24" \
+                                   "fooff" "0x36" \
+                                   "fop" "0x100" ] {
+       gdb_test_no_output "set \$$regname=$regvalue" "set a new value for $regname"
+    }
+
+    gdb_test "stepi" "fwait" "step forward one instruction for x87 test"
+
+    foreach {regname regvalue} [list "fctrl" "0x37f" \
+                                   "fstat" "0x3800" \
+                                   "ftag" "0x7777" \
+                                   "fiseg" "0x12" \
+                                   "fioff" "0x2418" \
+                                   "foseg" "0x24" \
+                                   "fooff" "0x36" \
+                                   "fop" "0x100" ] {
+       gdb_test "p/x \$$regname" "= $regvalue" "check new value of $regname"
+    }
+}
+
+check_x87_regs_around_init
+check_setting_mxcsr_before_enable
+check_setting_x87_regs_before_enable