From: Hannes Domani <ssbssa@yahoo.de>
To: gdb-patches@sourceware.org
Subject: [PATCH v3 7/8] Windows gdb: Implement AVX register support
Date: Sat, 29 Aug 2026 16:49:04 +0200 [thread overview]
Message-ID: <20260829145823.1034821-7-ssbssa@yahoo.de> (raw)
In-Reply-To: <20260829145823.1034821-1-ssbssa@yahoo.de>
This adds support for the Intel AVX registers on Windows.
It enables accessing registers $ymm0 - $ymm15 where they are available.
After this patch gdb.arch/i386-avx.exp passes on windows.
---
v3:
- merged gdb+gdbserver parts, and split again AVX/AVX-512 parts
---
gdb/NEWS | 3 ++
gdb/nat/windows-nat.c | 2 +-
gdb/x86-windows-nat.c | 67 +++++++++++++++++++++++++++++++++++--
gdbserver/win32-i386-low.cc | 61 +++++++++++++++++++++++++++++----
gdbserver/win32-low.cc | 15 ++++++---
5 files changed, 133 insertions(+), 15 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 10c182067f9..f7effc822e9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -118,6 +118,9 @@
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.
+
* Configure changes
** --with-babeltrace has been removed. The babeltrace library was
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 8930536f3ba..c9a21d7c41f 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -1339,7 +1339,7 @@ initialize_loadable ()
{
/* Available XState features masked with implemented features. */
xstate_features = (GetEnabledXStateFeatures ()
- & X86_XSTATE_SSE_MASK);
+ & X86_XSTATE_AVX_MASK);
/* The extended XState functions are only needed if the available
features exceed SSE. */
if ((xstate_features & ~X86_XSTATE_SSE_MASK) == 0)
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index 3af5ef4dae0..1cefe6171be 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,13 @@ get_context_reg_ptr (Context *context, int r)
char *context_offset;
if (r < mappings_count)
context_offset = (char *) context + mappings[r];
+ 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
gdb_assert_not_reached ("invalid register number %d", r);
@@ -267,7 +311,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 +377,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 +412,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.
diff --git a/gdbserver/win32-i386-low.cc b/gdbserver/win32-i386-low.cc
index b77f6adc6ed..a7e83c0239c 100644
--- a/gdbserver/win32-i386-low.cc
+++ b/gdbserver/win32-i386-low.cc
@@ -253,6 +253,11 @@ i386_get_thread_context (windows_thread_info *th)
| WindowsContext<decltype(context)>::floating
| WindowsContext<decltype(context)>::debug
| extended_registers);
+ if (xstate_features != 0)
+ {
+ context->ContextFlags |= CONTEXT_XSTATE_FLAG;
+ set_xstate_features_mask (context, xstate_features);
+ }
BOOL ret = get_thread_context (th->h, context);
if (!ret)
@@ -267,6 +272,24 @@ i386_get_thread_context (windows_thread_info *th)
error (_("GetThreadContext failure %ld\n"), (long) e);
}
+
+ DWORD64 features = 0;
+ if (xstate_features != 0
+ && 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);
+ }
+ }
+ }
});
}
@@ -292,6 +315,17 @@ i386_prepare_to_resume (windows_thread_info *th)
th->debug_registers_changed = false;
}
+
+ windows_process.with_context (th, [&] (auto *context)
+ {
+ 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);
+ }
+ });
}
static void
@@ -477,7 +511,7 @@ is_segment_register (int r)
template<typename Context>
static char *
-get_context_reg_ptr (Context *context, int r)
+get_context_reg_ptr (Context *context, int r, const target_desc *tdesc)
{
const int *mappings;
int mappings_count;
@@ -494,9 +528,21 @@ get_context_reg_ptr (Context *context, int r)
mappings_count = sizeof (i386_mappings) / sizeof (i386_mappings[0]);
}
+ bool amd64 = register_size (tdesc, 0) == 8;
+ int ymm0h_regnum;
+ const int num_xmm_registers = amd64 ? 16 : 8;
+
char *context_offset;
if (r < mappings_count)
context_offset = (char *) context + mappings[r];
+ else if ((xstate_features & X86_XSTATE_AVX) != 0
+ && r >= (ymm0h_regnum = find_regno (tdesc, "ymm0h"))
+ && r < ymm0h_regnum + num_xmm_registers)
+ {
+ context_offset = (char *) locate_xstate_feature
+ (context, X86_XSTATE_AVX_ID, NULL);
+ context_offset += 16 * (r - ymm0h_regnum);
+ }
else
gdb_assert_not_reached ("invalid register number %d", r);
@@ -510,7 +556,7 @@ i386_fetch_inferior_register (struct regcache *regcache,
{
char *context_offset = windows_process.with_context (th, [&] (auto *context)
{
- return get_context_reg_ptr (context, r);
+ return get_context_reg_ptr (context, r, regcache->tdesc);
});
/* GDB treats some registers as 32-bit, where they are in fact only
@@ -538,7 +584,7 @@ i386_store_inferior_register (struct regcache *regcache,
{
char *context_offset = windows_process.with_context (th, [&] (auto *context)
{
- return get_context_reg_ptr (context, r);
+ return get_context_reg_ptr (context, r, regcache->tdesc);
});
/* GDB treats some registers as 32-bit, where they are in fact only
@@ -571,14 +617,17 @@ i386_arch_setup (void)
{
target_desc_up tdesc;
+ DWORD64 xcr0 = xstate_features;
+ if (xcr0 == 0)
+ xcr0 = X86_XSTATE_SSE_MASK;
+
#ifdef __x86_64__
- tdesc = amd64_create_target_description (X86_XSTATE_SSE_MASK, false,
- false, false);
+ tdesc = amd64_create_target_description (xcr0, false, false, false);
init_target_desc (tdesc.get (), amd64_expedite_regs, WINDOWS_OSABI);
win32_tdesc = std::move (tdesc);
#endif
- tdesc = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
+ tdesc = i386_create_target_description (xcr0, false, false);
init_target_desc (tdesc.get (), i386_expedite_regs, WINDOWS_OSABI);
#ifdef __x86_64__
wow64_win32_tdesc = std::move (tdesc);
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 7629beca213..5ccdc89a7ef 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -33,6 +33,7 @@
#include <process.h>
#include "gdbsupport/gdb_tilde_expand.h"
#include "gdbsupport/common-inferior.h"
+#include "tdesc.h"
using namespace windows_nat;
@@ -426,8 +427,9 @@ child_fetch_inferior_registers (struct regcache *regcache, int r)
int regno;
windows_thread_info *th = windows_process.find_thread (current_thread->id);
win32_require_context (th);
- if (r == -1 || r > NUM_REGS)
- child_fetch_inferior_registers (regcache, NUM_REGS);
+ if (r == -1)
+ child_fetch_inferior_registers (regcache,
+ regcache->tdesc->reg_defs.size ());
else
for (regno = 0; regno < r; regno++)
(*the_low_target.fetch_inferior_register) (regcache, th, regno);
@@ -441,8 +443,9 @@ child_store_inferior_registers (struct regcache *regcache, int r)
int regno;
windows_thread_info *th = windows_process.find_thread (current_thread->id);
win32_require_context (th);
- if (r == -1 || r == 0 || r > NUM_REGS)
- child_store_inferior_registers (regcache, NUM_REGS);
+ if (r == -1)
+ child_store_inferior_registers (regcache,
+ regcache->tdesc->reg_defs.size ());
else
for (regno = 0; regno < r; regno++)
(*the_low_target.store_inferior_register) (regcache, th, regno);
@@ -1349,7 +1352,9 @@ void
initialize_low (void)
{
set_target_ops (&the_win32_target);
- the_low_target.arch_setup ();
initialize_loadable ();
+ /* Has to be done after initialize_loadable, because it uses the xstate
+ functions if available. */
+ the_low_target.arch_setup ();
}
--
2.54.0
next prev parent reply other threads:[~2026-08-29 15:02 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260829145823.1034821-1-ssbssa.ref@yahoo.de>
2026-08-29 14:48 ` [PATCH v3 1/8] gdb/testsuite: Add Windows replacement for aligned_alloc Hannes Domani
2026-08-29 14:48 ` [PATCH v3 2/8] Windows gdb: Use allocated buffer for CONTEXT Hannes Domani
2026-08-29 14:49 ` [PATCH v3 3/8] Windows gdb: Remove mappings member from windows_per_inferior Hannes Domani
2026-08-29 14:49 ` [PATCH v3 4/8] Windows gdb: Refactor getting pointer to register inside context Hannes Domani
2026-08-29 14:49 ` [PATCH v3 5/8] Windows gdb: Prepare XState functions Hannes Domani
2026-08-29 14:49 ` [PATCH v3 6/8] Windows gdb: Get available XState features Hannes Domani
2026-08-29 14:49 ` Hannes Domani [this message]
2026-08-29 14:49 ` [PATCH v3 8/8] Windows gdb: Implement AVX-512 register support Hannes Domani
2026-08-29 15:40 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260829145823.1034821-7-ssbssa@yahoo.de \
--to=ssbssa@yahoo.de \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox