[PATCH v2 7/8] Windows gdb: Implement XState (Intel AVX) support
Hannes Domani <[email protected]> Mon, 27 Jul 2026 19:42:31 +0200
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
This adds support for the Intel AVX and AVX-512 registers on Windows.
It enables accessing registers $ymm0 - $ymm31, $zmm0 - $zmm31, and
$k0 - $k7 where they are available.
It also enables reading the shadow stack pointer register $pl3_ssp (for
executables marked compatible with CET shadow stack [1]), but modifying
it seems to be restricted restricted by windows.
After this patch the tests gdb.arch/i386-avx.exp and gdb.arch/i386-avx512.exp
pass on windows.
[1] https://learn.microsoft.com/en-us/cpp/build/reference/cetcompat
---
v2:
- Extended commit message and NEWS entry
- Remove PKRU support
- Fixed context offset of $pl3_ssp
---
gdb/NEWS | 6 +++
gdb/x86-windows-nat.c | 108 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 111 insertions(+), 3 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 8f40ca5cb11..71ff5508d6d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -116,6 +116,12 @@
intent to remove it in a future release.
The s390 64-bit target (s390x-*) remains supported.
+* Support for Intel AVX registers on Windows.
+ Support displaying and modifying Intel AVX registers $ymm0 - $ymm31.
+ Support displaying and modifying Intel AVX-512 registers $zmm0 - $zmm31
+ and $k0 - $k7.
+ Support displaying the shadow stack pointer register $pl3_ssp.
+
* Configure changes
** --with-babeltrace has been removed. The babeltrace library was
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index 3af5ef4dae0..270f9eae8e7 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -27,6 +27,9 @@
#include "i386-tdep.h"
#include "i387-tdep.h"
+#ifdef __x86_64__
+#include "amd64-tdep.h"
+#endif
using namespace windows_nat;
@@ -70,6 +73,8 @@ struct x86_windows_nat_target final : public x86_nat_target<windows_nat_target>
windows_thread_info *th, int r) override;
bool is_sw_breakpoint (const EXCEPTION_RECORD *er) const override;
+
+ const struct target_desc *read_description () override;
};
/* The current process. */
@@ -109,7 +114,31 @@ x86_windows_per_inferior::fill_thread_context (windows_thread_info *th)
if (context->ContextFlags == 0)
{
context->ContextFlags = WindowsContext<decltype(context)>::all;
+ if (xstate_features != 0)
+ {
+ context->ContextFlags |= CONTEXT_XSTATE_FLAG;
+ set_xstate_features_mask (context, xstate_features);
+ }
CHECK (get_thread_context (th->h, context));
+
+ if (xstate_features != 0)
+ {
+ DWORD64 features = 0;
+ CHECK (get_xstate_features_mask (context, &features));
+ DWORD64 zeroed_features = xstate_features & ~features;
+
+ for (int f = X86_XSTATE_AVX_ID; f <= X86_XSTATE_CET_U_ID; f++)
+ {
+ DWORD64 flag = 1ULL << f;
+ if ((zeroed_features & flag) != 0)
+ {
+ DWORD size = 0;
+ void *loc = locate_xstate_feature (context, f, &size);
+ if (loc != nullptr && size > 0)
+ memset (loc, 0, size);
+ }
+ }
+ }
}
});
}
@@ -198,6 +227,14 @@ x86_windows_nat_target::thread_context_continue (windows_thread_info *th,
if (GetExitCodeThread (th->h, &ec)
&& ec == STILL_ACTIVE)
{
+ DWORD debug_registers = WindowsContext<decltype(context)>::debug;
+ if (xstate_features != 0
+ && (context->ContextFlags & ~debug_registers) != 0)
+ {
+ context->ContextFlags |= CONTEXT_XSTATE_FLAG;
+ set_xstate_features_mask (context, xstate_features);
+ }
+
BOOL status = set_thread_context (th->h, context);
if (!killed)
@@ -227,7 +264,7 @@ x86_windows_nat_target::thread_context_step (windows_thread_info *th,
template<typename Context>
static char *
-get_context_reg_ptr (Context *context, int r)
+get_context_reg_ptr (Context *context, int r, i386_gdbarch_tdep *tdep)
{
const int *mappings;
int mappings_count;
@@ -247,6 +284,54 @@ get_context_reg_ptr (Context *context, int r)
char *context_offset;
if (r < mappings_count)
context_offset = (char *) context + mappings[r];
+ else if (I387_ZMM0H_REGNUM (tdep) > 0 && r >= I387_ZMM0H_REGNUM (tdep)
+ && r < I387_ZMM16H_REGNUM (tdep) && r < I387_ZMMENDH_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_ZMM_H_ID, NULL);
+ context_offset += 32 * (r - I387_ZMM0H_REGNUM (tdep));
+ }
+ else if (I387_ZMM0H_REGNUM (tdep) > 0 && r >= I387_ZMM16H_REGNUM (tdep)
+ && r < I387_ZMMENDH_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_ZMM_ID, NULL);
+ context_offset += 32 + 64 * (r - I387_ZMM16H_REGNUM (tdep));
+ }
+ else if (I387_K0_REGNUM (tdep) > 0 && r >= I387_K0_REGNUM (tdep)
+ && r < I387_KEND_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_K_ID, NULL);
+ context_offset += 8 * (r - I387_K0_REGNUM (tdep));
+ }
+ else if (I387_YMM16H_REGNUM (tdep) > 0 && r >= I387_YMM16H_REGNUM (tdep)
+ && r < I387_YMMH_AVX512_END_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_ZMM_ID, NULL);
+ context_offset += 16 + 64 * (r - I387_YMM16H_REGNUM (tdep));
+ }
+ else if (I387_XMM16_REGNUM (tdep) > 0 && r >= I387_XMM16_REGNUM (tdep)
+ && r < I387_XMM_AVX512_END_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_ZMM_ID, NULL);
+ context_offset += 64 * (r - I387_XMM16_REGNUM (tdep));
+ }
+ else if (I387_YMM0H_REGNUM (tdep) > 0 && r >= I387_YMM0H_REGNUM (tdep)
+ && r < I387_YMMENDH_REGNUM (tdep))
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_AVX_ID, NULL);
+ context_offset += 16 * (r - I387_YMM0H_REGNUM (tdep));
+ }
+ else if (tdep->ssp_regnum > 0 && r == tdep->ssp_regnum)
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_CET_U_ID, NULL);
+ context_offset += 8;
+ }
else
gdb_assert_not_reached ("invalid register number %d", r);
@@ -267,7 +352,7 @@ x86_windows_nat_target::fetch_one_register (struct regcache *regcache,
char *context_offset
= x86_windows_process.with_context (th, [&] (auto *context)
{
- return get_context_reg_ptr (context, r);
+ return get_context_reg_ptr (context, r, tdep);
});
gdb_assert (!gdbarch_read_pc_p (gdbarch));
@@ -333,7 +418,7 @@ x86_windows_nat_target::store_one_register (const struct regcache *regcache,
= x86_windows_process.with_context (th, [&] (auto *context)
{
gdb_assert (context->ContextFlags != 0);
- return get_context_reg_ptr (context, r);
+ return get_context_reg_ptr (context, r, tdep);
});
/* GDB treats some registers as 32-bit, where they are in fact only
@@ -368,6 +453,23 @@ x86_windows_nat_target::is_sw_breakpoint (const EXCEPTION_RECORD *er) const
|| er->ExceptionCode == STATUS_WX86_BREAKPOINT);
}
+const struct target_desc *
+x86_windows_nat_target::read_description ()
+{
+ if (inferior_ptid == null_ptid)
+ return this->beneath ()->read_description ();
+
+ if (xstate_features == 0)
+ return nullptr;
+
+#ifdef __x86_64__
+ if (!x86_windows_process.wow64_process)
+ return amd64_target_description (xstate_features, false);
+ else
+#endif
+ return i386_target_description (xstate_features, false);
+}
+
/* Hardware watchpoint support, adapted from go32-nat.c code. */
/* Pass the address ADDR to the inferior in the I'th debug register.
--
2.54.0