From: Andrew Burgess Date: Thu, 3 May 2018 16:46:14 +0000 (+0100) Subject: gdb/x86: Handle kernels using compact xsave format X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8ee22052f690c007556b97eed59f49350ece5ca9;p=binutils-gdb.git 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. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 32038c59471..8314945f1c7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2018-05-08 Andrew Burgess + + * 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 * spu-tdep.c (info_spu_event_command): Fix output formatting. diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index 1fea26409ae..d555465c2f9 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -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); + } } } diff --git a/gdb/common/x86-xstate.h b/gdb/common/x86-xstate.h index 5a165b3f6cb..51e5c3c785a 100644 --- a/gdb/common/x86-xstate.h +++ b/gdb/common/x86-xstate.h @@ -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 */ diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 74613598a28..9cd72a89f71 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,13 @@ +2018-05-08 Andrew Burgess + + * 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 * configure: Rebuild. diff --git a/gdb/gdbserver/i387-fp.c b/gdb/gdbserver/i387-fp.c index f1f7d844537..bee1af9d8c6 100644 --- a/gdb/gdbserver/i387-fp.c +++ b/gdb/gdbserver/i387-fp.c @@ -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. */ diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c index cbdf766df2c..0718b9f9a04 100644 --- a/gdb/gdbserver/regcache.c +++ b/gdb/gdbserver/regcache.c @@ -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. */ diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h index 6ff13084b0f..2c0df648f6d 100644 --- a/gdb/gdbserver/regcache.h +++ b/gdb/gdbserver/regcache.h @@ -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); diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c index 19387929c59..aca70c186f9 100644 --- a/gdb/i387-tdep.c +++ b/gdb/i387-tdep.c @@ -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 diff --git a/gdb/i387-tdep.h b/gdb/i387-tdep.h index 86b16f37654..b2fa75aa122 100644 --- a/gdb/i387-tdep.h +++ b/gdb/i387-tdep.h @@ -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, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index d6db46916e6..9fc54c0001c 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-05-08 Andrew Burgess + + * gdb.arch/amd64-init-x87-values.S: New file. + * gdb.arch/amd64-init-x87-values.exp: New file. + 2018-05-08 Jan Kratochvil * 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 index 00000000000..86043a81148 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-init-x87-values.S @@ -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 . + + 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 index 00000000000..c17bb4e1e54 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp @@ -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 . + +# 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