gdb/x86: Handle kernels using compact xsave format

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.
This commit is contained in:
Andrew Burgess 2018-05-03 17:46:14 +01:00
parent 886d542809
commit 8ee22052f6
12 changed files with 918 additions and 446 deletions

View 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.

View 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);
}
}
}

View File

@ -72,4 +72,17 @@
(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 */

View 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.

View 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)
}
}
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);
}
}
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);
}
collect_register_by_name (regcache, "fooff", raw);
if (memcmp (raw, &fp->fooff, 4) != 0)
{
xstate_bv |= X86_XSTATE_X87;
memcpy (&fp->fooff, raw, 4);
}
/* 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;
}
/* Some registers are 16-bit. */
collect_register_by_name (regcache, "fctrl", &val);
if (fp->fctrl != val)
{
xstate_bv |= X86_XSTATE_X87;
fp->fctrl = val;
}
collect_register_by_name (regcache, "fstat", &val);
if (fp->fstat != val)
{
xstate_bv |= X86_XSTATE_X87;
fp->fstat = 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;
if (tag != 3)
val2 |= (1 << i);
}
if (fp->ftag != val2)
{
xstate_bv |= X86_XSTATE_X87;
fp->ftag = val2;
}
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;
collect_register_by_name (regcache, "fioff", &fp->fioff);
collect_register_by_name (regcache, "fooff", &fp->fooff);
collect_register_by_name (regcache, "mxcsr", &fp->mxcsr);
/* This one's 11 bits... */
collect_register_by_name (regcache, "fop", &val2);
fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
/* Some registers are 16-bit. */
collect_register_by_name (regcache, "fctrl", &val);
fp->fctrl = val;
collect_register_by_name (regcache, "fstat", &val);
fp->fstat = 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;
if (tag != 3)
val2 |= (1 << i);
}
fp->ftag = val2;
collect_register_by_name (regcache, "fiseg", &val);
fp->fiseg = val;
collect_register_by_name (regcache, "foseg", &val);
fp->foseg = val;
}
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);
/* Some registers are 16-bit. */
val = fp->fctrl & 0xFFFF;
supply_register_by_name (regcache, "fctrl", &val);
val = fp->fstat & 0xFFFF;
supply_register_by_name (regcache, "fstat", &val);
/* Generate the form of ftag data that GDB expects. */
top = (fp->fstat >> 11) & 0x7;
val = 0;
for (i = 7; i >= 0; i--)
if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
== (X86_XSTATE_SSE | X86_XSTATE_AVX))
{
int tag;
if (fp->ftag & (1 << i))
tag = i387_ftag (fxp, (i + 8 - top) % 8);
else
tag = 3;
val |= tag << (2 * i);
unsigned int default_mxcsr = I387_MXCSR_INIT_VAL;
supply_register_by_name (regcache, "mxcsr", &default_mxcsr);
}
supply_register_by_name (regcache, "ftag", &val);
else
supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
val = fp->fiseg & 0xFFFF;
supply_register_by_name (regcache, "fiseg", &val);
if ((clear_bv & X86_XSTATE_X87) != 0)
{
supply_register_by_name_zeroed (regcache, "fioff");
supply_register_by_name_zeroed (regcache, "fooff");
val = fp->foseg & 0xFFFF;
supply_register_by_name (regcache, "foseg", &val);
val = I387_FCTRL_INIT_VAL;
supply_register_by_name (regcache, "fctrl", &val);
val = (fp->fop) & 0x7FF;
supply_register_by_name (regcache, "fop", &val);
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");
}
else
{
supply_register_by_name (regcache, "fioff", &fp->fioff);
supply_register_by_name (regcache, "fooff", &fp->fooff);
/* Some registers are 16-bit. */
val = fp->fctrl & 0xFFFF;
supply_register_by_name (regcache, "fctrl", &val);
val = fp->fstat & 0xFFFF;
supply_register_by_name (regcache, "fstat", &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. */

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

View 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);

View 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,360 +1421,386 @@ 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);
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);
/* 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_BNDREGS))
for (i = I387_BND0R_REGNUM (tdep);
i < I387_BNDCFGU_REGNUM (tdep); i++)
memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 16);
/* Clear register set if its bit in xstat_bv is zero. */
if (clear_bv)
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_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))
{
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_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_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_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);
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_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 (regclass == all)
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_X87))
{
/* 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)
{
xstate_bv |= X86_XSTATE_PKRU;
memcpy (p, raw, 4);
}
}
for (i = I387_ST0_REGNUM (tdep);
i < I387_FCTRL_REGNUM (tdep); i++)
memset (FXSAVE_ADDR (tdep, regs, i), 0, 10);
/* 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)
{
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++)
{
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);
}
}
/* Check if any XMM or upper YMM registers are changed. */
if ((tdep->xcr0 & X86_XSTATE_ZMM))
for (i = I387_FCTRL_REGNUM (tdep);
i < I387_XMM0_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)
{
xstate_bv |= X86_XSTATE_ZMM;
memcpy (p, raw, 16);
}
}
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)
{
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++)
{
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);
}
}
/* 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))
{
xstate_bv |= X86_XSTATE_BNDREGS;
memcpy (p, raw, 16);
}
}
/* 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))
{
xstate_bv |= X86_XSTATE_BNDCFG;
memcpy (p, raw, 8);
}
}
/* 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))
{
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++)
{
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);
}
}
}
else
{
/* Check if REGNUM is changed. */
regcache_raw_collect (regcache, regnum, raw);
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)
{
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;
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_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 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 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);
}
}
if (i == I387_FCTRL_REGNUM (tdep))
store_unsigned_integer (FXSAVE_ADDR (tdep, regs, i), 2,
byte_order, I387_FCTRL_INIT_VAL);
else
{
p = XSAVE_MPX_ADDR (tdep, regs, regnum);
xstate_bv |= X86_XSTATE_BNDCFG;
memcpy (p, raw, 8);
}
break;
memset (FXSAVE_ADDR (tdep, regs, i), 0,
regcache_register_size (regcache, i));
}
}
}
case sse:
/* This is an SSE register. */
p = FXSAVE_ADDR (tdep, regs, regnum);
if (memcmp (raw, p, 16))
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)
{
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++)
{
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);
}
}
/* 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)
{
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))
{
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)
{
xstate_bv |= X86_XSTATE_SSE;
xstate_bv |= X86_XSTATE_ZMM;
memcpy (p, raw, 16);
}
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;
}
}
/* 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);
switch (regclass)
for (i = I387_XMM16_REGNUM (tdep);
i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
{
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;
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);
}
}
}
else
/* 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))
{
xstate_bv |= X86_XSTATE_BNDREGS;
memcpy (p, raw, 16);
}
}
/* 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))
{
xstate_bv |= X86_XSTATE_BNDCFG;
memcpy (p, raw, 8);
}
}
/* 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))
{
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++)
{
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);
}
}
if ((tdep->xcr0 & X86_XSTATE_AVX) || (tdep->xcr0 & X86_XSTATE_SSE))
{
/* Return if REGNUM isn't changed. */
if (regclass != all)
return;
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))
{
xstate_bv |= X86_XSTATE_X87;
memcpy (p, raw, 10);
}
}
}
else
{
/* Check if REGNUM is changed. */
regcache_raw_collect (regcache, regnum, raw);
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)
{
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;
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_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 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 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 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;
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 x87_ctrl_or_mxcsr:
/* We only handle MXCSR here. All other x87 control registers
are handled separately below. */
if (regnum == I387_MXCSR_REGNUM (tdep))
{
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);
}
}
}
}
@ -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

View 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,

View 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.

View File

@ -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

View File

@ -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