From patchwork Fri May 4 19:14:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 27119 Received: (qmail 103270 invoked by alias); 4 May 2018 19:14:54 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 103258 invoked by uid 89); 4 May 2018 19:14:53 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=one's, relating X-HELO: mail-wr0-f176.google.com Received: from mail-wr0-f176.google.com (HELO mail-wr0-f176.google.com) (209.85.128.176) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 04 May 2018 19:14:47 +0000 Received: by mail-wr0-f176.google.com with SMTP id f2-v6so10289284wrm.3 for ; Fri, 04 May 2018 12:14:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=C2kxumrB1s/3QbJ0aAjxmNwjIOr10+pqmfLyagG55sA=; b=SaO56EVjPWPIUPPAaKl61DjxI+2xnVi8SECDom9NB9+oLofYiTAXEsVbGlWbVVkbll iehoWH1foMVrLWSxKFxZ76njbM4nt7v5rP9U2dB5jMcCtqO4xDgteJSTt7rjZLRvRceT 5Ylo4d1S+4Zkt7Hm/0cAnzDdA05/mUfwA82ed7cKwY3szXM7/PjVLgoQGMnIPoft+NZ/ CcRPgZQTYBkrnN3Xip4wXPslxHnfKldtPfPeRQxxhJQQpVU2CA7lC+wMgsF+fSAq3gQY FYeKRi1ap/eeqNBMzLAJo1Uj6QyZWFO8MkLhQYBu6KUvvyoov1Dhsy153ZaGMpmnMaiH SLuQ== X-Gm-Message-State: ALQs6tBU6uFwXX7jlVO1Xl17vk+JihfRaZaZEIbeLz/v+qkOArTho5Ab GJKIhB5wkPvFpOu39zpqyHOzssro X-Google-Smtp-Source: AB8JxZqXOcczvR4d/S3oqLJzqoE8BCAmPL/bEBGYNDZIhtFgzVHbSgh5Hg2NQoPgZ5BT+ekWTyn0ZQ== X-Received: by 2002:adf:e38e:: with SMTP id e14-v6mr21793456wrm.198.1525461283092; Fri, 04 May 2018 12:14:43 -0700 (PDT) Received: from localhost (host81-147-175-127.range81-147.btcentralplus.com. [81.147.175.127]) by smtp.gmail.com with ESMTPSA id a69-v6sm2382667wma.7.2018.05.04.12.14.42 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 04 May 2018 12:14:42 -0700 (PDT) Date: Fri, 4 May 2018 20:14:41 +0100 From: Andrew Burgess To: Pedro Alves Cc: gdb-patches@sourceware.org Subject: Re: [PATCHv2] gdb/x86: Handle kernels using compact xsave format Message-ID: <20180504191441.GL3375@embecosm.com> References: <20180503170624.20847-1-andrew.burgess@embecosm.com> <6131c1b9-21c1-6a38-f466-34bfef144a26@redhat.com> <20180503173712.GI3375@embecosm.com> <3c303c56-2315-fb00-dff1-805ec84a115c@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <3c303c56-2315-fb00-dff1-805ec84a115c@redhat.com> X-Editor: GNU Emacs [ http://www.gnu.org/software/emacs ] User-Agent: Mutt/1.9.2 (2017-12-15) X-IsSubscribed: yes * Pedro Alves [2018-05-04 11:39:42 +0100]: > On 05/03/2018 06:37 PM, Andrew Burgess wrote: > > > Hope that helps, > > It does, thank you. > > The patch looks good to me, with the following nits > addressed. Thanks for all the review feedback. I've attached the new version below, the only changes are those you suggested in your review, so I think the patch is now approved. Still I wanted to give you a chance to take a final look. If I don't hear anything in a few days I'll apply this version. Thanks, Andrew --- 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. --- gdb/ChangeLog | 17 + gdb/amd64-tdep.c | 22 +- gdb/common/x86-xstate.h | 13 + gdb/gdbserver/ChangeLog | 10 + gdb/gdbserver/i387-fp.c | 210 +++++-- gdb/gdbserver/regcache.c | 13 + gdb/gdbserver/regcache.h | 3 + gdb/i387-tdep.c | 767 +++++++++++++---------- gdb/i387-tdep.h | 6 + gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.arch/amd64-init-x87-values.S | 31 + gdb/testsuite/gdb.arch/amd64-init-x87-values.exp | 175 ++++++ 12 files changed, 872 insertions(+), 400 deletions(-) create mode 100644 gdb/testsuite/gdb.arch/amd64-init-x87-values.S create mode 100644 gdb/testsuite/gdb.arch/amd64-init-x87-values.exp 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/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/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