[PATCH v4 04/11] gdb, gdbserver: Use xstate_bv for target description creation on x86.

Christina Schimpe
2025-06-17T12:18:57Z

The XSAVE features set is organized in state components, which are a set
of or parts of registers. So-called XSAVE-supported features are
organized using state-component bitmaps, each bit corresponding to a
single state component.

The SDM uses the term xstate_bv for a state-component bitmap, which is
defined as XCR0 | IA32_XSS. The control register XCR0 only contains a
state-component bitmap that specifies user state components, while IA32_XSS
contains a state-component bitmap that specifies supervisor state components.

Until now, XCR0 is used as input for target description creation in GDB.
However, a following patch will add userspace support for the CET shadow
stack feature by Intel. The CET state is configured in IA32_XSS and consists
of 2 state components:
- State component 11 used for the 2 MSRs controlling user-mode
functionality for CET (CET_U state)
- State component 12 used for the 3 MSRs containing shadow-stack pointers
for privilege levels 0-2 (CET_S state).

Reading the CET shadow stack pointer register on linux requires a separate
ptrace call using NT_X86_SHSTK. To pass the CET shadow stack enablement
state we would like to pass the xstate_bv value instead of xcr0 for target
description creation. To prepare for that, we rename the xcr0 mask
values for target description creation to xstate_bv. However, this
patch doesn't add any functional changes in GDB.

Future states specified in IA32_XSS such as CET will create a combined
xstate_bv_mask including xcr0 register value and its corresponding bit in
the state component bitmap. This combined mask will then be used to create
the target descriptions.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
gdb/amd64-tdep.c | 14 +++----
gdb/amd64-tdep.h | 8 +++-
gdb/arch/amd64-linux-tdesc.c | 33 ++++++++--------
gdb/arch/amd64-linux-tdesc.h | 7 ++--
gdb/arch/amd64.c | 15 +++-----
gdb/arch/amd64.h | 10 ++++-
gdb/arch/i386-linux-tdesc.c | 29 +++++++-------
gdb/arch/i386-linux-tdesc.h | 5 ++-
gdb/arch/i386.c | 15 ++++----
gdb/arch/i386.h | 8 +++-
gdb/arch/x86-linux-tdesc-features.c | 59 +++++++++++++++--------------
gdb/arch/x86-linux-tdesc-features.h | 25 +++++++-----
gdb/i386-tdep.c | 14 +++----
gdb/i386-tdep.h | 7 +++-
gdb/nat/x86-linux-tdesc.c | 18 +++++----
gdb/nat/x86-linux-tdesc.h | 7 ++--
gdb/x86-linux-nat.c | 11 ++++--
gdbserver/i387-fp.cc | 40 +++++++++----------
gdbserver/linux-amd64-ipa.cc | 10 +++--
gdbserver/linux-i386-ipa.cc | 6 +--
gdbserver/linux-x86-low.cc | 9 ++---
gdbsupport/x86-xstate.h | 4 +-
22 files changed, 198 insertions(+), 156 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index e4957783502..e4a0ecf7ac9 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3523,23 +3523,23 @@ amd64_x32_none_init_abi (gdbarch_info info, gdbarch *arch)
amd64_target_description (X86_XSTATE_SSE_MASK, true));
}

-/* Return the target description for a specified XSAVE feature mask. */
+/* See amd64-tdep.h. */

const struct target_desc *
-amd64_target_description (uint64_t xcr0, bool segments)
+amd64_target_description (uint64_t xstate_bv_mask, bool segments)
{
static target_desc *amd64_tdescs \
[2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
target_desc **tdesc;

- tdesc = &amd64_tdescs[(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
- [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
- [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
+ tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
+ [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
+ [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
[segments ? 1 : 0];

if (*tdesc == NULL)
- *tdesc = amd64_create_target_description (xcr0, false, false,
- segments);
+ *tdesc = amd64_create_target_description (xstate_bv_mask, false,
+ false, segments);

return *tdesc;
}
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index cbf8a97fa3c..82f781bf404 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -108,8 +108,12 @@ extern void amd64_init_abi (struct gdbarch_info info,
extern void amd64_x32_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch,
const target_desc *default_tdesc);
-extern const struct target_desc *amd64_target_description (uint64_t xcr0,
- bool segments);
+
+/* Return the target description for the specified xsave features as
+ defined in XSTATE_BV_MASK and SEGMENTS. */
+
+extern const struct target_desc *amd64_target_description
+ (uint64_t xstate_bv_mask, bool segments);

/* Fill register REGNUM in REGCACHE with the appropriate
floating-point or SSE register value from *FXSAVE. If REGNUM is
diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
index 91de75873a1..771badc0ca7 100644
--- a/gdb/arch/amd64-linux-tdesc.c
+++ b/gdb/arch/amd64-linux-tdesc.c
@@ -26,41 +26,42 @@
/* See arch/amd64-linux-tdesc.h. */

const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+amd64_linux_read_description (uint64_t xstate_bv_mask, bool is_x32)
{
/* The type used for the amd64 and x32 target description caches. */
using tdesc_cache_type = std::unordered_map<uint64_t, const target_desc_up>;

/* Caches for the previously seen amd64 and x32 target descriptions,
- indexed by the xcr0 value that created the target description. These
- need to be static within this function to ensure they are initialised
- before first use. */
+ indexed by the xstate_bv_mask value that created the target
+ description. These need to be static within this function to ensure
+ they are initialised before first use. */
static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;

tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;

- /* Only some bits are checked when creating a tdesc, but the XCR0 value
- contains other feature bits that are not relevant for tdesc creation.
- When indexing into the TDESC_CACHE we need to use a consistent xcr0
- value otherwise we might fail to find an existing tdesc which has the
- same set of relevant bits set. */
- xcr0 &= is_x32
- ? x86_linux_x32_xcr0_feature_mask ()
- : x86_linux_amd64_xcr0_feature_mask ();
+ /* Only some bits are checked when creating a tdesc, but the
+ xstate_bv_mask value contains other feature bits that are not
+ relevant for tdesc creation.
+ When indexing into the TDESC_CACHE we need to use a consistent
+ xstate_bv_mask value otherwise we might fail to find an existing
+ tdesc which has the same set of relevant bits set. */
+ xstate_bv_mask &= is_x32
+ ? x86_linux_x32_xstate_bv_feature_mask ()
+ : x86_linux_amd64_xstate_bv_feature_mask ();

- const auto it = tdesc_cache.find (xcr0);
+ const auto it = tdesc_cache.find (xstate_bv_mask);
if (it != tdesc_cache.end ())
return it->second.get ();

/* Create the previously unseen target description. */
- target_desc_up tdesc (amd64_create_target_description (xcr0, is_x32,
- true, true));
+ target_desc_up tdesc (amd64_create_target_description (xstate_bv_mask,
+ is_x32, true, true));
x86_linux_post_init_tdesc (tdesc.get (), true);

/* Add to the cache, and return a pointer borrowed from the
target_desc_up. This is safe as the cache (and the pointers contained
within it) are not deleted until GDB exits. */
target_desc *ptr = tdesc.get ();
- tdesc_cache.emplace (xcr0, std::move (tdesc));
+ tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
return ptr;
}
diff --git a/gdb/arch/amd64-linux-tdesc.h b/gdb/arch/amd64-linux-tdesc.h
index 8806a132562..0d0e1bbff72 100644
--- a/gdb/arch/amd64-linux-tdesc.h
+++ b/gdb/arch/amd64-linux-tdesc.h
@@ -22,9 +22,10 @@

struct target_desc;

-/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32. */
+/* Return the AMD64 target descriptions corresponding to XSTATE_BV_MASK
+ and IS_X32. */

-extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
- bool is_x32);
+extern const target_desc *amd64_linux_read_description
+ (uint64_t xstate_bv_mask, bool is_x32);

#endif /* GDB_ARCH_AMD64_LINUX_TDESC_H */
diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index 252650b6390..9cac0645cdc 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -30,14 +30,11 @@

#include "../features/i386/x32-core.c"

-/* Create amd64 target descriptions according to XCR0. If IS_X32 is
- true, create the x32 ones. If IS_LINUX is true, create target
- descriptions for Linux. If SEGMENTS is true, then include
- the "org.gnu.gdb.i386.segments" feature registers. */
+/* See amd64.h. */

target_desc *
-amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
- bool segments)
+amd64_create_target_description (uint64_t xstate_bv_mask, bool is_x32,
+ bool is_linux, bool segments)
{
target_desc_up tdesc = allocate_target_description ();

@@ -62,13 +59,13 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
if (segments)
regnum = create_feature_i386_64bit_segments (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_AVX)
+ if (xstate_bv_mask & X86_XSTATE_AVX)
regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_AVX512)
+ if (xstate_bv_mask & X86_XSTATE_AVX512)
regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_PKRU)
+ if (xstate_bv_mask & X86_XSTATE_PKRU)
regnum = create_feature_i386_pkeys (tdesc.get (), regnum);

return tdesc.release ();
diff --git a/gdb/arch/amd64.h b/gdb/arch/amd64.h
index 695660c55d6..54080ee10a9 100644
--- a/gdb/arch/amd64.h
+++ b/gdb/arch/amd64.h
@@ -21,7 +21,13 @@
#include "gdbsupport/tdesc.h"
#include <stdint.h>

-target_desc *amd64_create_target_description (uint64_t xcr0, bool is_x32,
- bool is_linux, bool segments);
+/* Create amd64 target descriptions according to XSTATE_BV_MASK. If
+ IS_X32 is true, create the x32 ones. If IS_LINUX is true, create
+ target descriptions for Linux. If SEGMENTS is true, then include
+ the "org.gnu.gdb.i386.segments" feature registers. */
+
+target_desc *amd64_create_target_description (uint64_t xstate_bv_mask,
+ bool is_x32, bool is_linux,
+ bool segments);

#endif /* GDB_ARCH_AMD64_H */
diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
index 51513176464..57cdbbfa7e5 100644
--- a/gdb/arch/i386-linux-tdesc.c
+++ b/gdb/arch/i386-linux-tdesc.c
@@ -25,32 +25,35 @@
/* See arch/i386-linux-tdesc.h. */

const target_desc *
-i386_linux_read_description (uint64_t xcr0)
+i386_linux_read_description (uint64_t xstate_bv_mask)
{
- /* Cache of previously seen i386 target descriptions, indexed by the xcr0
- value that created the target description. This needs to be static
- within this function to ensure it is initialised before first use. */
+ /* Cache of previously seen i386 target descriptions, indexed by the
+ xstate_bv_mask value that created the target description. This
+ needs to be static within this function to ensure it is initialised
+ before first use. */
static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;

- /* Only some bits are checked when creating a tdesc, but the XCR0 value
- contains other feature bits that are not relevant for tdesc creation.
- When indexing into the I386_TDESC_CACHE we need to use a consistent
- xcr0 value otherwise we might fail to find an existing tdesc which has
- the same set of relevant bits set. */
- xcr0 &= x86_linux_i386_xcr0_feature_mask ();
+ /* Only some bits are checked when creating a tdesc, but the
+ XSTATE_BV_MASK value contains other feature bits that are not
+ relevant for tdesc creation. When indexing into the I386_TDESC_CACHE
+ we need to use a consistent XSTATE_BV_MASK value otherwise we might
+ fail to find an existing tdesc which has the same set of relevant
+ bits set. */
+ xstate_bv_mask &= x86_linux_i386_xstate_bv_feature_mask ();

- const auto it = i386_tdesc_cache.find (xcr0);
+ const auto it = i386_tdesc_cache.find (xstate_bv_mask);
if (it != i386_tdesc_cache.end ())
return it->second.get ();

/* Create the previously unseen target description. */
- target_desc_up tdesc (i386_create_target_description (xcr0, true, false));
+ target_desc_up tdesc
+ (i386_create_target_description (xstate_bv_mask, true, false));
x86_linux_post_init_tdesc (tdesc.get (), false);

/* Add to the cache, and return a pointer borrowed from the
target_desc_up. This is safe as the cache (and the pointers contained
within it) are not deleted until GDB exits. */
target_desc *ptr = tdesc.get ();
- i386_tdesc_cache.emplace (xcr0, std::move (tdesc));
+ i386_tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
return ptr;
}
diff --git a/gdb/arch/i386-linux-tdesc.h b/gdb/arch/i386-linux-tdesc.h
index 2c3c1740d81..3392b3fa06e 100644
--- a/gdb/arch/i386-linux-tdesc.h
+++ b/gdb/arch/i386-linux-tdesc.h
@@ -22,8 +22,9 @@

struct target_desc;

-/* Return the i386 target description corresponding to XCR0. */
+/* Return the i386 target description corresponding to XSTATE_BV_MASK. */

-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+extern const struct target_desc *i386_linux_read_description
+ (uint64_t xstate_bv_mask);

#endif /* GDB_ARCH_I386_LINUX_TDESC_H */
diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
index 835df53c75d..84f31439e7a 100644
--- a/gdb/arch/i386.c
+++ b/gdb/arch/i386.c
@@ -29,10 +29,11 @@
#include "../features/i386/32bit-segments.c"
#include "../features/i386/pkeys.c"

-/* Create i386 target descriptions according to XCR0. */
+/* See i386.h. */

target_desc *
-i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
+i386_create_target_description (uint64_t xstate_bv_mask, bool is_linux,
+ bool segments)
{
target_desc_up tdesc = allocate_target_description ();

@@ -44,10 +45,10 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)

long regnum = 0;

- if (xcr0 & X86_XSTATE_X87)
+ if (xstate_bv_mask & X86_XSTATE_X87)
regnum = create_feature_i386_32bit_core (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_SSE)
+ if (xstate_bv_mask & X86_XSTATE_SSE)
regnum = create_feature_i386_32bit_sse (tdesc.get (), regnum);

if (is_linux)
@@ -56,13 +57,13 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
if (segments)
regnum = create_feature_i386_32bit_segments (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_AVX)
+ if (xstate_bv_mask & X86_XSTATE_AVX)
regnum = create_feature_i386_32bit_avx (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_AVX512)
+ if (xstate_bv_mask & X86_XSTATE_AVX512)
regnum = create_feature_i386_32bit_avx512 (tdesc.get (), regnum);

- if (xcr0 & X86_XSTATE_PKRU)
+ if (xstate_bv_mask & X86_XSTATE_PKRU)
regnum = create_feature_i386_pkeys (tdesc.get (), regnum);

return tdesc.release ();
diff --git a/gdb/arch/i386.h b/gdb/arch/i386.h
index 1fc41014624..a23deebc366 100644
--- a/gdb/arch/i386.h
+++ b/gdb/arch/i386.h
@@ -21,7 +21,13 @@
#include "gdbsupport/tdesc.h"
#include <stdint.h>

-target_desc *i386_create_target_description (uint64_t xcr0, bool is_linux,
+/* Create i386 target descriptions according to XSTATE_BV_MASK. If
+ IS_LINUX is true, create target descriptions for Linux. If SEGMENTS
+ is true, then include the "org.gnu.gdb.i386.segments" feature
+ registers. */
+
+target_desc *i386_create_target_description (uint64_t xstate_bv_mask,
+ bool is_linux,
bool segments);

#endif /* GDB_ARCH_I386_H */
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
index f65920cb95b..68f37fccaef 100644
--- a/gdb/arch/x86-linux-tdesc-features.c
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -28,18 +28,21 @@

We want to cache target descriptions, and this is currently done in
three separate caches, one each for i386, amd64, and x32. Additionally,
- the caching we're discussing here is Linux only, and for Linux, the only
- thing that has an impact on target description creation is the xcr0
- value.
-
- In order to ensure the cache functions correctly we need to filter out
- only those xcr0 feature bits that are relevant, we can then cache target
- descriptions based on the relevant feature bits. Two xcr0 values might
- be different, but have the same relevant feature bits. In this case we
- would expect the two xcr0 values to map to the same cache entry. */
+ the caching we're discussing here is Linux only. Currently for Linux,
+ the only thing that has an impact on target description creation are
+ the supported features in xsave which are modelled by a xstate_bv_mask
+ value, which has the same format than the state component bitmap.
+
+ In order to ensure the cache functions correctly we need to filter only
+ those xstate_bv_mask feature bits that are relevant, we can then cache
+ target descriptions based on the relevant feature bits. Two
+ xstate_bv_mask values might be different, but have the same relevant
+ feature bits. In this case we would expect the two xstate_bv_mask
+ values to map to the same cache entry. */

struct x86_xstate_feature {
- /* The xstate feature mask. This is a mask against an xcr0 value. */
+ /* The xstate feature mask. This is a mask against the state component
+ bitmap. */
uint64_t feature;

/* Is this feature checked when creating an i386 target description. */
@@ -56,9 +59,9 @@ struct x86_xstate_feature {
checked when building a target description for i386, amd64, or x32.

If in the future, due to simplifications or refactoring, this table ever
- ends up with 'true' for every xcr0 feature on every target type, then this
- is an indication that this table should probably be removed, and that the
- rest of the code in this file can be simplified. */
+ ends up with 'true' for every xsave feature on every target type, then
+ this is an indication that this table should probably be removed, and
+ that the rest of the code in this file can be simplified. */

static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
/* Feature, i386, amd64, x32. */
@@ -73,7 +76,7 @@ static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
that are checked for when building an i386 target description. */

static constexpr uint64_t
-x86_linux_i386_xcr0_feature_mask_1 ()
+x86_linux_i386_xstate_bv_feature_mask_1 ()
{
uint64_t mask = 0;

@@ -88,7 +91,7 @@ x86_linux_i386_xcr0_feature_mask_1 ()
that are checked for when building an amd64 target description. */

static constexpr uint64_t
-x86_linux_amd64_xcr0_feature_mask_1 ()
+x86_linux_amd64_xstate_bv_feature_mask_1 ()
{
uint64_t mask = 0;

@@ -103,7 +106,7 @@ x86_linux_amd64_xcr0_feature_mask_1 ()
that are checked for when building an x32 target description. */

static constexpr uint64_t
-x86_linux_x32_xcr0_feature_mask_1 ()
+x86_linux_x32_xstate_bv_feature_mask_1 ()
{
uint64_t mask = 0;

@@ -117,25 +120,25 @@ x86_linux_x32_xcr0_feature_mask_1 ()
/* See arch/x86-linux-tdesc-features.h. */

uint64_t
-x86_linux_i386_xcr0_feature_mask ()
+x86_linux_i386_xstate_bv_feature_mask ()
{
- return x86_linux_i386_xcr0_feature_mask_1 ();
+ return x86_linux_i386_xstate_bv_feature_mask_1 ();
}

/* See arch/x86-linux-tdesc-features.h. */

uint64_t
-x86_linux_amd64_xcr0_feature_mask ()
+x86_linux_amd64_xstate_bv_feature_mask ()
{
- return x86_linux_amd64_xcr0_feature_mask_1 ();
+ return x86_linux_amd64_xstate_bv_feature_mask_1 ();
}

/* See arch/x86-linux-tdesc-features.h. */

uint64_t
-x86_linux_x32_xcr0_feature_mask ()
+x86_linux_x32_xstate_bv_feature_mask ()
{
- return x86_linux_x32_xcr0_feature_mask_1 ();
+ return x86_linux_x32_xstate_bv_feature_mask_1 ();
}

#ifdef GDBSERVER
@@ -143,7 +146,7 @@ x86_linux_x32_xcr0_feature_mask ()
/* See arch/x86-linux-tdesc-features.h. */

int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask)
{
/* The following table shows which features are checked for when creating
the target descriptions (see nat/x86-linux-tdesc.c), the feature order
@@ -160,7 +163,7 @@ x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)

for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
{
- if ((xcr0 & x86_linux_all_xstate_features[i].feature)
+ if ((xstate_bv_mask & x86_linux_all_xstate_features[i].feature)
== x86_linux_all_xstate_features[i].feature)
idx |= (1 << i);
}
@@ -250,17 +253,17 @@ x86_linux_i386_tdesc_count ()
/* See arch/x86-linux-tdesc-features.h. */

uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
+x86_linux_tdesc_idx_to_xstate_bv_mask (int idx)
{
- uint64_t xcr0 = 0;
+ uint64_t xstate_bv_mask = 0;

for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
{
if ((idx & (1 << i)) != 0)
- xcr0 |= x86_linux_all_xstate_features[i].feature;
+ xstate_bv_mask |= x86_linux_all_xstate_features[i].feature;
}

- return xcr0;
+ return xstate_bv_mask;
}

#endif /* IN_PROCESS_AGENT */
diff --git a/gdb/arch/x86-linux-tdesc-features.h b/gdb/arch/x86-linux-tdesc-features.h
index 89fe7cecc68..d1d74e70df8 100644
--- a/gdb/arch/x86-linux-tdesc-features.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -27,17 +27,20 @@
the set of features which are checked for when creating the target
description for each of amd64, x32, and i386. */

-extern uint64_t x86_linux_amd64_xcr0_feature_mask ();
-extern uint64_t x86_linux_x32_xcr0_feature_mask ();
-extern uint64_t x86_linux_i386_xcr0_feature_mask ();
+extern uint64_t x86_linux_amd64_xstate_bv_feature_mask ();
+extern uint64_t x86_linux_x32_xstate_bv_feature_mask ();
+extern uint64_t x86_linux_i386_xstate_bv_feature_mask ();

#ifdef GDBSERVER

-/* Convert an xcr0 value into an integer. The integer will be passed from
+/* Convert an XSTATE_BV_MASK value into an integer. XSTATE_BV_MASK has
+ the same format than the state component bitmap and does include user
+ and supervisor state components. The integer will be passed from
gdbserver to the in-process-agent where it will then be passed through
- x86_linux_tdesc_idx_to_xcr0 to get back the original xcr0 value. */
+ x86_linux_tdesc_idx_to_xstate_bv_mask to get back the original mask. */

-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+extern int x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask);

#endif /* GDBSERVER */

@@ -51,11 +54,13 @@ extern int x86_linux_amd64_tdesc_count ();
extern int x86_linux_x32_tdesc_count ();
extern int x86_linux_i386_tdesc_count ();

-/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
- into an xcr0 value which can then be used to create a target
- description. */
+/* Convert an index number (as returned from
+ x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
+ value which can then be used to create a target description.
+ The return mask has the same format than the state component bitmap
+ and does include user and supervisor state components.*/

-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);

#endif /* IN_PROCESS_AGENT */

diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 23788b44e92..eca462f0f1b 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8949,23 +8949,23 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)

\f

-/* Return the target description for a specified XSAVE feature mask. */
+/* See i386-tdep.h. */

const struct target_desc *
-i386_target_description (uint64_t xcr0, bool segments)
+i386_target_description (uint64_t xstate_bv_mask, bool segments)
{
static target_desc *i386_tdescs \
[2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
target_desc **tdesc;

- tdesc = &i386_tdescs[(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
- [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
- [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
- [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
+ tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
+ [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
+ [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
+ [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
[segments ? 1 : 0];

if (*tdesc == NULL)
- *tdesc = i386_create_target_description (xcr0, false, segments);
+ *tdesc = i386_create_target_description (xstate_bv_mask, false, segments);

return *tdesc;
}
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index e849b336e12..239bc8674e8 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -450,8 +450,11 @@ extern int i386_svr4_reg_to_regnum (struct gdbarch *gdbarch, int reg);

extern int i386_process_record (struct gdbarch *gdbarch,
struct regcache *regcache, CORE_ADDR addr);
-extern const struct target_desc *i386_target_description (uint64_t xcr0,
- bool segments);
+
+/* Return the target description for the specified xsave features as
+ defined in XSTATE_BV_MASK and SEGMENTS. */
+extern const struct target_desc *i386_target_description
+ (uint64_t xstate_bv_mask, bool segments);

/* Functions and variables exported from i386-bsd-tdep.c. */

diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index 80e4337a270..e9cf2527c5f 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -43,7 +43,7 @@
/* See nat/x86-linux-tdesc.h. */

const target_desc *
-x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
+x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
x86_xsave_layout *xsave_layout_storage)
{
#ifdef __x86_64__
@@ -96,30 +96,32 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
these bits being set we generate a completely empty tdesc for
i386 which will be rejected by GDB. */
have_ptrace_getregset = TRIBOOL_FALSE;
- *xcr0_storage = X86_XSTATE_SSE_MASK;
+ *xstate_bv_storage = X86_XSTATE_SSE_MASK;
}
else
{
have_ptrace_getregset = TRIBOOL_TRUE;

/* Get XCR0 from XSAVE extended state. */
- *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ uint64_t xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
/ sizeof (uint64_t))];

*xsave_layout_storage
- = x86_fetch_xsave_layout (*xcr0_storage, x86_xsave_length ());
+ = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+
+ *xstate_bv_storage = xcr0;
}
}

- /* Use cached xcr0 value. */
- uint64_t xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+ /* Use cached XSTATE_BV_STORAGE value. */
+ uint64_t xstate_bv_features_bits = *xstate_bv_storage & X86_XSTATE_ALL_MASK;

#ifdef __x86_64__
if (is_64bit)
- return amd64_linux_read_description (xcr0_features_bits, is_x32);
+ return amd64_linux_read_description (xstate_bv_features_bits, is_x32);
else
#endif
- return i386_linux_read_description (xcr0_features_bits);
+ return i386_linux_read_description (xstate_bv_features_bits);
}

#endif /* !IN_PROCESS_AGENT */
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 38c71f10df2..19aa84f6b3f 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -27,9 +27,9 @@ struct x86_xsave_layout;

/* Return the target description for Linux thread TID.

- The storage pointed to by XCR0_STORAGE and XSAVE_LAYOUT_STORAGE must
+ The storage pointed to by XSTATE_BV_STORAGE and XSAVE_LAYOUT_STORAGE must
exist until the program (GDB or gdbserver) terminates, this storage is
- used to cache the xcr0 and xsave layout values. The values pointed to
+ used to cache the xstate_bv and xsave layout values. The values pointed to
by these arguments are only updated at most once, the first time this
function is called if the have_ptrace_getregset global is set to
TRIBOOL_UNKNOWN.
@@ -45,6 +45,7 @@ struct x86_xsave_layout;
returned. */

extern const target_desc *x86_linux_tdesc_for_tid
- (int tid, uint64_t *xcr0_storage, x86_xsave_layout *xsave_layout_storage);
+ (int tid, uint64_t *xstate_bv_storage,
+ x86_xsave_layout *xsave_layout_storage);

#endif /* GDB_NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index fc7c5f642ed..a82ad21da27 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -97,15 +97,20 @@ const struct target_desc *
x86_linux_nat_target::read_description ()
{
/* The x86_linux_tdesc_for_tid call only reads xcr0 the first time it is
- called, the xcr0 value is stored here and reused on subsequent calls. */
- static uint64_t xcr0_storage;
+ called. The mask is stored in XSTATE_BV_STORAGE and reused on
+ subsequent calls. Note that GDB currently supports features for user
+ state components only. However, once supervisor state components are
+ supported in GDB XSTATE_BV_STORAGE will not be configured based on
+ xcr0 only. */
+ static uint64_t xstate_bv_storage;

if (inferior_ptid == null_ptid)
return this->beneath ()->read_description ();

int tid = inferior_ptid.pid ();

- return x86_linux_tdesc_for_tid (tid, &xcr0_storage, &this->m_xsave_layout);
+ return x86_linux_tdesc_for_tid (tid, &xstate_bv_storage,
+ &this->m_xsave_layout);
}
\f

diff --git a/gdbserver/i387-fp.cc b/gdbserver/i387-fp.cc
index 4be0083a6fb..90824bdaee5 100644
--- a/gdbserver/i387-fp.cc
+++ b/gdbserver/i387-fp.cc
@@ -21,7 +21,7 @@
#include "nat/x86-xstate.h"

/* Default to SSE. */
-static uint64_t x86_xcr0 = X86_XSTATE_SSE_MASK;
+static uint64_t x86_xstate_bv = X86_XSTATE_SSE_MASK;

static const int num_avx512_k_registers = 8;
static const int num_pkeys_registers = 1;
@@ -265,7 +265,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)

/* The supported bits in `xstat_bv' are 8 bytes. Clear part in
vector registers if its bit in xstat_bv is zero. */
- clear_bv = (~fp->xstate_bv) & x86_xcr0;
+ clear_bv = (~fp->xstate_bv) & x86_xstate_bv;

/* Clear part in x87 and vector registers if its bit in xstat_bv is
zero. */
@@ -315,7 +315,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any x87 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_X87))
+ if ((x86_xstate_bv & X86_XSTATE_X87))
{
int st0_regnum = find_regno (regcache->tdesc, "st0");

@@ -332,7 +332,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any SSE registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_SSE))
+ if ((x86_xstate_bv & X86_XSTATE_SSE))
{
int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");

@@ -349,7 +349,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any AVX registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_AVX))
+ if ((x86_xstate_bv & X86_XSTATE_AVX))
{
int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");

@@ -366,7 +366,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any K registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_K))
+ if ((x86_xstate_bv & X86_XSTATE_K))
{
int k0_regnum = find_regno (regcache->tdesc, "k0");

@@ -383,7 +383,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any of ZMM0H-ZMM15H registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM_H))
+ if ((x86_xstate_bv & X86_XSTATE_ZMM_H))
{
int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");

@@ -400,7 +400,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any of ZMM16-ZMM31 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
+ if ((x86_xstate_bv & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
{
int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
@@ -437,7 +437,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}

/* Check if any PKEYS registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_PKRU))
+ if ((x86_xstate_bv & X86_XSTATE_PKRU))
{
int pkru_regnum = find_regno (regcache->tdesc, "pkru");

@@ -453,7 +453,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
+ if ((x86_xstate_bv & X86_XSTATE_SSE) || (x86_xstate_bv & X86_XSTATE_AVX))
{
collect_register_by_name (regcache, "mxcsr", raw);
if (memcmp (raw, &fp->mxcsr, 4) != 0)
@@ -465,7 +465,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}
}

- if (x86_xcr0 & X86_XSTATE_X87)
+ if (x86_xstate_bv & X86_XSTATE_X87)
{
collect_register_by_name (regcache, "fioff", raw);
if (memcmp (raw, &fp->fioff, 4) != 0)
@@ -658,10 +658,10 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)

/* The supported bits in `xstat_bv' are 8 bytes. Clear part in
vector registers if its bit in xstat_bv is zero. */
- clear_bv = (~fp->xstate_bv) & x86_xcr0;
+ clear_bv = (~fp->xstate_bv) & x86_xstate_bv;

/* Check if any x87 registers are changed. */
- if ((x86_xcr0 & X86_XSTATE_X87) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_X87) != 0)
{
int st0_regnum = find_regno (regcache->tdesc, "st0");

@@ -678,7 +678,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_SSE) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_SSE) != 0)
{
int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");

@@ -695,7 +695,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_AVX) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_AVX) != 0)
{
int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");

@@ -712,7 +712,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_K) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_K) != 0)
{
int k0_regnum = find_regno (regcache->tdesc, "k0");

@@ -729,7 +729,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_ZMM_H) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_ZMM_H) != 0)
{
int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");

@@ -746,7 +746,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
+ if ((x86_xstate_bv & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
{
int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
@@ -773,7 +773,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

- if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
+ if ((x86_xstate_bv & X86_XSTATE_PKRU) != 0)
{
int pkru_regnum = find_regno (regcache->tdesc, "pkru");

@@ -858,5 +858,5 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
std::pair<uint64_t *, x86_xsave_layout *>
i387_get_xsave_storage ()
{
- return { &x86_xcr0, &xsave_layout };
+ return { &x86_xstate_bv, &xsave_layout };
}
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index af4eb03b9b8..75d70a8787c 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -82,7 +82,7 @@ get_raw_reg (const unsigned char *raw_regs, int regnum)
const struct target_desc *
get_ipa_tdesc (int idx)
{
- uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+ uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx);

#if defined __ILP32__
bool is_x32 = true;
@@ -90,7 +90,7 @@ get_ipa_tdesc (int idx)
bool is_x32 = false;
#endif

- return amd64_linux_read_description (xcr0, is_x32);
+ return amd64_linux_read_description (xstate_bv_mask, is_x32);
}

/* Allocate buffer for the jump pads. The branch instruction has a
@@ -159,9 +159,11 @@ initialize_low_tracepoint (void)
{
#if defined __ILP32__
for (int i = 0; i < x86_linux_x32_tdesc_count (); i++)
- amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
+ amd64_linux_read_description
+ (x86_linux_tdesc_idx_to_xstate_bv_mask (i), true);
#else
for (int i = 0; i < x86_linux_amd64_tdesc_count (); i++)
- amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
+ amd64_linux_read_description
+ (x86_linux_tdesc_idx_to_xstate_bv_mask (i), false);
#endif
}
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 17af6eb3610..1a0393d127c 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -174,9 +174,9 @@ initialize_fast_tracepoint_trampoline_buffer (void)
const struct target_desc *
get_ipa_tdesc (int idx)
{
- uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+ uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx);

- return i386_linux_read_description (xcr0);
+ return i386_linux_read_description (xstate_bv_mask);
}

/* Allocate buffer for the jump pads. On i386, we can reach an arbitrary
@@ -199,5 +199,5 @@ initialize_low_tracepoint (void)
{
initialize_fast_tracepoint_trampoline_buffer ();
for (int i = 0; i < x86_linux_i386_tdesc_count (); i++)
- i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
+ i386_linux_read_description (x86_linux_tdesc_idx_to_xstate_bv_mask (i));
}
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 24920e71a53..dbc11d6856d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -873,7 +873,7 @@ x86_linux_read_description ()
bool have_ptrace_getregset_was_unknown
= have_ptrace_getregset == TRIBOOL_UNKNOWN;

- /* Get pointers to where we should store the xcr0 and xsave_layout
+ /* Get pointers to where we should store the xstate_bv and xsave_layout
values. These will be filled in by x86_linux_tdesc_for_tid the first
time that the function is called. Subsequent calls will not modify
the stored values. */
@@ -2892,17 +2892,16 @@ x86_target::get_ipa_tdesc_idx ()
|| tdesc == tdesc_amd64_linux_no_xml.get ()
#endif /* __x86_64__ */
);
- return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+ return x86_linux_xstate_bv_mask_to_tdesc_idx (X86_XSTATE_SSE_MASK);
}

- /* The xcr0 value and xsave layout value are cached when the target
+ /* The xstate_bv value and xsave layout value are cached when the target
description is read. Grab their cache location, and use the cached
value to calculate a tdesc index. */
std::pair<uint64_t *, x86_xsave_layout *> storage
= i387_get_xsave_storage ();
- uint64_t xcr0 = *storage.first;

- return x86_linux_xcr0_to_tdesc_idx (xcr0);
+ return x86_linux_xstate_bv_mask_to_tdesc_idx (*storage.first);
}

/* The linux target ops object. */
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 5d563ff4622..9bb373c3100 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -83,8 +83,10 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
#define X86_XSTATE_AVX_AVX512_PKU_MASK (X86_XSTATE_AVX_MASK\
| X86_XSTATE_AVX512 | X86_XSTATE_PKRU)

-#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_MASK)
+/* Supported mask of state-component bitmap xstate_bv. The SDM defines
+ xstate_bv as XCR0 | IA32_XSS. */

+#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_MASK)

#define X86_XSTATE_SSE_SIZE 576
#define X86_XSTATE_AVX_SIZE 832
--
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928